package httpapi

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

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

const identityPairingReconcileInterval = 5 * time.Second
const identityPairingLeadershipTimeout = 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(ctx context.Context) {
	reconcileCanonicalIdentityPairing(
		ctx,
		s.store,
		s.identityPairing,
		&s.identityRestoredPairings,
		s.startIdentityPairingWorker,
		func(pairingID string) { s.startIdentityPairingCleanupWorker(pairingID, nil) },
		s.logger,
	)
}

func (s *Server) runIdentityPairingCoordinator() {
	retry := time.NewTicker(identityPairingReconcileInterval)
	defer retry.Stop()
	for {
		acquireCtx, cancel := context.WithTimeout(s.ctx, identityPairingLeadershipTimeout)
		leadership, acquired, err := s.store.TryAcquireIdentityCoordinatorLeadership(acquireCtx)
		cancel()
		if err != nil && s.logger != nil {
			s.logger.Warn("identity pairing coordinator leader election failed",
				"error_category", "identity_coordinator_leader_election_failed",
				"error", err,
			)
		}
		if acquired {
			if s.logger != nil {
				s.logger.Info("identity pairing coordinator leadership acquired")
			}
			if !s.runIdentityPairingLeader(leadership) {
				return
			}
		}
		select {
		case <-s.ctx.Done():
			return
		case <-retry.C:
		}
	}
}

func (s *Server) runIdentityPairingLeader(leadership store.Leadership) bool {
	defer leadership.Release()
	ticker := time.NewTicker(identityPairingReconcileInterval)
	defer ticker.Stop()
	for {
		cycleCtx, cancel := context.WithTimeout(s.ctx, identityPairingLeadershipTimeout)
		if err := leadership.KeepAlive(cycleCtx); err != nil {
			cancel()
			if s.logger != nil {
				s.logger.Warn("identity pairing coordinator leadership lost",
					"error_category", "identity_coordinator_leadership_lost",
					"error", err,
				)
			}
			return true
		}
		s.reconcileIdentityRuntime(cycleCtx)
		s.reconcileCanonicalIdentityPairing(cycleCtx)
		cancel()
		select {
		case <-s.ctx.Done():
			return false
		case <-ticker.C:
		}
	}
}

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(ctx context.Context) {
	if s.identityRuntime == nil || !s.identityRuntime.ReconciliationRequired() {
		return
	}
	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",
			"error", err,
		)
	}
}

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