package find

import (
	"context"
	"fmt"
	"sync"
	"time"

	"lol.mleku.dev/chk"
	"next.orly.dev/pkg/database"
	"git.mleku.dev/mleku/nostr/encoders/event"
	"git.mleku.dev/mleku/nostr/encoders/hex"
	"git.mleku.dev/mleku/nostr/interfaces/signer"
)

// RegistryService implements the FIND name registry consensus protocol
type RegistryService struct {
	ctx              context.Context
	cancel           context.CancelFunc
	db               database.Database
	signer           signer.I
	trustGraph       *TrustGraph
	consensus        *ConsensusEngine
	config           *RegistryConfig
	pendingProposals map[string]*ProposalState
	mu               sync.RWMutex
	wg               sync.WaitGroup
}

// RegistryConfig holds configuration for the registry service
type RegistryConfig struct {
	Enabled            bool
	AttestationDelay   time.Duration
	SparseEnabled      bool
	SamplingRate       int
	BootstrapServices  []string
	MinimumAttesters   int
}

// ProposalState tracks a proposal during its attestation window
type ProposalState struct {
	Proposal     *RegistrationProposal
	Attestations []*Attestation
	ReceivedAt   time.Time
	ProcessedAt  *time.Time
	Timer        *time.Timer
}

// NewRegistryService creates a new registry service
func NewRegistryService(ctx context.Context, db database.Database, signer signer.I, config *RegistryConfig) (*RegistryService, error) {
	if !config.Enabled {
		return nil, nil
	}

	ctx, cancel := context.WithCancel(ctx)

	trustGraph := NewTrustGraph(signer.Pub())
	consensus := NewConsensusEngine(db, trustGraph)

	rs := &RegistryService{
		ctx:              ctx,
		cancel:           cancel,
		db:               db,
		signer:           signer,
		trustGraph:       trustGraph,
		consensus:        consensus,
		config:           config,
		pendingProposals: make(map[string]*ProposalState),
	}

	// Bootstrap trust graph if configured
	if len(config.BootstrapServices) > 0 {
		if err := rs.bootstrapTrustGraph(); chk.E(err) {
			fmt.Printf("failed to bootstrap trust graph: %v\n", err)
		}
	}

	return rs, nil
}

// Start starts the registry service
func (rs *RegistryService) Start() error {
	fmt.Println("starting FIND registry service")

	// Start proposal monitoring goroutine
	rs.wg.Add(1)
	go rs.monitorProposals()

	// Start attestation collection goroutine
	rs.wg.Add(1)
	go rs.collectAttestations()

	// Start trust graph refresh goroutine
	rs.wg.Add(1)
	go rs.refreshTrustGraph()

	return nil
}

// Stop stops the registry service
func (rs *RegistryService) Stop() error {
	fmt.Println("stopping FIND registry service")

	rs.cancel()
	rs.wg.Wait()

	return nil
}

// monitorProposals monitors for new registration proposals
func (rs *RegistryService) monitorProposals() {
	defer rs.wg.Done()

	ticker := time.NewTicker(10 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-rs.ctx.Done():
			return
		case <-ticker.C:
			rs.checkForNewProposals()
		}
	}
}

// checkForNewProposals checks database for new registration proposals
func (rs *RegistryService) checkForNewProposals() {
	// Query recent kind 30100 events (registration proposals)
	// This would use the actual database query API
	// For now, this is a stub

	// TODO: Implement database query for kind 30100 events
	// TODO: Parse proposals and add to pendingProposals map
	// TODO: Start attestation timer for each new proposal
}

// OnProposalReceived is called when a new proposal is received
func (rs *RegistryService) OnProposalReceived(proposal *RegistrationProposal) error {
	// Validate proposal
	if err := rs.consensus.ValidateProposal(proposal); chk.E(err) {
		fmt.Printf("invalid proposal: %v\n", err)
		return err
	}

	proposalID := hex.Enc(proposal.Event.ID)

	rs.mu.Lock()
	defer rs.mu.Unlock()

	// Check if already processing
	if _, exists := rs.pendingProposals[proposalID]; exists {
		return nil
	}

	fmt.Printf("received new proposal: %s name: %s\n", proposalID, proposal.Name)

	// Create proposal state
	state := &ProposalState{
		Proposal:     proposal,
		Attestations: make([]*Attestation, 0),
		ReceivedAt:   time.Now(),
	}

	// Start attestation timer
	state.Timer = time.AfterFunc(rs.config.AttestationDelay, func() {
		rs.processProposal(proposalID)
	})

	rs.pendingProposals[proposalID] = state

	// Publish attestation (if not using sparse or if dice roll succeeds)
	if rs.shouldAttest(proposalID) {
		go rs.publishAttestation(proposal, DecisionApprove, "valid_proposal")
	}

	return nil
}

// shouldAttest determines if this service should attest to a proposal
func (rs *RegistryService) shouldAttest(proposalID string) bool {
	if !rs.config.SparseEnabled {
		return true
	}

	// Sparse attestation: use hash of (proposal_id || service_pubkey) % K == 0
	// This provides deterministic but distributed attestation
	hash, err := hex.Dec(proposalID)
	if err != nil || len(hash) == 0 {
		return false
	}

	// Simple modulo check using first byte of hash
	return int(hash[0])%rs.config.SamplingRate == 0
}

// publishAttestation publishes an attestation for a proposal
func (rs *RegistryService) publishAttestation(proposal *RegistrationProposal, decision string, reason string) {
	attestation := &Attestation{
		ProposalID: hex.Enc(proposal.Event.ID),
		Decision:   decision,
		Weight:     100,
		Reason:     reason,
		ServiceURL: "", // TODO: Get from config
		Expiration: time.Now().Add(AttestationExpiry),
	}

	// TODO: Create and sign attestation event (kind 20100)
	// TODO: Publish to database
	_ = attestation

	fmt.Printf("published attestation for proposal: %s decision: %s\n", proposal.Name, decision)
}

// collectAttestations collects attestations from other registry services
func (rs *RegistryService) collectAttestations() {
	defer rs.wg.Done()

	ticker := time.NewTicker(5 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-rs.ctx.Done():
			return
		case <-ticker.C:
			rs.updateAttestations()
		}
	}
}

// updateAttestations fetches new attestations from database
func (rs *RegistryService) updateAttestations() {
	rs.mu.RLock()
	proposalIDs := make([]string, 0, len(rs.pendingProposals))
	for id := range rs.pendingProposals {
		proposalIDs = append(proposalIDs, id)
	}
	rs.mu.RUnlock()

	if len(proposalIDs) == 0 {
		return
	}

	// TODO: Query kind 20100 events (attestations) for pending proposals
	// TODO: Add attestations to proposal states
}

// processProposal processes a proposal after the attestation window expires
func (rs *RegistryService) processProposal(proposalID string) {
	rs.mu.Lock()
	state, exists := rs.pendingProposals[proposalID]
	if !exists {
		rs.mu.Unlock()
		return
	}

	// Mark as processed
	now := time.Now()
	state.ProcessedAt = &now
	rs.mu.Unlock()

	fmt.Printf("processing proposal: %s name: %s\n", proposalID, state.Proposal.Name)

	// Check for competing proposals for the same name
	competingProposals := rs.getCompetingProposals(state.Proposal.Name)

	// Gather all attestations
	allAttestations := make([]*Attestation, 0)
	for _, p := range competingProposals {
		allAttestations = append(allAttestations, p.Attestations...)
	}

	// Compute consensus
	proposalList := make([]*RegistrationProposal, 0, len(competingProposals))
	for _, p := range competingProposals {
		proposalList = append(proposalList, p.Proposal)
	}

	result, err := rs.consensus.ComputeConsensus(proposalList, allAttestations)
	if chk.E(err) {
		fmt.Printf("consensus computation failed: %v\n", err)
		return
	}

	// Log result
	if result.Conflicted {
		fmt.Printf("consensus conflicted for name: %s reason: %s\n", state.Proposal.Name, result.Reason)
		return
	}

	fmt.Printf("consensus reached for name: %s winner: %s confidence: %f\n",
		state.Proposal.Name,
		hex.Enc(result.Winner.Event.ID),
		result.Confidence)

	// Publish name state (kind 30102)
	if err := rs.publishNameState(result); chk.E(err) {
		fmt.Printf("failed to publish name state: %v\n", err)
		return
	}

	// Clean up processed proposals
	rs.cleanupProposals(state.Proposal.Name)
}

// getCompetingProposals returns all pending proposals for the same name
func (rs *RegistryService) getCompetingProposals(name string) []*ProposalState {
	rs.mu.RLock()
	defer rs.mu.RUnlock()

	proposals := make([]*ProposalState, 0)
	for _, state := range rs.pendingProposals {
		if state.Proposal.Name == name {
			proposals = append(proposals, state)
		}
	}

	return proposals
}

// publishNameState publishes a name state event after consensus
func (rs *RegistryService) publishNameState(result *ConsensusResult) error {
	nameState, err := rs.consensus.CreateNameState(result, rs.signer.Pub())
	if err != nil {
		return err
	}

	// TODO: Create kind 30102 event
	// TODO: Sign with registry service key
	// TODO: Publish to database
	_ = nameState

	return nil
}

// cleanupProposals removes processed proposals from the pending map
func (rs *RegistryService) cleanupProposals(name string) {
	rs.mu.Lock()
	defer rs.mu.Unlock()

	for id, state := range rs.pendingProposals {
		if state.Proposal.Name == name && state.ProcessedAt != nil {
			// Cancel timer if still running
			if state.Timer != nil {
				state.Timer.Stop()
			}
			delete(rs.pendingProposals, id)
		}
	}
}

// refreshTrustGraph periodically refreshes the trust graph from other services
func (rs *RegistryService) refreshTrustGraph() {
	defer rs.wg.Done()

	ticker := time.NewTicker(1 * time.Hour)
	defer ticker.Stop()

	for {
		select {
		case <-rs.ctx.Done():
			return
		case <-ticker.C:
			rs.updateTrustGraph()
		}
	}
}

// updateTrustGraph fetches trust graphs from other services
func (rs *RegistryService) updateTrustGraph() {
	fmt.Println("updating trust graph")

	// TODO: Query kind 30101 events (trust graphs) from database
	// TODO: Parse and update trust graph
	// TODO: Remove expired trust graphs
}

// bootstrapTrustGraph initializes trust relationships with bootstrap services
func (rs *RegistryService) bootstrapTrustGraph() error {
	fmt.Printf("bootstrapping trust graph with %d services\n", len(rs.config.BootstrapServices))

	for _, pubkeyHex := range rs.config.BootstrapServices {
		entry := TrustEntry{
			Pubkey:     pubkeyHex,
			ServiceURL: "",
			TrustScore: 0.7, // Medium trust for bootstrap services
		}

		if err := rs.trustGraph.AddEntry(entry); chk.E(err) {
			fmt.Printf("failed to add bootstrap trust entry: %v\n", err)
			continue
		}
	}

	return nil
}

// GetTrustGraph returns the current trust graph
func (rs *RegistryService) GetTrustGraph() *TrustGraph {
	return rs.trustGraph
}

// GetMetrics returns registry service metrics
func (rs *RegistryService) GetMetrics() *RegistryMetrics {
	rs.mu.RLock()
	defer rs.mu.RUnlock()

	metrics := &RegistryMetrics{
		PendingProposals: len(rs.pendingProposals),
		TrustMetrics:     rs.trustGraph.CalculateTrustMetrics(),
	}

	return metrics
}

// RegistryMetrics holds metrics about the registry service
type RegistryMetrics struct {
	PendingProposals int
	TrustMetrics     *TrustMetrics
}

// QueryNameOwnership queries the ownership state of a name
func (rs *RegistryService) QueryNameOwnership(name string) (*NameState, error) {
	return rs.consensus.QueryNameState(name)
}

// ValidateProposal validates a proposal without adding it to pending
func (rs *RegistryService) ValidateProposal(proposal *RegistrationProposal) error {
	return rs.consensus.ValidateProposal(proposal)
}

// HandleEvent processes incoming FIND-related events
func (rs *RegistryService) HandleEvent(ev *event.E) error {
	switch ev.Kind {
	case KindRegistrationProposal:
		// Parse proposal
		proposal, err := ParseRegistrationProposal(ev)
		if err != nil {
			return err
		}
		return rs.OnProposalReceived(proposal)

	case KindAttestation:
		// Parse attestation
		// TODO: Implement attestation parsing and handling
		return nil

	case KindTrustGraph:
		// Parse trust graph
		// TODO: Implement trust graph parsing and integration
		return nil

	default:
		return nil
	}
}
