package ucca

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

func TestEvidenceRequirement_Validate(t *testing.T) {
	valid := EvidenceRequirement{Framework: "OWASP ASVS", Control: "V6.3.1", EvidenceType: "config_export", EvidenceSource: "github", FreshnessRequirement: "per_release", Required: true}
	if err := valid.Validate(); err != nil {
		t.Fatalf("valid rejected: %v", err)
	}
	bad := []struct {
		name string
		e    EvidenceRequirement
	}{
		{"no control", EvidenceRequirement{Framework: "X", EvidenceType: "sbom", EvidenceSource: "ci", FreshnessRequirement: "per_release"}},
		{"bad evidence_type", EvidenceRequirement{Framework: "X", Control: "Y", EvidenceType: "screenshot", EvidenceSource: "ci", FreshnessRequirement: "per_release"}},
		{"bad evidence_source", EvidenceRequirement{Framework: "X", Control: "Y", EvidenceType: "sbom", EvidenceSource: "email", FreshnessRequirement: "per_release"}},
		{"bad freshness", EvidenceRequirement{Framework: "X", Control: "Y", EvidenceType: "sbom", EvidenceSource: "ci", FreshnessRequirement: "weekly"}},
	}
	for _, tt := range bad {
		if err := tt.e.Validate(); err == nil {
			t.Errorf("%s: expected rejection", tt.name)
		}
	}
}

func TestLoadEvidenceRequirements(t *testing.T) {
	dir := t.TempDir()
	content := `// header
{"framework":"OWASP ASVS","control":"V6.3.1","evidence_type":"config_export","evidence_source":"github","freshness_requirement":"per_release","required":true,"version":"2026-06-25"}
{"framework":"OWASP ASVS","control":"V6.3.1","evidence_type":"pentest","evidence_source":"manual_upload","freshness_requirement":"annually","required":false,"version":"2026-06-25"}
`
	if err := os.WriteFile(filepath.Join(dir, "e.jsonl"), []byte(content), 0o644); err != nil {
		t.Fatal(err)
	}
	set, err := LoadEvidenceRequirements(dir)
	if err != nil {
		t.Fatalf("load: %v", err)
	}
	if len(set.All) != 2 {
		t.Fatalf("want 2, got %d", len(set.All))
	}
	if got := set.For("OWASP ASVS", "V6.3.1"); len(got) != 2 {
		t.Errorf("For: want 2, got %d", len(got))
	}
	if got := set.RequiredFor("OWASP ASVS", "V6.3.1"); len(got) != 1 {
		t.Errorf("RequiredFor: want 1 (pentest is optional), got %d", len(got))
	}
}

func TestEvidenceRequirements_SeedFileValid(t *testing.T) {
	set, err := LoadEvidenceRequirements("../../data/evidence_requirements")
	if err != nil {
		t.Fatalf("seed evidence_requirements failed to load: %v", err)
	}
	if len(set.All) == 0 {
		t.Fatal("seed evidence_requirements is empty")
	}
}

// TestGraph_AcceptedControlsHaveEvidence wires the two layers: every control an accepted
// CRA->OWASP mapping points to must have >=1 required evidence — the Obligation -> Control ->
// Evidence chain must be connected, no dangling control nodes.
func TestGraph_AcceptedControlsHaveEvidence(t *testing.T) {
	maps, err := LoadControlMappings("../../data/control_mappings")
	if err != nil {
		t.Fatal(err)
	}
	ev, err := LoadEvidenceRequirements("../../data/evidence_requirements")
	if err != nil {
		t.Fatal(err)
	}
	for _, m := range maps.All {
		if !m.IsAccepted() {
			continue
		}
		if len(ev.RequiredFor(m.TargetFramework, m.TargetControl)) == 0 {
			t.Errorf("accepted control %s %s has no required evidence (dangling graph node)", m.TargetFramework, m.TargetControl)
		}
	}
}
