package backbeat

import (
	"encoding/json"
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/nats-io/nats.go"
)

// BackbeatIntegration provides beat-aware timing coordination for BUBBLE decision operations
type BackbeatIntegration struct {
	nc               *nats.Conn
	activeOperations sync.Map
	clusterID        string
	agentID          string
	tempo            int // beats per minute
}

// DecisionOperation represents a tracked decision bundle operation with beat timing
type DecisionOperation struct {
	ID               string                 `json:"id"`
	OperationType    DecisionOperationType  `json:"operation_type"`
	StartBeat        uint64                 `json:"start_beat"`
	EstimatedBeats   int                    `json:"estimated_beats"`
	Phase            DecisionPhase          `json:"phase"`
	StartTime        time.Time              `json:"start_time"`
	Data             map[string]interface{} `json:"data"`
}

// DecisionOperationType represents the type of decision operation being performed
type DecisionOperationType string

const (
	DecisionBundleGeneration DecisionOperationType = "bundle_generation"
	DecisionWalkback         DecisionOperationType = "walkback_traversal"
	DecisionScoring          DecisionOperationType = "decision_scoring"
	DecisionSummaryCreation  DecisionOperationType = "summary_creation"
	DecisionEvidenceGather   DecisionOperationType = "evidence_gathering"
)

// DecisionPhase represents the current phase of a decision operation
type DecisionPhase string

const (
	PhaseStarted     DecisionPhase = "started"
	PhaseTraversing  DecisionPhase = "traversing"
	PhaseScoring     DecisionPhase = "scoring" 
	PhaseSummarizing DecisionPhase = "summarizing"
	PhaseCompleted   DecisionPhase = "completed"
	PhaseFailed      DecisionPhase = "failed"
)

// DecisionResult contains the results of a completed decision operation
type DecisionResult struct {
	OperationID    string                 `json:"operation_id"`
	OperationType  DecisionOperationType  `json:"operation_type"`
	Success        bool                   `json:"success"`
	BeatsTaken     uint64                 `json:"beats_taken"`
	DurationMS     int64                  `json:"duration_ms"`
	StartBeat      uint64                 `json:"start_beat"`
	EndBeat        uint64                 `json:"end_beat"`
	ResultData     map[string]interface{} `json:"result_data"`
}

// DecisionStatus represents the current status of a decision operation for BACKBEAT network
type DecisionStatus struct {
	ClusterID      string                 `json:"cluster_id"`
	AgentID        string                 `json:"agent_id"`
	OperationID    string                 `json:"operation_id"`
	OperationType  DecisionOperationType  `json:"operation_type"`
	Phase          DecisionPhase          `json:"phase"`
	Beat           uint64                 `json:"beat"`
	EstimatedBeats int                    `json:"estimated_beats"`
	Timestamp      string                 `json:"timestamp"`
	Metadata       map[string]interface{} `json:"metadata,omitempty"`
}

// NewBackbeatIntegration creates a new BACKBEAT integration for decision tracking
func NewBackbeatIntegration(clusterID, agentID string, tempo int) *BackbeatIntegration {
	return &BackbeatIntegration{
		clusterID: clusterID,
		agentID:   agentID,
		tempo:     tempo,
	}
}

// Connect establishes connection to BACKBEAT infrastructure via NATS
func (b *BackbeatIntegration) Connect(natsURL string) error {
	log.Printf("🥁 Connecting to BACKBEAT infrastructure at %s", natsURL)
	
	nc, err := nats.Connect(natsURL)
	if err != nil {
		return fmt.Errorf("failed to connect to NATS: %w", err)
	}
	
	b.nc = nc
	log.Printf("✅ Connected to BACKBEAT tempo grid (%d bpm)", b.tempo)
	return nil
}

// Close closes the BACKBEAT connection
func (b *BackbeatIntegration) Close() {
	if b.nc != nil {
		b.nc.Close()
		log.Println("🔌 Disconnected from BACKBEAT infrastructure")
	}
}

// StartDecisionOperation begins tracking a decision operation with beat timing
func (b *BackbeatIntegration) StartDecisionOperation(operationID string, operationType DecisionOperationType, estimatedBeats int, data map[string]interface{}) error {
	currentBeat := b.getCurrentBeat()
	
	operation := &DecisionOperation{
		ID:             operationID,
		OperationType:  operationType,
		StartBeat:      currentBeat,
		EstimatedBeats: estimatedBeats,
		Phase:          PhaseStarted,
		StartTime:      time.Now(),
		Data:           data,
	}

	b.activeOperations.Store(operationID, operation)
	
	if err := b.emitDecisionStatus(operation); err != nil {
		log.Printf("⚠️ Failed to emit decision status: %v", err)
	}
	
	log.Printf("📊 Started %s operation %s at beat %d (estimated: %d beats)", 
		operationType, operationID, currentBeat, estimatedBeats)
	
	return nil
}

// UpdateDecisionPhase updates the phase of an active decision operation
func (b *BackbeatIntegration) UpdateDecisionPhase(operationID string, phase DecisionPhase) error {
	value, exists := b.activeOperations.Load(operationID)
	if !exists {
		log.Printf("⚠️ Decision operation %s not found for phase update", operationID)
		return fmt.Errorf("operation not found: %s", operationID)
	}
	
	operation := value.(*DecisionOperation)
	operation.Phase = phase
	
	if err := b.emitDecisionStatus(operation); err != nil {
		log.Printf("⚠️ Failed to emit decision status: %v", err)
	}
	
	log.Printf("🔄 Updated decision %s to phase %s", operationID, phase)
	return nil
}

// CompleteDecisionOperation finishes a decision operation and returns results
func (b *BackbeatIntegration) CompleteDecisionOperation(operationID string, success bool, resultData map[string]interface{}) (*DecisionResult, error) {
	value, exists := b.activeOperations.Load(operationID)
	if !exists {
		return nil, fmt.Errorf("operation not found: %s", operationID)
	}
	
	operation := value.(*DecisionOperation)
	b.activeOperations.Delete(operationID)
	
	currentBeat := b.getCurrentBeat()
	beatsTaken := currentBeat - operation.StartBeat
	duration := time.Since(operation.StartTime)
	
	result := &DecisionResult{
		OperationID:   operationID,
		OperationType: operation.OperationType,
		Success:       success,
		BeatsTaken:    beatsTaken,
		DurationMS:    duration.Milliseconds(),
		StartBeat:     operation.StartBeat,
		EndBeat:       currentBeat,
		ResultData:    resultData,
	}

	// Emit final status
	operation.Phase = PhaseCompleted
	if !success {
		operation.Phase = PhaseFailed
	}
	if err := b.emitDecisionStatus(operation); err != nil {
		log.Printf("⚠️ Failed to emit final decision status: %v", err)
	}
	
	log.Printf("✅ Completed decision %s - Success: %t (took %d beats, %dms)", 
		operationID, success, beatsTaken, duration.Milliseconds())
	
	return result, nil
}

// GetActiveOperations returns a list of currently active decision operations
func (b *BackbeatIntegration) GetActiveOperations() []*DecisionOperation {
	var operations []*DecisionOperation
	
	b.activeOperations.Range(func(key, value interface{}) bool {
		if operation, ok := value.(*DecisionOperation); ok {
			operations = append(operations, operation)
		}
		return true
	})
	
	return operations
}

// IsWithinBeatBudget checks if an operation is still within its estimated beat budget
func (b *BackbeatIntegration) IsWithinBeatBudget(operationID string) bool {
	value, exists := b.activeOperations.Load(operationID)
	if !exists {
		return false
	}
	
	operation := value.(*DecisionOperation)
	currentBeat := b.getCurrentBeat()
	beatsElapsed := currentBeat - operation.StartBeat
	
	return int(beatsElapsed) <= operation.EstimatedBeats
}

// TimeoutOperation forcefully times out an operation that exceeds its beat budget
func (b *BackbeatIntegration) TimeoutOperation(operationID string) error {
	log.Printf("⏰ Timing out decision operation %s due to beat budget exceeded", operationID)
	
	timeoutData := map[string]interface{}{
		"timeout_reason": "beat_budget_exceeded",
		"timeout_at_beat": b.getCurrentBeat(),
	}
	
	_, err := b.CompleteDecisionOperation(operationID, false, timeoutData)
	return err
}

// getCurrentBeat calculates the current beat based on system time and tempo
func (b *BackbeatIntegration) getCurrentBeat() uint64 {
	// Calculate beat based on 2 BPM tempo (30 seconds per beat)
	// In production, this would sync with BACKBEAT infrastructure
	epochTime := time.Now().Unix()
	beatsPerSecond := float64(b.tempo) / 60.0
	return uint64(float64(epochTime) * beatsPerSecond)
}

// emitDecisionStatus publishes decision operation status to BACKBEAT network
func (b *BackbeatIntegration) emitDecisionStatus(operation *DecisionOperation) error {
	if b.nc == nil {
		return nil // Not connected, skip emission
	}

	status := DecisionStatus{
		ClusterID:      b.clusterID,
		AgentID:        b.agentID,
		OperationID:    operation.ID,
		OperationType:  operation.OperationType,
		Phase:          operation.Phase,
		Beat:           b.getCurrentBeat(),
		EstimatedBeats: operation.EstimatedBeats,
		Timestamp:      time.Now().UTC().Format(time.RFC3339),
		Metadata:       operation.Data,
	}

	statusJSON, err := json.Marshal(status)
	if err != nil {
		return fmt.Errorf("failed to marshal status: %w", err)
	}

	// Publish to BACKBEAT decision tracking topic
	subject := fmt.Sprintf("backbeat.decision.%s.%s", b.clusterID, b.agentID)
	if err := b.nc.Publish(subject, statusJSON); err != nil {
		return fmt.Errorf("failed to publish status: %w", err)
	}

	log.Printf("📡 Emitted decision status: %s - %s", operation.ID, operation.Phase)
	return nil
}

// GetDecisionMetrics returns aggregated metrics for decision operations
func (b *BackbeatIntegration) GetDecisionMetrics() map[string]interface{} {
	activeOps := b.GetActiveOperations()
	
	operationTypes := make(map[DecisionOperationType]int)
	operationPhases := make(map[DecisionPhase]int)
	
	for _, op := range activeOps {
		operationTypes[op.OperationType]++
		operationPhases[op.Phase]++
	}
	
	return map[string]interface{}{
		"active_operations": len(activeOps),
		"operation_types":   operationTypes,
		"operation_phases":  operationPhases,
		"cluster_id":        b.clusterID,
		"agent_id":          b.agentID,
		"tempo_bpm":         b.tempo,
		"connected":         b.nc != nil && b.nc.IsConnected(),
	}
}