package httpapi

import (
	"context"
	"errors"
	"sync"
	"testing"

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/identity"
)

type identityPairingCoordinatorStore struct {
	pending      []identity.PairingExchange
	completed    []identity.PairingExchange
	cleanups     []identity.PairingExchange
	pendingErr   error
	completedErr error
	cleanupErr   error
}

func (store identityPairingCoordinatorStore) PendingIdentityPairingExchanges(context.Context) ([]identity.PairingExchange, error) {
	return store.pending, store.pendingErr
}

func (store identityPairingCoordinatorStore) CompletedIdentityPairingsAwaitingActivation(context.Context) ([]identity.PairingExchange, error) {
	return store.completed, store.completedErr
}

func (store identityPairingCoordinatorStore) PendingIdentityPairingCleanups(context.Context) ([]identity.PairingExchange, error) {
	return store.cleanups, store.cleanupErr
}

type identityPairingCoordinatorRestorer struct {
	calls int
	err   error
}

func (restorer *identityPairingCoordinatorRestorer) RestoreCompletedSecurityEvents(context.Context, string) error {
	restorer.calls++
	return restorer.err
}

func TestReconcileCanonicalIdentityPairingRetriesFailedRestore(t *testing.T) {
	repository := identityPairingCoordinatorStore{
		completed: []identity.PairingExchange{{ID: "pairing-1"}},
	}
	restorer := &identityPairingCoordinatorRestorer{err: errors.New("SecretStore temporarily unavailable")}
	restored := &sync.Map{}

	reconcileCanonicalIdentityPairing(context.Background(), repository, restorer, restored, func(string) {}, func(string) {}, nil)
	reconcileCanonicalIdentityPairing(context.Background(), repository, restorer, restored, func(string) {}, func(string) {}, nil)

	if restorer.calls != 2 {
		t.Fatalf("restore calls = %d, want 2", restorer.calls)
	}
}

func TestReconcileCanonicalIdentityPairingRestoresSuccessfulPairingOnce(t *testing.T) {
	repository := identityPairingCoordinatorStore{
		completed: []identity.PairingExchange{{ID: "pairing-1"}},
	}
	restorer := &identityPairingCoordinatorRestorer{}
	restored := &sync.Map{}

	reconcileCanonicalIdentityPairing(context.Background(), repository, restorer, restored, func(string) {}, func(string) {}, nil)
	reconcileCanonicalIdentityPairing(context.Background(), repository, restorer, restored, func(string) {}, func(string) {}, nil)

	if restorer.calls != 1 {
		t.Fatalf("restore calls = %d, want 1", restorer.calls)
	}
}

func TestReconcileCanonicalIdentityPairingPrioritizesPendingWork(t *testing.T) {
	repository := identityPairingCoordinatorStore{
		pending:   []identity.PairingExchange{{ID: "pending-pairing"}},
		completed: []identity.PairingExchange{{ID: "completed-pairing"}},
	}
	restorer := &identityPairingCoordinatorRestorer{}
	restored := &sync.Map{}
	started := ""

	reconcileCanonicalIdentityPairing(context.Background(), repository, restorer, restored, func(pairingID string) {
		started = pairingID
	}, func(string) {}, nil)

	if started != "pending-pairing" {
		t.Fatalf("started pairing = %q, want pending-pairing", started)
	}
	if restorer.calls != 0 {
		t.Fatalf("restore calls = %d, want 0", restorer.calls)
	}
}

func TestReconcileCanonicalIdentityPairingForgetsTerminalPairing(t *testing.T) {
	restored := &sync.Map{}
	restored.Store("terminal-pairing", struct{}{})

	reconcileCanonicalIdentityPairing(
		context.Background(),
		identityPairingCoordinatorStore{},
		&identityPairingCoordinatorRestorer{},
		restored,
		func(string) {},
		func(string) {},
		nil,
	)

	if _, ok := restored.Load("terminal-pairing"); ok {
		t.Fatal("terminal pairing restore marker was not removed")
	}
}

func TestReconcileCanonicalIdentityPairingResumesCommittedCleanup(t *testing.T) {
	repository := identityPairingCoordinatorStore{
		cleanups: []identity.PairingExchange{{ID: "cancelled-pairing"}},
	}
	restorer := &identityPairingCoordinatorRestorer{}
	restored := &sync.Map{}
	startedCleanup := ""

	reconcileCanonicalIdentityPairing(
		context.Background(),
		repository,
		restorer,
		restored,
		func(string) {},
		func(pairingID string) { startedCleanup = pairingID },
		nil,
	)

	if startedCleanup != "cancelled-pairing" {
		t.Fatalf("started cleanup = %q, want cancelled-pairing", startedCleanup)
	}
}

func TestCleanupCoordinatorCancelsPairingWorkerBeforeCleanup(t *testing.T) {
	serverContext, cancelServer := context.WithCancel(context.Background())
	cancelServer()
	workerContext, cancelWorker := context.WithCancel(context.Background())
	server := &Server{ctx: serverContext}
	server.identityPairingWorkers.Store("pairing-1", &identityPairingWorker{cancel: cancelWorker, done: make(chan struct{})})

	server.startIdentityPairingCleanupWorker("pairing-1", nil)

	select {
	case <-workerContext.Done():
	default:
		t.Fatal("coordinator cleanup did not cancel the pairing worker first")
	}
}
