package api

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"gitea.deepblack.cloud/chorus/bubble/backbeat"
	"gitea.deepblack.cloud/chorus/bubble/core"
	"gitea.deepblack.cloud/chorus/bubble/models"
	"gitea.deepblack.cloud/chorus/bubble/storage"
)

// Server holds the dependencies for the API server.
type Server struct {
	Store    storage.Storage
	Backbeat *backbeat.BackbeatIntegration
}

// NewServer creates a new API server.
func NewServer(store storage.Storage) *Server {
	return &Server{
		Store: store,
	}
}

// SetBackbeatIntegration adds BACKBEAT timing integration to the server
func (s *Server) SetBackbeatIntegration(bb *backbeat.BackbeatIntegration) {
	s.Backbeat = bb
}

// Start begins listening for HTTP requests.
func (s *Server) Start(addr string) error {
	http.HandleFunc("/decision/bundle", s.handleDecisionBundle)
	http.HandleFunc("/backbeat/metrics", s.handleBackbeatMetrics)
	http.HandleFunc("/backbeat/operations", s.handleBackbeatOperations)
	return http.ListenAndServe(addr, nil)
}

// handleDecisionBundle is the handler for the /decision/bundle endpoint with BACKBEAT timing.
func (s *Server) handleDecisionBundle(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
		return
	}

	var req models.DecisionBundleRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if req.StartID == "" && req.Query == "" {
		http.Error(w, "start_id or query is required", http.StatusBadRequest)
		return
	}

	// Generate operation ID for BACKBEAT tracking
	operationID := fmt.Sprintf("bundle-%d", time.Now().UnixNano())
	
	// Estimate beats based on complexity
	estimatedBeats := s.estimateBundleComplexity(req)
	
	// Start BACKBEAT tracking if available
	if s.Backbeat != nil {
		operationData := map[string]interface{}{
			"start_id":   req.StartID,
			"role":       req.Role,
			"max_hops":   req.MaxHops,
			"top_k":      req.TopK,
			"query":      req.Query,
		}
		
		if err := s.Backbeat.StartDecisionOperation(operationID, backbeat.DecisionBundleGeneration, estimatedBeats, operationData); err != nil {
			log.Printf("⚠️ Failed to start BACKBEAT tracking: %v", err)
		}
	}

	// --- Core Logic ---
	// Phase 1: Node resolution and validation
	if s.Backbeat != nil {
		s.Backbeat.UpdateDecisionPhase(operationID, backbeat.PhaseTraversing)
	}
	
	startNode, err := s.Store.GetDecisionMetadata(req.StartID)
	if err != nil {
		s.completeOperation(operationID, false, map[string]interface{}{"error": "failed to get start node", "details": err.Error()})
		http.Error(w, fmt.Sprintf("Failed to get start node: %v", err), http.StatusInternalServerError)
		return
	}
	if startNode == nil {
		s.completeOperation(operationID, false, map[string]interface{}{"error": "start node not found"})
		http.Error(w, "Start node not found", http.StatusNotFound)
		return
	}

	// Phase 2: Perform the provenance walk with scoring
	if s.Backbeat != nil {
		s.Backbeat.UpdateDecisionPhase(operationID, backbeat.PhaseScoring)
	}
	
	timeline, err := core.WalkBack(s.Store, req.StartID, req.Query, req.Role, req.MaxHops, req.TopK)
	if err != nil {
		s.completeOperation(operationID, false, map[string]interface{}{"error": "walkback failed", "details": err.Error()})
		http.Error(w, fmt.Sprintf("Failed to walk provenance graph: %v", err), http.StatusInternalServerError)
		return
	}

	// Phase 3: Generate summary and assemble bundle
	if s.Backbeat != nil {
		s.Backbeat.UpdateDecisionPhase(operationID, backbeat.PhaseSummarizing)
	}

	// Assemble the response bundle with enhanced metadata
	response := models.DecisionBundleResponse{
		BundleID:           fmt.Sprintf("bundle:%s", req.StartID),
		StartID:            req.StartID,
		GeneratedAt:        time.Now().UTC().Format(time.RFC3339),
		Summary:            fmt.Sprintf("Decision bundle for %s, found %d related decisions through %d-hop traversal.", req.StartID, len(timeline), req.MaxHops),
		Timeline:           timeline,
		ConstraintsSummary: s.generateConstraintsSummary(timeline),
		KeyEvidenceRefs:    s.extractEvidenceRefs(timeline),
		GoalAlignment:      s.calculateGoalAlignment(timeline, req.Query),
		SuggestedActions:   s.generateSuggestedActions(timeline, req.Role),
		Escalation:         s.checkEscalationNeeded(timeline),
		CacheHit:           false, // Caching not yet implemented
	}

	// Complete BACKBEAT tracking
	resultData := map[string]interface{}{
		"bundle_id":       response.BundleID,
		"decisions_found": len(timeline),
		"start_node_id":   req.StartID,
		"role":            req.Role,
		"max_hops":        req.MaxHops,
	}
	
	s.completeOperation(operationID, true, resultData)

	w.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(w).Encode(response); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// completeOperation completes BACKBEAT tracking if available
func (s *Server) completeOperation(operationID string, success bool, resultData map[string]interface{}) {
	if s.Backbeat != nil {
		if _, err := s.Backbeat.CompleteDecisionOperation(operationID, success, resultData); err != nil {
			log.Printf("⚠️ Failed to complete BACKBEAT operation: %v", err)
		}
	}
}

// estimateBundleComplexity estimates the number of beats required based on request complexity
func (s *Server) estimateBundleComplexity(req models.DecisionBundleRequest) int {
	baseBeats := 2 // Base complexity for simple operations
	
	// Add beats based on max hops (traversal complexity)
	if req.MaxHops > 5 {
		baseBeats += (req.MaxHops - 5) / 2
	}
	
	// Add beats based on top-K (scoring complexity)
	if req.TopK > 10 {
		baseBeats += (req.TopK - 10) / 5
	}
	
	// Add beats for query processing if present
	if req.Query != "" {
		baseBeats += 1
	}
	
	// Add beats for full decision record inclusion
	if req.IncludeFullDR {
		baseBeats += 2
	}
	
	return baseBeats
}

// generateConstraintsSummary creates a summary of constraints from the decision timeline
func (s *Server) generateConstraintsSummary(timeline []models.DecisionRecordSummary) []string {
	constraints := make([]string, 0)
	
	for _, decision := range timeline {
		if len(decision.Tags) > 0 {
			for _, tag := range decision.Tags {
				if tag == "constraint" || tag == "requirement" || tag == "policy" {
					constraint := fmt.Sprintf("From %s: %s", decision.ID, decision.Statement)
					constraints = append(constraints, constraint)
				}
			}
		}
	}
	
	if len(constraints) == 0 {
		constraints = append(constraints, "No explicit constraints identified in decision timeline")
	}
	
	return constraints
}

// extractEvidenceRefs extracts key evidence references from the timeline
func (s *Server) extractEvidenceRefs(timeline []models.DecisionRecordSummary) []string {
	evidenceRefs := make([]string, 0)
	
	for _, decision := range timeline {
		if len(decision.EvidenceRefs) > 0 {
			evidenceRefs = append(evidenceRefs, decision.EvidenceRefs...)
		}
	}
	
	if len(evidenceRefs) == 0 {
		evidenceRefs = append(evidenceRefs, "No evidence references found in timeline")
	}
	
	return evidenceRefs
}

// calculateGoalAlignment analyzes how well decisions align with stated goals
func (s *Server) calculateGoalAlignment(timeline []models.DecisionRecordSummary, query string) models.GoalAlignment {
	if len(timeline) == 0 {
		return models.GoalAlignment{
			Score:   0.0,
			Reasons: []string{"No decisions found for alignment analysis"},
		}
	}
	
	// Simple scoring based on decision lifecycle states
	activeDecisions := 0
	completedDecisions := 0
	
	for _, decision := range timeline {
		switch decision.LifecycleState {
		case "active", "implementing":
			activeDecisions++
		case "completed", "validated":
			completedDecisions++
		}
	}
	
	// Calculate alignment score based on decision maturity
	score := float64(completedDecisions) / float64(len(timeline)) * 0.7
	score += float64(activeDecisions) / float64(len(timeline)) * 0.3
	
	reasons := []string{
		fmt.Sprintf("Found %d completed decisions out of %d total", completedDecisions, len(timeline)),
		fmt.Sprintf("Found %d active decisions indicating ongoing progress", activeDecisions),
	}
	
	if query != "" {
		reasons = append(reasons, fmt.Sprintf("Analysis filtered for query: '%s'", query))
	}
	
	return models.GoalAlignment{
		Score:   score,
		Reasons: reasons,
	}
}

// generateSuggestedActions creates recommended next steps based on the decision timeline
func (s *Server) generateSuggestedActions(timeline []models.DecisionRecordSummary, role string) []models.SuggestedAction {
	actions := make([]models.SuggestedAction, 0)
	
	if len(timeline) == 0 {
		actions = append(actions, models.SuggestedAction{
			Type:        "investigate",
			Description: "No related decisions found - investigate if this is a new decision domain",
			Assignee:    role,
			Confidence:  0.8,
		})
		return actions
	}
	
	// Analyze decision states to suggest actions
	pendingDecisions := 0
	activeDecisions := 0
	
	for _, decision := range timeline {
		switch decision.LifecycleState {
		case "pending", "proposed":
			pendingDecisions++
		case "active", "implementing":
			activeDecisions++
		}
	}
	
	if pendingDecisions > 0 {
		actions = append(actions, models.SuggestedAction{
			Type:        "review",
			Description: fmt.Sprintf("Review %d pending decisions for approval or closure", pendingDecisions),
			Assignee:    role,
			Confidence:  0.9,
		})
	}
	
	if activeDecisions > 2 {
		actions = append(actions, models.SuggestedAction{
			Type:        "monitor",
			Description: fmt.Sprintf("Monitor progress on %d active decisions to prevent bottlenecks", activeDecisions),
			Assignee:    role,
			Confidence:  0.7,
		})
	}
	
	if len(actions) == 0 {
		actions = append(actions, models.SuggestedAction{
			Type:        "document",
			Description: "Document lessons learned from completed decision timeline",
			Assignee:    role,
			Confidence:  0.6,
		})
	}
	
	return actions
}

// checkEscalationNeeded determines if human escalation is required
func (s *Server) checkEscalationNeeded(timeline []models.DecisionRecordSummary) models.Escalation {
	if len(timeline) == 0 {
		return models.Escalation{
			Required: false,
		}
	}
	
	// Check for escalation indicators
	conflictingDecisions := 0
	highRiskDecisions := 0
	
	for _, decision := range timeline {
		for _, tag := range decision.Tags {
			if tag == "conflict" || tag == "contradiction" {
				conflictingDecisions++
			}
			if tag == "high-risk" || tag == "critical" {
				highRiskDecisions++
			}
		}
	}
	
	if conflictingDecisions > 0 || highRiskDecisions > 1 {
		return models.Escalation{
			Required: true,
			Who:      []string{"decision_lead", "technical_lead", "product_owner"},
		}
	}
	
	return models.Escalation{
		Required: false,
	}
}

// handleBackbeatMetrics returns BACKBEAT metrics for monitoring
func (s *Server) handleBackbeatMetrics(w http.ResponseWriter, r *http.Request) {
	if s.Backbeat == nil {
		http.Error(w, "BACKBEAT integration not available", http.StatusServiceUnavailable)
		return
	}
	
	metrics := s.Backbeat.GetDecisionMetrics()
	w.Header().Set("Content-Type", "application/json")
	
	if err := json.NewEncoder(w).Encode(metrics); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// handleBackbeatOperations returns currently active BACKBEAT operations
func (s *Server) handleBackbeatOperations(w http.ResponseWriter, r *http.Request) {
	if s.Backbeat == nil {
		http.Error(w, "BACKBEAT integration not available", http.StatusServiceUnavailable)
		return
	}
	
	operations := s.Backbeat.GetActiveOperations()
	w.Header().Set("Content-Type", "application/json")
	
	if err := json.NewEncoder(w).Encode(operations); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}