package rendezvous

import (
	"context"
	"errors"
	"github.com/cirruslabs/orchard/internal/concurrentmap"
)

var ErrInvalidToken = errors.New("invalid rendezvous token")

type Rendezvous[T any] struct {
	sessions *concurrentmap.ConcurrentMap[*TokenSlot[T]]
}

type ResultWithErrorMessage[T any] struct {
	Result       T
	ErrorMessage string
}

type TokenSlot[T any] struct {
	ctx context.Context
	ch  chan T
}

func New[T any]() *Rendezvous[T] {
	return &Rendezvous[T]{
		sessions: concurrentmap.NewConcurrentMap[*TokenSlot[T]](),
	}
}

func (rendezvous *Rendezvous[T]) Request(ctx context.Context, session string) (chan T, func()) {
	tokenSlot := &TokenSlot[T]{
		ctx: ctx,
		ch:  make(chan T, 1),
	}

	rendezvous.sessions.Store(session, tokenSlot)

	return tokenSlot.ch, func() {
		rendezvous.sessions.Delete(session)
	}
}

func (rendezvous *Rendezvous[T]) Respond(session string, conn T) (context.Context, error) {
	tokenSlot, ok := rendezvous.sessions.Load(session)
	if !ok {
		return nil, ErrInvalidToken
	}

	select {
	case tokenSlot.ch <- conn:
		// first response
	default:
		// the receiving party still hadn't picked up the previous response, so discard the new one
	}

	return tokenSlot.ctx, nil
}
