package handlers

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/breakpilot/ai-compliance-sdk/internal/llm"
	"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
	"github.com/breakpilot/ai-compliance-sdk/internal/ucca"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// UCCAHandlers handles UCCA-related API endpoints
type UCCAHandlers struct {
	store            *ucca.Store
	escalationStore  *ucca.EscalationStore
	policyEngine     *ucca.PolicyEngine
	legacyRuleEngine *ucca.RuleEngine // Keep for backwards compatibility
	providerRegistry *llm.ProviderRegistry
	legalRAGClient   *ucca.LegalRAGClient
	escalationTrigger *ucca.EscalationTrigger
}

// NewUCCAHandlers creates new UCCA handlers
func NewUCCAHandlers(store *ucca.Store, escalationStore *ucca.EscalationStore, providerRegistry *llm.ProviderRegistry) *UCCAHandlers {
	// Try to create YAML-based policy engine first
	policyEngine, err := ucca.NewPolicyEngine()
	if err != nil {
		// Log warning but don't fail - fall back to legacy engine
		fmt.Printf("Warning: Could not load YAML policy engine: %v. Falling back to legacy rules.\n", err)
	}

	return &UCCAHandlers{
		store:            store,
		escalationStore:  escalationStore,
		policyEngine:     policyEngine, // May be nil if YAML loading failed
		legacyRuleEngine: ucca.NewRuleEngine(),
		providerRegistry: providerRegistry,
		legalRAGClient:   ucca.NewLegalRAGClient(),
		escalationTrigger: ucca.DefaultEscalationTrigger(),
	}
}

// evaluateIntake runs evaluation using YAML engine or legacy fallback
func (h *UCCAHandlers) evaluateIntake(intake *ucca.UseCaseIntake) (*ucca.AssessmentResult, string) {
	if h.policyEngine != nil {
		return h.policyEngine.Evaluate(intake), h.policyEngine.GetPolicyVersion()
	}
	return h.legacyRuleEngine.Evaluate(intake), "1.0.0-legacy"
}

// Assess evaluates a use case intake and creates an assessment
func (h *UCCAHandlers) Assess(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	userID := rbac.GetUserID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	var intake ucca.UseCaseIntake
	if err := c.ShouldBindJSON(&intake); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	result, policyVersion := h.evaluateIntake(&intake)

	// Calculate hash of use case text
	hash := sha256.Sum256([]byte(intake.UseCaseText))
	hashStr := hex.EncodeToString(hash[:])

	assessment := &ucca.Assessment{
		TenantID:                tenantID,
		Title:                   intake.Title,
		PolicyVersion:           policyVersion,
		Status:                  "completed",
		Intake:                  intake,
		UseCaseTextStored:       intake.StoreRawText,
		UseCaseTextHash:         hashStr,
		Feasibility:             result.Feasibility,
		RiskLevel:               result.RiskLevel,
		Complexity:              result.Complexity,
		RiskScore:               result.RiskScore,
		TriggeredRules:          result.TriggeredRules,
		RequiredControls:        result.RequiredControls,
		RecommendedArchitecture: result.RecommendedArchitecture,
		ForbiddenPatterns:       result.ForbiddenPatterns,
		ExampleMatches:          result.ExampleMatches,
		DSFARecommended:         result.DSFARecommended,
		Art22Risk:               result.Art22Risk,
		TrainingAllowed:         result.TrainingAllowed,
		Domain:                  intake.Domain,
		CreatedBy:               userID,
	}

	if !intake.StoreRawText {
		assessment.Intake.UseCaseText = ""
	}
	if assessment.Title == "" {
		assessment.Title = fmt.Sprintf("Assessment vom %s", time.Now().Format("02.01.2006 15:04"))
	}

	if err := h.store.CreateAssessment(c.Request.Context(), assessment); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	escalation := h.createEscalationForAssessment(c, assessment, result, tenantID, userID)

	c.JSON(http.StatusCreated, ucca.AssessResponse{
		Assessment: *assessment,
		Result:     *result,
		Escalation: escalation,
	})
}

// ListAssessments returns all assessments for a tenant
func (h *UCCAHandlers) ListAssessments(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	filters := &ucca.AssessmentFilters{
		Feasibility: c.Query("feasibility"),
		Domain:      c.Query("domain"),
		RiskLevel:   c.Query("risk_level"),
		Search:      c.Query("search"),
	}
	if limit, err := strconv.Atoi(c.DefaultQuery("limit", "0")); err == nil {
		filters.Limit = limit
	}
	if offset, err := strconv.Atoi(c.DefaultQuery("offset", "0")); err == nil {
		filters.Offset = offset
	}

	assessments, total, err := h.store.ListAssessments(c.Request.Context(), tenantID, filters)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"assessments": assessments, "total": total})
}

// GetAssessment returns a single assessment by ID
func (h *UCCAHandlers) GetAssessment(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	assessment, err := h.store.GetAssessment(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	if assessment == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
		return
	}

	c.JSON(http.StatusOK, assessment)
}

// DeleteAssessment deletes an assessment
func (h *UCCAHandlers) DeleteAssessment(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	if err := h.store.DeleteAssessment(c.Request.Context(), id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "deleted"})
}

// UpdateAssessment re-evaluates and updates an existing assessment
func (h *UCCAHandlers) UpdateAssessment(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
		return
	}

	var intake ucca.UseCaseIntake
	if err := c.ShouldBindJSON(&intake); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	result, policyVersion := h.evaluateIntake(&intake)

	hash := sha256.Sum256([]byte(intake.UseCaseText))
	hashStr := hex.EncodeToString(hash[:])

	updated := &ucca.Assessment{
		Title:                   intake.Title,
		PolicyVersion:           policyVersion,
		Intake:                  intake,
		UseCaseTextStored:       intake.StoreRawText,
		UseCaseTextHash:         hashStr,
		Feasibility:             result.Feasibility,
		RiskLevel:               result.RiskLevel,
		Complexity:              result.Complexity,
		RiskScore:               result.RiskScore,
		TriggeredRules:          result.TriggeredRules,
		RequiredControls:        result.RequiredControls,
		RecommendedArchitecture: result.RecommendedArchitecture,
		ForbiddenPatterns:       result.ForbiddenPatterns,
		ExampleMatches:          result.ExampleMatches,
		DSFARecommended:         result.DSFARecommended,
		Art22Risk:               result.Art22Risk,
		TrainingAllowed:         result.TrainingAllowed,
		Domain:                  intake.Domain,
	}
	if !intake.StoreRawText {
		updated.Intake.UseCaseText = ""
	}
	if updated.Title == "" {
		updated.Title = fmt.Sprintf("Assessment vom %s", time.Now().Format("02.01.2006 15:04"))
	}

	if err := h.store.UpdateAssessment(c.Request.Context(), id, updated); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	assessment, err := h.store.GetAssessment(c.Request.Context(), id)
	if err != nil || assessment == nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "assessment not found after update"})
		return
	}
	c.JSON(http.StatusOK, assessment)
}

// GetStats returns UCCA statistics for a tenant
func (h *UCCAHandlers) GetStats(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	stats, err := h.store.GetStats(c.Request.Context(), tenantID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, stats)
}

// createEscalationForAssessment automatically creates an escalation based on assessment result
func (h *UCCAHandlers) createEscalationForAssessment(c *gin.Context, assessment *ucca.Assessment, result *ucca.AssessmentResult, tenantID, userID uuid.UUID) *ucca.Escalation {
	if h.escalationStore == nil || h.escalationTrigger == nil {
		return nil
	}

	level, reason := h.escalationTrigger.DetermineEscalationLevel(result)

	responseHours, _ := ucca.GetDefaultSLA(level)
	var dueDate *time.Time
	if responseHours > 0 {
		due := time.Now().UTC().Add(time.Duration(responseHours) * time.Hour)
		dueDate = &due
	}

	escalation := &ucca.Escalation{
		TenantID:         tenantID,
		AssessmentID:     assessment.ID,
		EscalationLevel:  level,
		EscalationReason: reason,
		Status:           ucca.EscalationStatusPending,
		DueDate:          dueDate,
	}

	if level == ucca.EscalationLevelE0 {
		escalation.Status = ucca.EscalationStatusApproved
		approveDecision := ucca.EscalationDecisionApprove
		escalation.Decision = &approveDecision
		now := time.Now().UTC()
		escalation.DecisionAt = &now
		autoNotes := "Automatische Freigabe (E0)"
		escalation.DecisionNotes = &autoNotes
	}

	if err := h.escalationStore.CreateEscalation(c.Request.Context(), escalation); err != nil {
		fmt.Printf("Warning: Could not create escalation: %v\n", err)
		return nil
	}

	h.escalationStore.AddEscalationHistory(c.Request.Context(), &ucca.EscalationHistory{
		EscalationID: escalation.ID,
		Action:       "auto_created",
		NewStatus:    string(escalation.Status),
		NewLevel:     string(escalation.EscalationLevel),
		ActorID:      userID,
		Notes:        "Automatisch erstellt bei Assessment",
	})

	if level != ucca.EscalationLevelE0 {
		role := ucca.GetRoleForLevel(level)
		reviewer, err := h.escalationStore.GetNextAvailableReviewer(c.Request.Context(), tenantID, role)
		if err == nil && reviewer != nil {
			h.escalationStore.AssignEscalation(c.Request.Context(), escalation.ID, reviewer.UserID, role)
			h.escalationStore.IncrementReviewerCount(c.Request.Context(), reviewer.UserID)
			h.escalationStore.AddEscalationHistory(c.Request.Context(), &ucca.EscalationHistory{
				EscalationID: escalation.ID,
				Action:       "auto_assigned",
				OldStatus:    string(ucca.EscalationStatusPending),
				NewStatus:    string(ucca.EscalationStatusAssigned),
				ActorID:      userID,
				Notes:        "Automatisch zugewiesen an: " + reviewer.UserName,
			})
		}
	}

	return escalation
}

// AssessEnriched evaluates a use case with optional company profile context.
func (h *UCCAHandlers) AssessEnriched(c *gin.Context) {
	tenantID := rbac.GetTenantID(c)
	userID := rbac.GetUserID(c)
	if tenantID == uuid.Nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
		return
	}

	var req struct {
		Intake         ucca.UseCaseIntake        `json:"intake"`
		CompanyProfile *ucca.CompanyProfileInput  `json:"company_profile,omitempty"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Standard UCCA evaluation
	result, policyVersion := h.evaluateIntake(&req.Intake)
	hash := sha256.Sum256([]byte(req.Intake.UseCaseText))

	assessment := &ucca.Assessment{
		TenantID: tenantID, Title: req.Intake.Title, PolicyVersion: policyVersion,
		Status: "completed", Intake: req.Intake,
		UseCaseTextStored: req.Intake.StoreRawText, UseCaseTextHash: hex.EncodeToString(hash[:]),
		Feasibility: result.Feasibility, RiskLevel: result.RiskLevel,
		Complexity: result.Complexity, RiskScore: result.RiskScore,
		TriggeredRules: result.TriggeredRules, RequiredControls: result.RequiredControls,
		RecommendedArchitecture: result.RecommendedArchitecture,
		ForbiddenPatterns: result.ForbiddenPatterns, ExampleMatches: result.ExampleMatches,
		DSFARecommended: result.DSFARecommended, Art22Risk: result.Art22Risk,
		TrainingAllowed: result.TrainingAllowed, Domain: req.Intake.Domain, CreatedBy: userID,
	}
	if !req.Intake.StoreRawText {
		assessment.Intake.UseCaseText = ""
	}
	if assessment.Title == "" {
		assessment.Title = fmt.Sprintf("Assessment vom %s", time.Now().Format("02.01.2006 15:04"))
	}
	if err := h.store.CreateAssessment(c.Request.Context(), assessment); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Build enriched response
	resp := gin.H{
		"assessment": assessment,
		"result":     result,
	}

	// Company profile enrichment
	if req.CompanyProfile != nil {
		resp["enrichment_hints"] = ucca.ComputeEnrichmentHints(req.CompanyProfile)
		resp["company_context"] = ucca.BuildCompanyContext(req.CompanyProfile)
	} else {
		resp["enrichment_hints"] = ucca.ComputeEnrichmentHints(nil)
	}

	c.JSON(http.StatusCreated, resp)
}
