package maximizer

import (
	"path/filepath"
	"runtime"
	"testing"
)

func loadTestRules(t *testing.T) *ConstraintRuleSet {
	t.Helper()
	_, filename, _, ok := runtime.Caller(0)
	if !ok {
		t.Fatal("cannot determine test file location")
	}
	// Walk up from internal/maximizer/ to ai-compliance-sdk/
	dir := filepath.Dir(filename) // internal/maximizer
	dir = filepath.Dir(dir)       // internal
	dir = filepath.Dir(dir)       // ai-compliance-sdk
	path := filepath.Join(dir, "policies", "maximizer_constraints_v1.json")
	rules, err := LoadConstraintRules(path)
	if err != nil {
		t.Fatalf("LoadConstraintRules: %v", err)
	}
	return rules
}

func TestLoadConstraintRules(t *testing.T) {
	rules := loadTestRules(t)
	if rules.Version != "1.0.0" {
		t.Errorf("expected version 1.0.0, got %s", rules.Version)
	}
	if len(rules.Rules) < 20 {
		t.Errorf("expected at least 20 rules, got %d", len(rules.Rules))
	}
}

func TestEvalCompliantConfig(t *testing.T) {
	rules := loadTestRules(t)
	eval := NewEvaluator(rules)

	config := &DimensionConfig{
		AutomationLevel:      AutoAssistive,
		DecisionBinding:      BindingHumanReview,
		DecisionImpact:       ImpactLow,
		Domain:               DomainGeneral,
		DataType:             DataNonPersonal,
		HumanInLoop:          HILRequired,
		Explainability:       ExplainBasic,
		RiskClassification:   RiskMinimal,
		LegalBasis:           LegalContract,
		TransparencyRequired: false,
		LoggingRequired:      false,
		ModelType:            ModelRuleBased,
		DeploymentScope:      ScopeInternal,
	}

	result := eval.Evaluate(config)
	if !result.IsCompliant {
		t.Errorf("expected compliant, got violations: %+v", result.Violations)
	}
	// All dimensions should be SAFE
	for dim, zi := range result.ZoneMap {
		if zi.Zone != ZoneSafe {
			t.Errorf("dimension %s: expected SAFE, got %s", dim, zi.Zone)
		}
	}
}

func TestEvalHRFullAutomationBlocked(t *testing.T) {
	rules := loadTestRules(t)
	eval := NewEvaluator(rules)

	config := &DimensionConfig{
		AutomationLevel:      AutoFull,
		DecisionBinding:      BindingFullyBinding,
		DecisionImpact:       ImpactHigh,
		Domain:               DomainHR,
		DataType:             DataPersonal,
		HumanInLoop:          HILNone,
		Explainability:       ExplainNone,
		RiskClassification:   RiskMinimal,
		LegalBasis:           LegalContract,
		TransparencyRequired: false,
		LoggingRequired:      false,
		ModelType:            ModelBlackboxLLM,
		DeploymentScope:      ScopeExternal,
	}

	result := eval.Evaluate(config)
	if result.IsCompliant {
		t.Error("expected non-compliant for HR full automation")
	}
	if len(result.Violations) == 0 {
		t.Error("expected at least one violation")
	}

	// automation_level should be FORBIDDEN
	zi := result.ZoneMap["automation_level"]
	if zi.Zone != ZoneForbidden {
		t.Errorf("automation_level: expected FORBIDDEN, got %s", zi.Zone)
	}
}

func TestEvalProhibitedClassification(t *testing.T) {
	rules := loadTestRules(t)
	eval := NewEvaluator(rules)

	config := &DimensionConfig{
		RiskClassification: RiskProhibited,
		DeploymentScope:    ScopePublic,
	}

	result := eval.Evaluate(config)
	if result.IsCompliant {
		t.Error("expected non-compliant for prohibited classification")
	}

	found := false
	for _, v := range result.Violations {
		if v.RuleID == "MC-AIA-PROHIBITED-001" {
			found = true
		}
	}
	if !found {
		t.Error("expected MC-AIA-PROHIBITED-001 violation")
	}
}

func TestEvalSensitiveDataRequiresConsent(t *testing.T) {
	rules := loadTestRules(t)
	eval := NewEvaluator(rules)

	config := &DimensionConfig{
		DataType:   DataSensitive,
		LegalBasis: LegalLegitimateInterest, // wrong basis for sensitive
	}

	result := eval.Evaluate(config)
	if result.IsCompliant {
		t.Error("expected non-compliant: sensitive data without consent")
	}

	// Should require consent
	found := false
	for _, r := range result.Restrictions {
		if val, ok := r.Required["legal_basis"]; ok && val == "consent" {
			found = true
		}
	}
	if !found {
		t.Error("expected restriction requiring legal_basis=consent")
	}
}

func TestEvalHighRiskRequiresLogging(t *testing.T) {
	rules := loadTestRules(t)
	eval := NewEvaluator(rules)

	config := &DimensionConfig{
		RiskClassification:   RiskHigh,
		LoggingRequired:      false,
		TransparencyRequired: false,
		HumanInLoop:          HILNone,
		Explainability:       ExplainNone,
	}

	result := eval.Evaluate(config)
	if result.IsCompliant {
		t.Error("expected non-compliant: high risk without logging/transparency/hil")
	}

	// Check logging_required is RESTRICTED
	zi := result.ZoneMap["logging_required"]
	if zi.Zone != ZoneRestricted {
		t.Errorf("logging_required: expected RESTRICTED, got %s", zi.Zone)
	}
}

func TestEvalTriggeredRulesHaveObligationRefs(t *testing.T) {
	rules := loadTestRules(t)
	eval := NewEvaluator(rules)

	config := &DimensionConfig{
		AutomationLevel: AutoFull,
		DecisionImpact:  ImpactHigh,
		Domain:          DomainHR,
		DataType:        DataPersonal,
	}

	result := eval.Evaluate(config)
	for _, tr := range result.TriggeredRules {
		if tr.RuleID == "" {
			t.Error("triggered rule missing RuleID")
		}
		if tr.ObligationID == "" {
			t.Error("triggered rule missing ObligationID")
		}
		if tr.ArticleRef == "" {
			t.Error("triggered rule missing ArticleRef")
		}
	}
}

func TestConditionSetMatchesExact(t *testing.T) {
	config := &DimensionConfig{
		Domain:         DomainHR,
		DecisionImpact: ImpactHigh,
	}

	tests := []struct {
		name    string
		cond    ConditionSet
		matches bool
	}{
		{"exact match", ConditionSet{"domain": "hr", "decision_impact": "high"}, true},
		{"partial match fails", ConditionSet{"domain": "hr", "decision_impact": "low"}, false},
		{"unknown value", ConditionSet{"domain": "finance"}, false},
		{"empty condition", ConditionSet{}, true},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			got := tc.cond.Matches(config)
			if got != tc.matches {
				t.Errorf("expected %v, got %v", tc.matches, got)
			}
		})
	}
}
