package handlers

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"testing"

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

func init() {
	gin.SetMode(gin.TestMode)
}

// getProjectRoot returns the project root directory
func getProjectRoot(t *testing.T) string {
	dir, err := os.Getwd()
	if err != nil {
		t.Fatalf("Failed to get working directory: %v", err)
	}

	for {
		if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
			return dir
		}
		parent := filepath.Dir(dir)
		if parent == dir {
			t.Fatalf("Could not find project root (no go.mod found)")
		}
		dir = parent
	}
}

// mockTenantContext sets up a gin context with tenant ID
func mockTenantContext(c *gin.Context, tenantID, userID uuid.UUID) {
	c.Set("tenant_id", tenantID)
	c.Set("user_id", userID)
}

// ============================================================================
// Policy Engine Integration Tests (No DB)
// ============================================================================

func TestUCCAHandlers_ListPatterns(t *testing.T) {
	root := getProjectRoot(t)
	policyPath := filepath.Join(root, "policies", "ucca_policy_v1.yaml")

	engine, err := ucca.NewPolicyEngineFromPath(policyPath)
	if err != nil {
		t.Skipf("Skipping test - could not load policy engine: %v", err)
	}

	handler := &UCCAHandlers{
		policyEngine: engine,
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	handler.ListPatterns(c)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	patterns, ok := response["patterns"].([]interface{})
	if !ok {
		t.Fatal("Expected patterns array in response")
	}

	if len(patterns) == 0 {
		t.Error("Expected at least some patterns")
	}
}

func TestUCCAHandlers_ListControls(t *testing.T) {
	root := getProjectRoot(t)
	policyPath := filepath.Join(root, "policies", "ucca_policy_v1.yaml")

	engine, err := ucca.NewPolicyEngineFromPath(policyPath)
	if err != nil {
		t.Skipf("Skipping test - could not load policy engine: %v", err)
	}

	handler := &UCCAHandlers{
		policyEngine: engine,
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	handler.ListControls(c)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	controls, ok := response["controls"].([]interface{})
	if !ok {
		t.Fatal("Expected controls array in response")
	}

	if len(controls) == 0 {
		t.Error("Expected at least some controls")
	}
}

func TestUCCAHandlers_ListRules(t *testing.T) {
	root := getProjectRoot(t)
	policyPath := filepath.Join(root, "policies", "ucca_policy_v1.yaml")

	engine, err := ucca.NewPolicyEngineFromPath(policyPath)
	if err != nil {
		t.Skipf("Skipping test - could not load policy engine: %v", err)
	}

	handler := &UCCAHandlers{
		policyEngine: engine,
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	handler.ListRules(c)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	rules, ok := response["rules"].([]interface{})
	if !ok {
		t.Fatal("Expected rules array in response")
	}

	if len(rules) == 0 {
		t.Error("Expected at least some rules")
	}

	// Check that policy version is returned
	if _, ok := response["policy_version"]; !ok {
		t.Error("Expected policy_version in response")
	}
}

func TestUCCAHandlers_ListExamples(t *testing.T) {
	handler := &UCCAHandlers{}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	handler.ListExamples(c)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	examples, ok := response["examples"].([]interface{})
	if !ok {
		t.Fatal("Expected examples array in response")
	}

	if len(examples) == 0 {
		t.Error("Expected at least some examples")
	}
}

func TestUCCAHandlers_ListProblemSolutions_WithEngine(t *testing.T) {
	root := getProjectRoot(t)
	policyPath := filepath.Join(root, "policies", "ucca_policy_v1.yaml")

	engine, err := ucca.NewPolicyEngineFromPath(policyPath)
	if err != nil {
		t.Skipf("Skipping test - could not load policy engine: %v", err)
	}

	handler := &UCCAHandlers{
		policyEngine: engine,
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	handler.ListProblemSolutions(c)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	if _, ok := response["problem_solutions"]; !ok {
		t.Error("Expected problem_solutions in response")
	}
}

func TestUCCAHandlers_ListProblemSolutions_WithoutEngine(t *testing.T) {
	handler := &UCCAHandlers{
		policyEngine: nil,
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	handler.ListProblemSolutions(c)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	if _, ok := response["message"]; !ok {
		t.Error("Expected message when policy engine not available")
	}
}

// ============================================================================
// Request Validation Tests
// ============================================================================

func TestUCCAHandlers_Assess_MissingTenantID(t *testing.T) {
	handler := &UCCAHandlers{}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("POST", "/assess", nil)

	// Don't set tenant ID
	handler.Assess(c)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Expected status 400, got %d", w.Code)
	}
}

func TestUCCAHandlers_Assess_InvalidJSON(t *testing.T) {
	root := getProjectRoot(t)
	policyPath := filepath.Join(root, "policies", "ucca_policy_v1.yaml")

	engine, _ := ucca.NewPolicyEngineFromPath(policyPath)

	handler := &UCCAHandlers{
		policyEngine:     engine,
		legacyRuleEngine: ucca.NewRuleEngine(),
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("POST", "/assess", bytes.NewBufferString("invalid json"))
	c.Request.Header.Set("Content-Type", "application/json")
	c.Set("tenant_id", uuid.New())
	c.Set("user_id", uuid.New())

	handler.Assess(c)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Expected status 400 for invalid JSON, got %d", w.Code)
	}
}

func TestUCCAHandlers_GetAssessment_InvalidID(t *testing.T) {
	handler := &UCCAHandlers{}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Params = gin.Params{{Key: "id", Value: "not-a-uuid"}}
	c.Request = httptest.NewRequest("GET", "/assessments/not-a-uuid", nil)

	handler.GetAssessment(c)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Expected status 400 for invalid ID, got %d", w.Code)
	}
}

func TestUCCAHandlers_DeleteAssessment_InvalidID(t *testing.T) {
	handler := &UCCAHandlers{}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Params = gin.Params{{Key: "id", Value: "invalid"}}
	c.Request = httptest.NewRequest("DELETE", "/assessments/invalid", nil)

	handler.DeleteAssessment(c)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Expected status 400 for invalid ID, got %d", w.Code)
	}
}

func TestUCCAHandlers_Export_InvalidID(t *testing.T) {
	handler := &UCCAHandlers{}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Params = gin.Params{{Key: "id", Value: "not-valid"}}
	c.Request = httptest.NewRequest("GET", "/export/not-valid", nil)

	handler.Export(c)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Expected status 400 for invalid ID, got %d", w.Code)
	}
}

func TestUCCAHandlers_Explain_InvalidID(t *testing.T) {
	handler := &UCCAHandlers{}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Params = gin.Params{{Key: "id", Value: "bad-id"}}
	c.Request = httptest.NewRequest("POST", "/assessments/bad-id/explain", nil)

	handler.Explain(c)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Expected status 400 for invalid ID, got %d", w.Code)
	}
}

func TestUCCAHandlers_ListAssessments_MissingTenantID(t *testing.T) {
	handler := &UCCAHandlers{}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/assessments", nil)

	handler.ListAssessments(c)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Expected status 400, got %d", w.Code)
	}
}

func TestUCCAHandlers_GetStats_MissingTenantID(t *testing.T) {
	handler := &UCCAHandlers{}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/stats", nil)

	handler.GetStats(c)

	if w.Code != http.StatusBadRequest {
		t.Errorf("Expected status 400, got %d", w.Code)
	}
}

// ============================================================================
// Markdown Export Generation Tests
// ============================================================================

func TestGenerateMarkdownExport(t *testing.T) {
	assessment := &ucca.Assessment{
		ID:          uuid.New(),
		Title:       "Test Assessment",
		Domain:      ucca.DomainEducation,
		Feasibility: ucca.FeasibilityCONDITIONAL,
		RiskLevel:   ucca.RiskLevelMEDIUM,
		RiskScore:   45,
		Complexity:  ucca.ComplexityMEDIUM,
		TriggeredRules: []ucca.TriggeredRule{
			{Code: "R-A001", Title: "Test Rule", Severity: "WARN", ScoreDelta: 10},
		},
		RequiredControls: []ucca.RequiredControl{
			{ID: "C-001", Title: "Test Control", Description: "Test Description"},
		},
		DSFARecommended:  true,
		Art22Risk:        false,
		TrainingAllowed:  ucca.TrainingCONDITIONAL,
		PolicyVersion:    "1.0.0",
	}

	markdown := generateMarkdownExport(assessment)

	// Check for expected content
	if markdown == "" {
		t.Error("Expected non-empty markdown")
	}

	expectedContents := []string{
		"# UCCA Use-Case Assessment",
		"CONDITIONAL",
		"MEDIUM",
		"45/100",
		"Test Rule",
		"Test Control",
		"DSFA",
		"1.0.0",
	}

	for _, expected := range expectedContents {
		if !bytes.Contains([]byte(markdown), []byte(expected)) {
			t.Errorf("Expected markdown to contain '%s'", expected)
		}
	}
}

func TestGenerateMarkdownExport_WithExplanation(t *testing.T) {
	explanation := "Dies ist eine KI-generierte Erklärung."
	assessment := &ucca.Assessment{
		ID:              uuid.New(),
		Feasibility:     ucca.FeasibilityYES,
		RiskLevel:       ucca.RiskLevelMINIMAL,
		RiskScore:       10,
		ExplanationText: &explanation,
		PolicyVersion:   "1.0.0",
	}

	markdown := generateMarkdownExport(assessment)

	if !bytes.Contains([]byte(markdown), []byte("KI-Erklärung")) {
		t.Error("Expected markdown to contain explanation section")
	}

	if !bytes.Contains([]byte(markdown), []byte(explanation)) {
		t.Error("Expected markdown to contain the explanation text")
	}
}

func TestGenerateMarkdownExport_WithForbiddenPatterns(t *testing.T) {
	assessment := &ucca.Assessment{
		ID:          uuid.New(),
		Feasibility: ucca.FeasibilityNO,
		RiskLevel:   ucca.RiskLevelHIGH,
		RiskScore:   85,
		ForbiddenPatterns: []ucca.ForbiddenPattern{
			{PatternID: "FP-001", Title: "Forbidden Pattern", Reason: "Not allowed"},
		},
		PolicyVersion: "1.0.0",
	}

	markdown := generateMarkdownExport(assessment)

	if !bytes.Contains([]byte(markdown), []byte("Verbotene Patterns")) {
		t.Error("Expected markdown to contain forbidden patterns section")
	}

	if !bytes.Contains([]byte(markdown), []byte("Not allowed")) {
		t.Error("Expected markdown to contain forbidden pattern reason")
	}
}

// ============================================================================
// Explanation Prompt Building Tests
// ============================================================================

func TestBuildExplanationPrompt(t *testing.T) {
	assessment := &ucca.Assessment{
		Feasibility: ucca.FeasibilityCONDITIONAL,
		RiskLevel:   ucca.RiskLevelMEDIUM,
		RiskScore:   50,
		Complexity:  ucca.ComplexityMEDIUM,
		TriggeredRules: []ucca.TriggeredRule{
			{Code: "R-001", Title: "Test", Severity: "WARN"},
		},
		RequiredControls: []ucca.RequiredControl{
			{Title: "Control", Description: "Desc"},
		},
		DSFARecommended: true,
		Art22Risk:       true,
	}

	prompt := buildExplanationPrompt(assessment, "de", "")

	// Check prompt contains expected elements
	expectedElements := []string{
		"CONDITIONAL",
		"MEDIUM",
		"50/100",
		"Ausgelöste Regeln",
		"Erforderliche Maßnahmen",
		"DSFA",
		"Art. 22",
	}

	for _, expected := range expectedElements {
		if !bytes.Contains([]byte(prompt), []byte(expected)) {
			t.Errorf("Expected prompt to contain '%s'", expected)
		}
	}
}

func TestBuildExplanationPrompt_WithLegalContext(t *testing.T) {
	assessment := &ucca.Assessment{
		Feasibility: ucca.FeasibilityYES,
		RiskLevel:   ucca.RiskLevelLOW,
		RiskScore:   15,
		Complexity:  ucca.ComplexityLOW,
	}

	legalContext := "**Relevante Rechtsgrundlagen:**\nArt. 6 DSGVO - Rechtmäßigkeit"

	prompt := buildExplanationPrompt(assessment, "de", legalContext)

	if !bytes.Contains([]byte(prompt), []byte("Relevante Rechtsgrundlagen")) {
		t.Error("Expected prompt to contain legal context")
	}
}

// ============================================================================
// Legacy Rule Engine Fallback Tests
// ============================================================================

func TestUCCAHandlers_ListRules_LegacyFallback(t *testing.T) {
	handler := &UCCAHandlers{
		policyEngine:     nil, // No YAML engine
		legacyRuleEngine: ucca.NewRuleEngine(),
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	handler.ListRules(c)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	// Should have legacy policy version
	policyVersion, ok := response["policy_version"].(string)
	if !ok {
		t.Fatal("Expected policy_version string")
	}

	if policyVersion != "1.0.0-legacy" {
		t.Errorf("Expected legacy policy version, got %s", policyVersion)
	}
}

func TestUCCAHandlers_ListPatterns_LegacyFallback(t *testing.T) {
	handler := &UCCAHandlers{
		policyEngine: nil, // No YAML engine
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	handler.ListPatterns(c)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	patterns, ok := response["patterns"].([]interface{})
	if !ok {
		t.Fatal("Expected patterns array in response")
	}

	// Legacy patterns should still be returned
	if len(patterns) == 0 {
		t.Error("Expected at least some legacy patterns")
	}
}

func TestUCCAHandlers_ListControls_LegacyFallback(t *testing.T) {
	handler := &UCCAHandlers{
		policyEngine: nil, // No YAML engine
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	handler.ListControls(c)

	if w.Code != http.StatusOK {
		t.Errorf("Expected status 200, got %d", w.Code)
	}

	var response map[string]interface{}
	if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	controls, ok := response["controls"].([]interface{})
	if !ok {
		t.Fatal("Expected controls array in response")
	}

	// Legacy controls should still be returned
	if len(controls) == 0 {
		t.Error("Expected at least some legacy controls")
	}
}
