package handlers

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/breakpilot/ai-compliance-sdk/internal/iace"
	"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"
)

// ============================================================================
// Handler Struct & Constructor
// ============================================================================

// IACEHandler handles HTTP requests for the IACE module (Inherent-risk Adjusted
// Control Effectiveness). It provides endpoints for project management, component
// onboarding, regulatory classification, hazard/risk analysis, evidence management,
// CE technical file generation, and post-market monitoring.
type IACEHandler struct {
	store       *iace.Store
	engine      *iace.RiskEngine
	classifier  *iace.Classifier
	checker     *iace.CompletenessChecker
	ragClient   *ucca.LegalRAGClient
	techFileGen *iace.TechFileGenerator
	exporter    *iace.DocumentExporter
	llmRegistry *llm.ProviderRegistry
}

// NewIACEHandler creates a new IACEHandler with all required dependencies.
func NewIACEHandler(store *iace.Store, providerRegistry *llm.ProviderRegistry) *IACEHandler {
	ragClient := ucca.NewLegalRAGClient()
	return &IACEHandler{
		store:       store,
		engine:      iace.NewRiskEngine(),
		classifier:  iace.NewClassifier(),
		checker:     iace.NewCompletenessChecker(),
		ragClient:   ragClient,
		techFileGen: iace.NewTechFileGenerator(providerRegistry, ragClient, store),
		exporter:    iace.NewDocumentExporter(),
		llmRegistry: providerRegistry,
	}
}

// ============================================================================
// Helper: Tenant ID extraction
// ============================================================================

// getTenantID extracts the tenant UUID from the X-Tenant-Id header.
// It first checks the rbac middleware context; if not present, falls back to the
// raw header value.
func getTenantID(c *gin.Context) (uuid.UUID, error) {
	// Prefer value set by RBAC middleware
	tid := rbac.GetTenantID(c)
	if tid != uuid.Nil {
		return tid, nil
	}

	tenantStr := c.GetHeader("X-Tenant-Id")
	if tenantStr == "" {
		return uuid.Nil, fmt.Errorf("X-Tenant-Id header required")
	}
	return uuid.Parse(tenantStr)
}

// ============================================================================
// Internal Helpers
// ============================================================================

// buildCompletenessContext constructs the CompletenessContext needed by the checker
// by loading all related entities for a project.
func (h *IACEHandler) buildCompletenessContext(
	c *gin.Context,
	project *iace.Project,
	components []iace.Component,
	classifications []iace.RegulatoryClassification,
) *iace.CompletenessContext {
	projectID := project.ID

	hazards, _ := h.store.ListHazards(c.Request.Context(), projectID)

	// Batch queries instead of N+1 per hazard (was 462 queries for 231 hazards)
	assessmentMap, _ := h.store.GetLatestAssessmentsByProject(c.Request.Context(), projectID)
	var allAssessments []iace.RiskAssessment
	for _, a := range assessmentMap {
		allAssessments = append(allAssessments, a)
	}
	allMitigations, _ := h.store.ListMitigationsByProject(c.Request.Context(), projectID)

	evidence, _ := h.store.ListEvidence(c.Request.Context(), projectID)
	techFileSections, _ := h.store.ListTechFileSections(c.Request.Context(), projectID)

	hasAI := false
	for _, comp := range components {
		if comp.ComponentType == iace.ComponentTypeAIModel {
			hasAI = true
			break
		}
	}

	return &iace.CompletenessContext{
		Project:          project,
		Components:       components,
		Classifications:  classifications,
		Hazards:          hazards,
		Assessments:      allAssessments,
		Mitigations:      allMitigations,
		Evidence:         evidence,
		TechFileSections: techFileSections,
		HasAI:            hasAI,
	}
}

// containsString checks if a string slice contains the given value.
func containsString(slice []string, val string) bool {
	for _, s := range slice {
		if s == val {
			return true
		}
	}
	return false
}

// componentTypeKeys extracts keys from a map[string]bool and returns them as a sorted slice.
func componentTypeKeys(m map[string]bool) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	// Sort for deterministic output
	sortStrings(keys)
	return keys
}

// sortStrings sorts a slice of strings in place using a simple insertion sort.
func sortStrings(s []string) {
	for i := 1; i < len(s); i++ {
		for j := i; j > 0 && strings.Compare(s[j-1], s[j]) > 0; j-- {
			s[j-1], s[j] = s[j], s[j-1]
		}
	}
}

// mustMarshalJSON marshals the given value to json.RawMessage.
func mustMarshalJSON(v interface{}) json.RawMessage {
	data, err := json.Marshal(v)
	if err != nil {
		return nil
	}
	return data
}
