package httpapi

import (
	"context"
	"log/slog"
	"sync"
	"time"

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

const identityPairingReconcileInterval = 5 * time.Second

type identityPairingReconciliationStore interface {
	PendingIdentityPairingExchanges(context.Context) ([]identity.PairingExchange, error)
	CompletedIdentityPairingsAwaitingActivation(context.Context) ([]identity.PairingExchange, error)
	PendingIdentityPairingCleanups(context.Context) ([]identity.PairingExchange, error)
}

type identityPairingRestorer interface {
	RestoreCompletedSecurityEvents(context.Context, string) error
}

func (s *Server) reconcileCanonicalIdentityPairing() {
	reconcileCanonicalIdentityPairing(
		s.ctx,
		s.store,
		s.identityPairing,
		&s.identityRestoredPairings,
		s.startIdentityPairingWorker,
		func(pairingID string) { s.startIdentityPairingCleanupWorker(pairingID, nil) },
		s.logger,
	)
}

func (s *Server) runIdentityPairingCoordinator() {
	ticker := time.NewTicker(identityPairingReconcileInterval)
	defer ticker.Stop()
	for {
		select {
		case <-s.ctx.Done():
			return
		case <-ticker.C:
			s.reconcileIdentityRuntime()
			s.reconcileCanonicalIdentityPairing()
		}
	}
}

func reconcileCanonicalIdentityPairing(
	ctx context.Context,
	repository identityPairingReconciliationStore,
	restorer identityPairingRestorer,
	restored *sync.Map,
	startWorker func(string),
	startCleanup func(string),
	logger *slog.Logger,
) {
	cleanups, cleanupErr := repository.PendingIdentityPairingCleanups(ctx)
	if cleanupErr != nil {
		if logger != nil {
			logger.Warn("pending identity pairing cleanups could not be resumed", "error_category", "pairing_cleanup_resume_failed")
		}
	} else {
		for _, pairing := range cleanups {
			startCleanup(pairing.ID)
		}
	}

	pending, err := repository.PendingIdentityPairingExchanges(ctx)
	if err != nil {
		if logger != nil {
			logger.Warn("pending identity pairings could not be resumed", "error_category", "pairing_resume_failed")
		}
		return
	}
	if len(pending) > 0 {
		forgetOtherRestoredPairings(restored, "")
		startWorker(pending[0].ID)
		return
	}

	completed, err := repository.CompletedIdentityPairingsAwaitingActivation(ctx)
	if err != nil {
		if logger != nil {
			logger.Warn("completed identity pairings could not be loaded", "error_category", "pairing_receiver_restore_failed")
		}
		return
	}
	if len(completed) == 0 {
		forgetOtherRestoredPairings(restored, "")
		return
	}

	pairingID := completed[0].ID
	forgetOtherRestoredPairings(restored, pairingID)
	if _, loaded := restored.LoadOrStore(pairingID, struct{}{}); loaded {
		return
	}
	if err := restorer.RestoreCompletedSecurityEvents(ctx, pairingID); err != nil {
		restored.Delete(pairingID)
		if logger != nil {
			logger.Warn("completed identity pairing receiver could not be restored", "pairing_id", pairingID, "error_category", "pairing_receiver_restore_failed")
		}
	}
}

func (s *Server) reconcileIdentityRuntime() {
	if s.identityRuntime == nil || !s.identityRuntime.ReconciliationRequired() {
		return
	}
	ctx, cancel := context.WithTimeout(s.ctx, 5*time.Second)
	defer cancel()
	if err := s.identityRuntime.ReconcileActive(ctx); err != nil && s.logger != nil {
		s.logger.Warn("identity runtime reconciliation failed and will retry", "error_category", "identity_runtime_reconciliation_failed")
	}
}

func forgetOtherRestoredPairings(restored *sync.Map, keepID string) {
	restored.Range(func(key, _ any) bool {
		if key != keepID {
			restored.Delete(key)
		}
		return true
	})
}
