package cli

import (
	"bytes"
	"encoding/json"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"

	"github.com/Clyra-AI/wrkr/core/ingest"
	reportcore "github.com/Clyra-AI/wrkr/core/report"
)

func TestReportPDFDeterministicForFixedState(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	pdfPath := filepath.Join(tmp, "report.pdf")
	snapshot := map[string]any{
		"version": "v1",
		"inventory": map[string]any{
			"tools": []any{
				map[string]any{"tool_type": "cursor"},
				map[string]any{"tool_type": "mcp"},
				map[string]any{"tool_type": "mcp"},
			},
		},
		"profile": map[string]any{
			"failing_rules": []any{"WRKR-001", "WRKR-002"},
		},
		"risk_report": map[string]any{
			"generated_at": "2026-02-20T12:00:00Z",
			"top_findings": []any{
				map[string]any{
					"risk_score": 9.1,
					"finding": map[string]any{
						"finding_type": "policy_violation",
						"location":     "WRKR-001",
					},
				},
			},
			"ranked_findings": []any{
				map[string]any{
					"risk_score": 9.1,
					"finding": map[string]any{
						"finding_type": "policy_violation",
						"location":     "WRKR-001",
					},
				},
			},
		},
	}
	payload, err := json.Marshal(snapshot)
	if err != nil {
		t.Fatalf("marshal state payload: %v", err)
	}
	if err := os.WriteFile(statePath, append(payload, '\n'), 0o600); err != nil {
		t.Fatalf("write state payload: %v", err)
	}

	var out bytes.Buffer
	var errOut bytes.Buffer
	if code := Run([]string{"report", "--pdf", "--pdf-path", pdfPath, "--state", statePath, "--json"}, &out, &errOut); code != 0 {
		t.Fatalf("report --pdf failed: %d %s", code, errOut.String())
	}
	first, err := os.ReadFile(pdfPath)
	if err != nil {
		t.Fatalf("read first pdf: %v", err)
	}

	out.Reset()
	errOut.Reset()
	if code := Run([]string{"report", "--pdf", "--pdf-path", pdfPath, "--state", statePath, "--json"}, &out, &errOut); code != 0 {
		t.Fatalf("report --pdf second run failed: %d %s", code, errOut.String())
	}
	second, err := os.ReadFile(pdfPath)
	if err != nil {
		t.Fatalf("read second pdf: %v", err)
	}
	if string(first) != string(second) {
		t.Fatal("expected deterministic report pdf bytes for fixed state")
	}
}

func TestReportCustomerTemplatesEmitArtifactBundlePaths(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state with spaces.json")
	writeJSONFile(t, statePath, map[string]any{
		"version": "v1",
		"control_backlog": map[string]any{
			"control_backlog_version": "1",
			"summary":                 map[string]any{"total_items": 1},
			"items": []any{map[string]any{
				"id":                   "cb-1",
				"repo":                 "payments",
				"path":                 ".github/workflows/release.yml",
				"control_surface_type": "ci_automation",
				"control_path_type":    "ci_automation",
				"capability":           "repo_write",
				"owner":                "@acme/payments",
				"evidence_source":      "risk_action_path",
				"evidence_basis":       []any{"workflow_permission"},
				"approval_status":      "unapproved",
				"security_visibility":  "unknown_to_security",
				"signal_class":         "unique_wrkr_signal",
				"recommended_action":   "approve",
				"confidence":           "medium",
				"sla":                  "7d",
				"closure_criteria":     "Record owner-approved evidence and rescan.",
			}},
		},
	})
	mdPath := filepath.Join(tmp, "report.md")
	pdfPath := filepath.Join(tmp, "report.pdf")
	bundlePath := filepath.Join(tmp, "evidence.json")
	csvPath := filepath.Join(tmp, "backlog.csv")
	var out bytes.Buffer
	var errOut bytes.Buffer
	code := Run([]string{
		"report",
		"--state", statePath,
		"--template", "ciso",
		"--md", "--md-path", mdPath,
		"--pdf", "--pdf-path", pdfPath,
		"--evidence-json", "--evidence-json-path", bundlePath,
		"--csv-backlog", "--csv-backlog-path", csvPath,
		"--json",
	}, &out, &errOut)
	if code != 0 {
		t.Fatalf("report failed: %d %s", code, errOut.String())
	}
	var payload map[string]any
	if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
		t.Fatalf("parse report payload: %v", err)
	}
	artifactPaths, ok := payload["artifact_paths"].(map[string]any)
	if !ok {
		t.Fatalf("expected artifact_paths, got %v", payload)
	}
	for key, path := range map[string]string{"markdown": mdPath, "pdf": pdfPath, "evidence_json": bundlePath, "backlog_csv": csvPath} {
		if artifactPaths[key] != path {
			t.Fatalf("expected artifact %s=%s, got %v", key, path, artifactPaths)
		}
		if _, err := os.Stat(path); err != nil {
			t.Fatalf("expected artifact %s: %v", path, err)
		}
	}
	csvPayload, err := os.ReadFile(csvPath)
	if err != nil {
		t.Fatalf("read csv: %v", err)
	}
	if !strings.Contains(string(csvPayload), "closure_criteria") || !strings.Contains(string(csvPayload), "7d") {
		t.Fatalf("expected backlog csv fields, got %q", string(csvPayload))
	}
}

func TestReportJSONIncludesDeterministicNextSteps(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	writeJSONFile(t, statePath, map[string]any{
		"version": "v1",
		"inventory": map[string]any{
			"tools": []any{map[string]any{"tool_type": "cursor"}},
		},
		"risk_report": map[string]any{
			"generated_at":    "2026-04-23T12:00:00Z",
			"top_findings":    []any{},
			"ranked_findings": []any{},
		},
	})
	mdPath := filepath.Join(tmp, "report.md")
	pdfPath := filepath.Join(tmp, "report.pdf")
	bundlePath := filepath.Join(tmp, "evidence.json")
	csvPath := filepath.Join(tmp, "backlog.csv")

	var out bytes.Buffer
	var errOut bytes.Buffer
	code := Run([]string{
		"report",
		"--state", statePath,
		"--template", "audit",
		"--md", "--md-path", mdPath,
		"--pdf", "--pdf-path", pdfPath,
		"--evidence-json", "--evidence-json-path", bundlePath,
		"--csv-backlog", "--csv-backlog-path", csvPath,
		"--json",
	}, &out, &errOut)
	if code != 0 {
		t.Fatalf("report failed: %d %s", code, errOut.String())
	}

	var payload map[string]any
	if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
		t.Fatalf("parse report payload: %v", err)
	}
	nextSteps, ok := payload["next_steps"].([]any)
	if !ok || len(nextSteps) != 3 {
		t.Fatalf("expected three next steps, got %v", payload["next_steps"])
	}

	first, ok := nextSteps[0].(map[string]any)
	if !ok {
		t.Fatalf("unexpected first next step type: %T", nextSteps[0])
	}
	if first["id"] != "review_report_artifacts" {
		t.Fatalf("unexpected first next step: %v", first)
	}
	artifacts, ok := first["artifacts"].([]any)
	if !ok || len(artifacts) != 4 {
		t.Fatalf("expected report artifact references, got %v", first["artifacts"])
	}
	expectedRefs := []string{
		"backlog_csv_path",
		"evidence_json_path",
		"md_path",
		"pdf_path",
	}
	for idx, expected := range expectedRefs {
		if artifacts[idx] != expected {
			t.Fatalf("expected artifact reference %q at %d, got %v", expected, idx, artifacts[idx])
		}
	}

	second, ok := nextSteps[1].(map[string]any)
	if !ok {
		t.Fatalf("unexpected second next step type: %T", nextSteps[1])
	}
	quotedStatePath := "'" + statePath + "'"
	expectedEvidenceCmd := "wrkr evidence --frameworks eu-ai-act,soc2,pci-dss --state " + quotedStatePath + " --output ./wrkr-evidence --json"
	if second["command"] != expectedEvidenceCmd {
		t.Fatalf("expected evidence handoff command %q, got %v", expectedEvidenceCmd, second["command"])
	}

	third, ok := nextSteps[2].(map[string]any)
	if !ok {
		t.Fatalf("unexpected third next step type: %T", nextSteps[2])
	}
	expectedVerifyCmd := "wrkr verify --chain --state " + quotedStatePath + " --json"
	if third["command"] != expectedVerifyCmd {
		t.Fatalf("expected verify handoff command %q, got %v", expectedVerifyCmd, third["command"])
	}
}

func TestReportJSONIncludesTopLevelRuntimeEvidenceWhenSidecarPresent(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	writeJSONFile(t, statePath, map[string]any{
		"version": "v1",
		"inventory": map[string]any{
			"tools": []any{map[string]any{"tool_type": "ci_agent"}},
		},
		"risk_report": map[string]any{
			"generated_at":    "2026-04-30T12:00:00Z",
			"top_findings":    []any{},
			"ranked_findings": []any{},
			"action_paths": []any{
				map[string]any{
					"path_id":            "apc-runtime-123",
					"agent_id":           "wrkr:ci_agent:acme",
					"org":                "acme",
					"repo":               "demo-app",
					"tool_type":          "ci_agent",
					"location":           ".github/workflows/release.yml",
					"recommended_action": "proof",
					"credential_access":  true,
				},
			},
		},
	})
	if err := ingest.Save(ingest.DefaultPath(statePath), ingest.Bundle{
		SchemaVersion: ingest.SchemaVersion,
		GeneratedAt:   "2026-04-30T12:05:00Z",
		Records: []ingest.Record{{
			RecordID:      "rec-runtime-1",
			PathID:        "apc-runtime-123",
			AgentID:       "wrkr:ci_agent:acme",
			Tool:          "ci_agent",
			Repo:          "demo-app",
			Location:      ".github/workflows/release.yml",
			Source:        "runtime_probe",
			ObservedAt:    "2026-04-30T12:04:00Z",
			EvidenceClass: ingest.EvidenceClassPolicyDecision,
		}},
	}); err != nil {
		t.Fatalf("save runtime evidence sidecar: %v", err)
	}

	for _, template := range []string{"operator", "agent-action-bom"} {
		t.Run(template, func(t *testing.T) {
			var out bytes.Buffer
			var errOut bytes.Buffer
			if code := Run([]string{"report", "--state", statePath, "--template", template, "--json"}, &out, &errOut); code != 0 {
				t.Fatalf("report failed: %d %s", code, errOut.String())
			}

			var payload map[string]any
			if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
				t.Fatalf("parse report payload: %v", err)
			}
			topLevel, ok := payload["runtime_evidence"].(map[string]any)
			if !ok {
				t.Fatalf("expected top-level runtime_evidence, got %v", payload["runtime_evidence"])
			}
			summary, ok := payload["summary"].(map[string]any)
			if !ok {
				t.Fatalf("expected summary object, got %T", payload["summary"])
			}
			nested, ok := summary["runtime_evidence"].(map[string]any)
			if !ok {
				t.Fatalf("expected summary runtime_evidence, got %v", summary["runtime_evidence"])
			}
			if !reflect.DeepEqual(topLevel, nested) {
				t.Fatalf("expected top-level and summary runtime_evidence to match\ntop=%v\nsummary=%v", topLevel, nested)
			}
			if topLevel["matched_records"] != float64(1) {
				t.Fatalf("expected one matched runtime evidence record, got %v", topLevel["matched_records"])
			}
		})
	}
}

func TestReportJSONOmitsTopLevelRuntimeEvidenceWhenUnavailable(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	writeJSONFile(t, statePath, map[string]any{
		"version": "v1",
		"inventory": map[string]any{
			"tools": []any{map[string]any{"tool_type": "ci_agent"}},
		},
		"risk_report": map[string]any{
			"generated_at":    "2026-04-30T12:00:00Z",
			"top_findings":    []any{},
			"ranked_findings": []any{},
			"action_paths": []any{
				map[string]any{
					"path_id":            "apc-runtime-123",
					"agent_id":           "wrkr:ci_agent:acme",
					"org":                "acme",
					"repo":               "demo-app",
					"tool_type":          "ci_agent",
					"location":           ".github/workflows/release.yml",
					"recommended_action": "proof",
					"credential_access":  true,
				},
			},
		},
	})

	for _, template := range []string{"operator", "agent-action-bom"} {
		t.Run(template, func(t *testing.T) {
			var out bytes.Buffer
			var errOut bytes.Buffer
			if code := Run([]string{"report", "--state", statePath, "--template", template, "--json"}, &out, &errOut); code != 0 {
				t.Fatalf("report failed: %d %s", code, errOut.String())
			}

			var payload map[string]any
			if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
				t.Fatalf("parse report payload: %v", err)
			}
			if _, ok := payload["runtime_evidence"]; ok {
				t.Fatalf("expected top-level runtime_evidence to be omitted, got %v", payload["runtime_evidence"])
			}
			summary, ok := payload["summary"].(map[string]any)
			if !ok {
				t.Fatalf("expected summary object, got %T", payload["summary"])
			}
			if _, ok := summary["runtime_evidence"]; ok {
				t.Fatalf("expected summary runtime_evidence to be omitted, got %v", summary["runtime_evidence"])
			}
		})
	}
}

func TestReportJSONArtifactReferencesResolveToPayloadFields(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	writeJSONFile(t, statePath, map[string]any{
		"version": "v1",
		"inventory": map[string]any{
			"tools": []any{map[string]any{"tool_type": "cursor"}},
		},
		"risk_report": map[string]any{
			"generated_at":    "2026-04-23T12:00:00Z",
			"top_findings":    []any{},
			"ranked_findings": []any{},
		},
	})
	mdPath := filepath.Join(tmp, "report.md")
	pdfPath := filepath.Join(tmp, "report.pdf")

	var out bytes.Buffer
	var errOut bytes.Buffer
	code := Run([]string{
		"report",
		"--state", statePath,
		"--md", "--md-path", mdPath,
		"--pdf", "--pdf-path", pdfPath,
		"--json",
	}, &out, &errOut)
	if code != 0 {
		t.Fatalf("report failed: %d %s", code, errOut.String())
	}

	var payload map[string]any
	if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
		t.Fatalf("parse report payload: %v", err)
	}
	if payload["md_path"] != mdPath {
		t.Fatalf("expected md_path %q, got %v", mdPath, payload["md_path"])
	}
	if payload["pdf_path"] != pdfPath {
		t.Fatalf("expected pdf_path %q, got %v", pdfPath, payload["pdf_path"])
	}
	if _, ok := payload["artifact_paths"]; ok {
		t.Fatalf("did not expect artifact_paths for md/pdf-only operator report: %v", payload["artifact_paths"])
	}

	nextSteps, ok := payload["next_steps"].([]any)
	if !ok || len(nextSteps) == 0 {
		t.Fatalf("expected next steps, got %v", payload["next_steps"])
	}
	first, ok := nextSteps[0].(map[string]any)
	if !ok {
		t.Fatalf("unexpected first next step type: %T", nextSteps[0])
	}
	artifacts, ok := first["artifacts"].([]any)
	if !ok {
		t.Fatalf("expected next-step artifact references, got %v", first["artifacts"])
	}
	for _, ref := range artifacts {
		refString, ok := ref.(string)
		if !ok {
			t.Fatalf("expected string next-step reference, got %v", ref)
		}
		if _, ok := payload[refString]; !ok {
			t.Fatalf("next-step reference %q did not resolve in payload %v", refString, payload)
		}
	}
}

func writeJSONFile(t *testing.T, path string, payload any) {
	t.Helper()
	encoded, err := json.Marshal(payload)
	if err != nil {
		t.Fatalf("marshal json: %v", err)
	}
	if err := os.WriteFile(path, append(encoded, '\n'), 0o600); err != nil {
		t.Fatalf("write json: %v", err)
	}
}

func TestReportHelpMatchesBehaviorContract(t *testing.T) {
	t.Parallel()

	var out bytes.Buffer
	var errOut bytes.Buffer
	code := Run([]string{"report", "--help"}, &out, &errOut)
	if code != 0 {
		t.Fatalf("expected exit 0, got %d stderr=%q", code, errOut.String())
	}

	helpText := errOut.String()
	for _, sentence := range []string{reportBehaviorContractSentenceOne, reportBehaviorContractSentenceTwo} {
		if !strings.Contains(helpText, sentence) {
			t.Fatalf("report help missing behavior contract sentence %q", sentence)
		}
	}

	repoRoot := mustRepoRoot(t)
	docsText, err := os.ReadFile(filepath.Join(repoRoot, "docs/commands/report.md"))
	if err != nil {
		t.Fatalf("read report docs: %v", err)
	}
	for _, sentence := range []string{reportBehaviorContractSentenceOne, reportBehaviorContractSentenceTwo} {
		if !strings.Contains(string(docsText), sentence) {
			t.Fatalf("docs/commands/report.md missing behavior contract sentence %q", sentence)
		}
	}
}

func TestRenderSimplePDFWrapsAndPaginatesLongContent(t *testing.T) {
	t.Parallel()

	lines := []string{
		strings.Repeat("long-line-segment ", 16) + "tail-marker",
	}
	for i := 0; i < 80; i++ {
		lines = append(lines, "section line "+strings.Repeat("x", 24))
	}

	payload := string(renderSimplePDF(lines))
	if !strings.Contains(payload, "/Count 2") {
		t.Fatalf("expected multi-page PDF payload, got %q", payload)
	}
	if !strings.Contains(payload, "tail-marker") {
		t.Fatalf("expected wrapped content to preserve tail marker, got %q", payload)
	}
}

func TestReportMySetupSummaryIncludesActivationProjection(t *testing.T) {
	tmpHome := t.TempDir()
	t.Setenv("HOME", tmpHome)
	t.Setenv("USERPROFILE", tmpHome)
	t.Setenv("OPENAI_API_KEY", "redacted")

	if err := os.MkdirAll(filepath.Join(tmpHome, ".codex"), 0o755); err != nil {
		t.Fatalf("mkdir codex: %v", err)
	}
	if err := os.WriteFile(filepath.Join(tmpHome, ".codex", "config.toml"), []byte("model = \"gpt-5\"\n"), 0o600); err != nil {
		t.Fatalf("write codex config: %v", err)
	}

	statePath := filepath.Join(t.TempDir(), "state.json")
	var scanOut bytes.Buffer
	var scanErr bytes.Buffer
	if code := Run([]string{"scan", "--my-setup", "--state", statePath, "--json"}, &scanOut, &scanErr); code != 0 {
		t.Fatalf("scan failed: %d %s", code, scanErr.String())
	}

	var reportOut bytes.Buffer
	var reportErr bytes.Buffer
	if code := Run([]string{"report", "--state", statePath, "--json"}, &reportOut, &reportErr); code != 0 {
		t.Fatalf("report failed: %d %s", code, reportErr.String())
	}

	var payload map[string]any
	if err := json.Unmarshal(reportOut.Bytes(), &payload); err != nil {
		t.Fatalf("parse report payload: %v", err)
	}
	summary, ok := payload["summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary object, got %T", payload["summary"])
	}
	activation, ok := summary["activation"].(map[string]any)
	if !ok {
		t.Fatalf("expected activation summary, got %v", summary["activation"])
	}
	items, ok := activation["items"].([]any)
	if !ok || len(items) == 0 {
		t.Fatalf("expected activation items in report summary, got %v", activation["items"])
	}
	for _, item := range items {
		row, ok := item.(map[string]any)
		if !ok {
			continue
		}
		if row["tool_type"] == "policy" {
			t.Fatalf("policy findings must not appear in report activation items: %v", items)
		}
	}
}

func TestReportOrgSummaryIncludesGovernFirstActivationProjection(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	snapshot := map[string]any{
		"version": "v1",
		"target": map[string]any{
			"mode":  "org",
			"value": "acme",
		},
		"inventory": map[string]any{
			"agent_privilege_map": []any{
				map[string]any{
					"agent_id":                   "wrkr:alpha:acme",
					"framework":                  "langchain",
					"repos":                      []any{"payments"},
					"location":                   "agents/payments.py",
					"risk_score":                 8.8,
					"write_capable":              true,
					"production_write":           true,
					"approval_classification":    "approved",
					"security_visibility_status": "approved",
				},
			},
		},
		"risk_report": map[string]any{
			"generated_at":    "2026-03-25T12:00:00Z",
			"top_findings":    []any{},
			"ranked_findings": []any{},
		},
	}
	payload, err := json.Marshal(snapshot)
	if err != nil {
		t.Fatalf("marshal state payload: %v", err)
	}
	if err := os.WriteFile(statePath, append(payload, '\n'), 0o600); err != nil {
		t.Fatalf("write state payload: %v", err)
	}

	var out bytes.Buffer
	var errOut bytes.Buffer
	if code := Run([]string{"report", "--state", statePath, "--json"}, &out, &errOut); code != 0 {
		t.Fatalf("report failed: %d %s", code, errOut.String())
	}

	var reportPayload map[string]any
	if err := json.Unmarshal(out.Bytes(), &reportPayload); err != nil {
		t.Fatalf("parse report payload: %v", err)
	}
	summary, ok := reportPayload["summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary object, got %T", reportPayload["summary"])
	}
	activation, ok := summary["activation"].(map[string]any)
	if !ok {
		t.Fatalf("expected activation summary, got %v", summary["activation"])
	}
	items, ok := activation["items"].([]any)
	if !ok || len(items) != 1 {
		t.Fatalf("expected one govern-first activation item, got %v", activation["items"])
	}
	first, ok := items[0].(map[string]any)
	if !ok {
		t.Fatalf("unexpected activation item type: %T", items[0])
	}
	if first["item_class"] != "production_target_backed" {
		t.Fatalf("expected production_target_backed item class, got %v", first["item_class"])
	}
}

func TestReportIncludesActionPathsProjection(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	snapshot := map[string]any{
		"version": "v1",
		"target": map[string]any{
			"mode":  "org",
			"value": "acme",
		},
		"inventory": map[string]any{
			"agent_privilege_map": []any{
				map[string]any{
					"agent_id":                   "wrkr:alpha:acme",
					"framework":                  "langchain",
					"org":                        "acme",
					"repos":                      []any{"payments"},
					"location":                   "agents/payments.py",
					"risk_score":                 8.8,
					"write_capable":              true,
					"production_write":           true,
					"approval_classification":    "approved",
					"security_visibility_status": "approved",
				},
			},
		},
		"risk_report": map[string]any{
			"generated_at":    "2026-03-25T12:00:00Z",
			"top_findings":    []any{},
			"ranked_findings": []any{},
			"attack_paths": []any{
				map[string]any{
					"org":        "acme",
					"repo":       "payments",
					"path_score": 9.1,
				},
			},
			"action_paths": []any{
				map[string]any{
					"path_id":            "apc-123456",
					"org":                "acme",
					"repo":               "payments",
					"recommended_action": "control",
					"write_capable":      true,
					"credential_access":  true,
					"credential_provenance": map[string]any{
						"type":            "unknown",
						"scope":           "unknown",
						"confidence":      "low",
						"risk_multiplier": 1.2,
					},
					"production_write":  true,
					"attack_path_score": 9.1,
					"risk_score":        8.8,
					"tool_type":         "langchain",
					"location":          "agents/payments.py",
				},
			},
			"action_path_to_control_first": map[string]any{
				"summary": map[string]any{
					"total_paths":                    1,
					"write_capable_paths":            1,
					"production_target_backed_paths": 1,
					"govern_first_paths":             0,
				},
				"path": map[string]any{
					"path_id":            "apc-123456",
					"org":                "acme",
					"repo":               "payments",
					"recommended_action": "control",
				},
			},
			"control_path_graph": map[string]any{
				"version": "1",
				"summary": map[string]any{
					"total_nodes": 1,
					"total_edges": 0,
					"node_kinds": []any{
						map[string]any{"kind": "control_path", "count": 1},
					},
					"edge_kinds": []any{},
				},
				"nodes": []any{
					map[string]any{
						"node_id": "cpg-node-1",
						"path_id": "apc-123456",
						"kind":    "control_path",
						"org":     "acme",
						"repo":    "payments",
					},
				},
				"edges": []any{},
			},
		},
	}
	payload, err := json.Marshal(snapshot)
	if err != nil {
		t.Fatalf("marshal state payload: %v", err)
	}
	if err := os.WriteFile(statePath, append(payload, '\n'), 0o600); err != nil {
		t.Fatalf("write state payload: %v", err)
	}

	var out bytes.Buffer
	var errOut bytes.Buffer
	if code := Run([]string{"report", "--state", statePath, "--json"}, &out, &errOut); code != 0 {
		t.Fatalf("report failed: %d %s", code, errOut.String())
	}

	var reportPayload map[string]any
	if err := json.Unmarshal(out.Bytes(), &reportPayload); err != nil {
		t.Fatalf("parse report payload: %v", err)
	}
	if _, ok := reportPayload["action_paths"].([]any); !ok {
		t.Fatalf("expected top-level action_paths, got %v", reportPayload["action_paths"])
	}
	if _, ok := reportPayload["action_path_to_control_first"].(map[string]any); !ok {
		t.Fatalf("expected top-level action_path_to_control_first, got %v", reportPayload["action_path_to_control_first"])
	}
	if _, ok := reportPayload["control_path_graph"].(map[string]any); !ok {
		t.Fatalf("expected top-level control_path_graph, got %v", reportPayload["control_path_graph"])
	}
	summary, ok := reportPayload["summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary object, got %T", reportPayload["summary"])
	}
	if _, ok := summary["action_paths"].([]any); !ok {
		t.Fatalf("expected summary action_paths, got %v", summary["action_paths"])
	}
	actionPaths, ok := summary["action_paths"].([]any)
	if !ok || len(actionPaths) != 1 {
		t.Fatalf("expected one summary action path, got %v", summary["action_paths"])
	}
	firstPath, ok := actionPaths[0].(map[string]any)
	if !ok {
		t.Fatalf("unexpected summary action path type: %T", actionPaths[0])
	}
	if firstPath["confidence_lane"] == "" || firstPath["control_state"] == "" || firstPath["risk_zone"] == "" {
		t.Fatalf("expected projected confidence/control/risk fields on action path, got %v", firstPath)
	}
	controlFirst, ok := summary["action_path_to_control_first"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary action_path_to_control_first, got %v", summary["action_path_to_control_first"])
	}
	controlSummary, ok := controlFirst["summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected action_path_to_control_first summary, got %v", controlFirst["summary"])
	}
	if controlSummary["empty_state_status"] == nil {
		t.Fatalf("expected empty_state_status in control-first summary, got %v", controlSummary)
	}
	if _, ok := summary["control_path_graph"].(map[string]any); !ok {
		t.Fatalf("expected summary control_path_graph, got %v", summary["control_path_graph"])
	}
}

func TestReportEvidenceJSONIncludesActionSurfaceRegistry(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	bundlePath := filepath.Join(tmp, "evidence.json")
	writeJSONFile(t, statePath, map[string]any{
		"version": "v1",
		"target": map[string]any{
			"mode":  "org",
			"value": "acme",
		},
		"inventory": map[string]any{
			"agent_privilege_map": []any{
				map[string]any{
					"agent_id":                "wrkr:payments:acme",
					"framework":               "openapi",
					"org":                     "acme",
					"repos":                   []any{"payments"},
					"location":                "openapi.yaml",
					"risk_score":              8.4,
					"write_capable":           true,
					"approval_classification": "approved",
				},
			},
		},
		"risk_report": map[string]any{
			"generated_at":    "2026-05-11T12:00:00Z",
			"top_findings":    []any{},
			"ranked_findings": []any{},
			"action_paths": []any{
				map[string]any{
					"path_id":            "apc-7890",
					"org":                "acme",
					"repo":               "payments",
					"tool_type":          "openapi",
					"location":           "openapi.yaml",
					"write_capable":      true,
					"production_write":   false,
					"approval_gap":       false,
					"credential_access":  false,
					"attack_path_score":  7.8,
					"risk_score":         8.4,
					"recommended_action": "control",
					"confidence_lane":    "likely_action_path",
					"control_state":      "evidence_required",
					"risk_zone":          "production_data",
					"review_burden":      "high",
					"tool_instance_id":   "openapi-payments",
					"purpose":            "Payments API",
					"mutable_endpoint_semantics": []any{
						map[string]any{
							"semantic":   "payment",
							"confidence": "high",
							"surface":    "openapi",
							"operation":  "POST /v1/payments",
						},
					},
				},
			},
			"action_path_to_control_first": map[string]any{
				"summary": map[string]any{
					"total_paths": 1,
				},
				"path": map[string]any{
					"path_id": "apc-7890",
					"org":     "acme",
					"repo":    "payments",
				},
			},
			"control_path_graph": map[string]any{
				"version": "1",
				"summary": map[string]any{
					"total_nodes": 0,
					"total_edges": 0,
					"node_kinds":  []any{},
					"edge_kinds":  []any{},
				},
				"nodes": []any{},
				"edges": []any{},
			},
		},
	})

	var out bytes.Buffer
	var errOut bytes.Buffer
	if code := Run([]string{"report", "--state", statePath, "--json", "--evidence-json", "--evidence-json-path", bundlePath}, &out, &errOut); code != 0 {
		t.Fatalf("report failed: %d %s", code, errOut.String())
	}

	var reportPayload map[string]any
	if err := json.Unmarshal(out.Bytes(), &reportPayload); err != nil {
		t.Fatalf("parse report payload: %v", err)
	}
	summary, ok := reportPayload["summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary object, got %T", reportPayload["summary"])
	}
	registry, ok := summary["action_surface_registry"].([]any)
	if !ok || len(registry) != 1 {
		t.Fatalf("expected summary action_surface_registry entry, got %v", summary["action_surface_registry"])
	}

	bundlePayload, err := os.ReadFile(bundlePath)
	if err != nil {
		t.Fatalf("read evidence bundle: %v", err)
	}
	var evidencePayload map[string]any
	if err := json.Unmarshal(bundlePayload, &evidencePayload); err != nil {
		t.Fatalf("parse evidence bundle: %v", err)
	}
	evidenceRegistry, ok := evidencePayload["action_surface_registry"].([]any)
	if !ok || len(evidenceRegistry) != 1 {
		t.Fatalf("expected evidence action_surface_registry entry, got %v", evidencePayload["action_surface_registry"])
	}
}

func TestReportContractBuyerArtifactsPassQAInternalAndRedacted(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	writeJSONFile(t, statePath, map[string]any{
		"version": "v1",
		"risk_report": map[string]any{
			"generated_at":    "2026-05-25T12:00:00Z",
			"top_findings":    []any{},
			"ranked_findings": []any{},
			"action_paths": []any{
				map[string]any{
					"path_id":                   "apc-plain-source",
					"org":                       "acme",
					"repo":                      "acme/payments",
					"tool_type":                 "openapi",
					"location":                  "openapi/payments.yaml",
					"write_capable":             true,
					"risk_score":                8.9,
					"recommended_action":        "control",
					"confidence_lane":           "confirmed_action_path",
					"action_path_type":          "plain_source_code",
					"target_class":              "customer_data_adjacent",
					"control_state":             "evidence_required",
					"risk_zone":                 "production_data",
					"review_burden":             "high",
					"control_priority":          "control_first",
					"risk_tier":                 "high",
					"control_resolution_state":  "external_control_reference",
					"approval_evidence_state":   "declared",
					"owner_evidence_state":      "verified",
					"proof_evidence_state":      "unknown",
					"runtime_evidence_state":    "unknown",
					"target_evidence_state":     "verified",
					"credential_evidence_state": "unknown",
					"remediation":               "Review the declared mutable endpoint scope, tighten approval evidence for the exact action path, and rescan.",
				},
			},
		},
	})

	for _, tc := range []struct {
		name         string
		shareProfile string
	}{
		{name: "internal", shareProfile: "internal"},
		{name: "customer-redacted", shareProfile: "customer-redacted"},
	} {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			var out bytes.Buffer
			var errOut bytes.Buffer
			mdPath := filepath.Join(tmp, tc.name+"-report.md")
			csvPath := filepath.Join(tmp, tc.name+"-backlog.csv")
			code := Run([]string{
				"report",
				"--state", statePath,
				"--template", "agent-action-bom",
				"--share-profile", tc.shareProfile,
				"--md", "--md-path", mdPath,
				"--csv-backlog", "--csv-backlog-path", csvPath,
				"--json",
			}, &out, &errOut)
			if code != 0 {
				t.Fatalf("report failed: %d %s", code, errOut.String())
			}
			var reportPayload map[string]any
			if err := json.Unmarshal(out.Bytes(), &reportPayload); err != nil {
				t.Fatalf("parse report payload: %v", err)
			}

			markdown, err := os.ReadFile(mdPath)
			if err != nil {
				t.Fatalf("read markdown artifact: %v", err)
			}
			backlogCSV, err := os.ReadFile(csvPath)
			if err != nil {
				t.Fatalf("read backlog artifact: %v", err)
			}
			if err := reportcore.ValidateBuyerArtifactTexts(reportcore.BuyerArtifactQAInput{
				ActionPathTypes: []string{"plain_source_code"},
				PathEvidence:    reportQAPathEvidence(t, reportPayload),
				Texts: map[string]string{
					tc.name + "_json":     out.String(),
					tc.name + "_markdown": string(markdown),
					tc.name + "_backlog":  string(backlogCSV),
				},
			}); err != nil {
				t.Fatalf("expected %s artifacts to pass buyer QA: %v", tc.name, err)
			}
		})
	}
}

func reportQAPathEvidence(t *testing.T, reportPayload map[string]any) []reportcore.BuyerArtifactPathEvidence {
	t.Helper()

	actionPaths, ok := reportPayload["action_paths"].([]any)
	if !ok {
		return nil
	}
	evidence := make([]reportcore.BuyerArtifactPathEvidence, 0, len(actionPaths))
	for _, item := range actionPaths {
		record, ok := item.(map[string]any)
		if !ok {
			t.Fatalf("unexpected action path type: %T", item)
		}
		evidence = append(evidence, reportcore.BuyerArtifactPathEvidence{
			ActionPathType: stringValue(record["action_path_type"]),
			Repo:           stringValue(record["repo"]),
			Location:       stringValue(record["location"]),
		})
	}
	return evidence
}

func stringValue(value any) string {
	text, _ := value.(string)
	return text
}

func TestReportAssessmentSummaryPrioritizesGovernFirstPaths(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	snapshot := map[string]any{
		"version": "v1",
		"target": map[string]any{
			"mode":  "org",
			"value": "acme",
		},
		"profile": map[string]any{
			"profile": "assessment",
		},
		"inventory": map[string]any{
			"non_human_identities": []any{
				map[string]any{
					"identity_type": "github_app",
					"subject":       "github_app",
					"source":        "workflow_static_signal",
				},
			},
		},
		"risk_report": map[string]any{
			"generated_at": "2026-03-25T12:00:00Z",
			"top_findings": []any{
				map[string]any{
					"canonical_key": "secret|1",
					"risk_score":    9.5,
					"finding": map[string]any{
						"finding_type": "secret_presence",
						"severity":     "high",
						"tool_type":    "secret",
						"org":          "acme",
						"repo":         "payments",
						"location":     ".env",
					},
				},
			},
			"ranked_findings": []any{
				map[string]any{
					"canonical_key": "secret|1",
					"risk_score":    9.5,
					"finding": map[string]any{
						"finding_type": "secret_presence",
						"severity":     "high",
						"tool_type":    "secret",
						"org":          "acme",
						"repo":         "payments",
						"location":     ".env",
					},
				},
			},
			"action_paths": []any{
				map[string]any{
					"path_id":                   "apc-123456",
					"org":                       "acme",
					"repo":                      "payments",
					"recommended_action":        "control",
					"write_capable":             true,
					"production_write":          true,
					"attack_path_score":         9.1,
					"risk_score":                8.8,
					"tool_type":                 "langchain",
					"location":                  "agents/payments.py",
					"execution_identity":        "github_app",
					"execution_identity_type":   "github_app",
					"execution_identity_source": "workflow_static_signal",
					"execution_identity_status": "known",
				},
			},
			"action_path_to_control_first": map[string]any{
				"summary": map[string]any{
					"total_paths":                    1,
					"write_capable_paths":            1,
					"production_target_backed_paths": 1,
					"govern_first_paths":             0,
				},
				"path": map[string]any{
					"path_id":                   "apc-123456",
					"org":                       "acme",
					"repo":                      "payments",
					"recommended_action":        "control",
					"write_capable":             true,
					"production_write":          true,
					"attack_path_score":         9.1,
					"risk_score":                8.8,
					"tool_type":                 "langchain",
					"location":                  "agents/payments.py",
					"execution_identity":        "github_app",
					"execution_identity_type":   "github_app",
					"execution_identity_source": "workflow_static_signal",
					"execution_identity_status": "known",
				},
			},
		},
	}
	payload, err := json.Marshal(snapshot)
	if err != nil {
		t.Fatalf("marshal state payload: %v", err)
	}
	if err := os.WriteFile(statePath, append(payload, '\n'), 0o600); err != nil {
		t.Fatalf("write state payload: %v", err)
	}

	var out bytes.Buffer
	var errOut bytes.Buffer
	if code := Run([]string{"report", "--state", statePath, "--json"}, &out, &errOut); code != 0 {
		t.Fatalf("report failed: %d %s", code, errOut.String())
	}

	var reportPayload map[string]any
	if err := json.Unmarshal(out.Bytes(), &reportPayload); err != nil {
		t.Fatalf("parse report payload: %v", err)
	}
	if _, ok := reportPayload["assessment_summary"].(map[string]any); !ok {
		t.Fatalf("expected top-level assessment_summary, got %v", reportPayload["assessment_summary"])
	}
	if _, ok := reportPayload["exposure_groups"].([]any); !ok {
		t.Fatalf("expected top-level exposure_groups, got %v", reportPayload["exposure_groups"])
	}
	summary, ok := reportPayload["summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary object, got %T", reportPayload["summary"])
	}
	assessmentSummary, ok := summary["assessment_summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary assessment_summary, got %v", summary["assessment_summary"])
	}
	for _, key := range []string{"ownerless_exposure", "identity_exposure_summary", "identity_to_review_first", "identity_to_revoke_first"} {
		if _, present := assessmentSummary[key]; !present {
			t.Fatalf("expected assessment_summary key %q, got %v", key, assessmentSummary)
		}
	}
	topRisks, ok := summary["top_risks"].([]any)
	if !ok || len(topRisks) == 0 {
		t.Fatalf("expected top_risks payload, got %v", summary["top_risks"])
	}
	first, ok := topRisks[0].(map[string]any)
	if !ok {
		t.Fatalf("unexpected top_risks item type: %T", topRisks[0])
	}
	if first["finding_type"] != "action_path" {
		t.Fatalf("expected action_path to lead top_risks when action_paths exist, got %v", first)
	}
}

func TestReportPublicShareRedactsActionPathProjection(t *testing.T) {
	t.Parallel()

	tmp := t.TempDir()
	statePath := filepath.Join(tmp, "state.json")
	snapshot := map[string]any{
		"version": "v1",
		"target": map[string]any{
			"mode":  "org",
			"value": "acme",
		},
		"inventory": map[string]any{},
		"risk_report": map[string]any{
			"generated_at":    "2026-03-25T12:00:00Z",
			"top_findings":    []any{},
			"ranked_findings": []any{},
			"action_paths": []any{
				map[string]any{
					"path_id":                    "apc-123456",
					"org":                        "acme",
					"repo":                       "payments",
					"agent_id":                   "wrkr:alpha:acme",
					"tool_type":                  "langchain",
					"location":                   "agents/payments.py",
					"risk_score":                 8.8,
					"attack_path_score":          9.1,
					"recommended_action":         "control",
					"execution_identity":         "github_app",
					"execution_identity_type":    "github_app",
					"execution_identity_status":  "known",
					"matched_production_targets": []any{"deploy/prod"},
				},
			},
			"action_path_to_control_first": map[string]any{
				"summary": map[string]any{
					"total_paths":                    1,
					"write_capable_paths":            1,
					"production_target_backed_paths": 1,
					"govern_first_paths":             0,
				},
				"path": map[string]any{
					"path_id":            "apc-123456",
					"org":                "acme",
					"repo":               "payments",
					"agent_id":           "wrkr:alpha:acme",
					"tool_type":          "langchain",
					"location":           "agents/payments.py",
					"recommended_action": "control",
				},
			},
		},
	}
	payload, err := json.Marshal(snapshot)
	if err != nil {
		t.Fatalf("marshal state payload: %v", err)
	}
	if err := os.WriteFile(statePath, append(payload, '\n'), 0o600); err != nil {
		t.Fatalf("write state payload: %v", err)
	}

	var out bytes.Buffer
	var errOut bytes.Buffer
	if code := Run([]string{"report", "--state", statePath, "--template", "public", "--share-profile", "public", "--json"}, &out, &errOut); code != 0 {
		t.Fatalf("report failed: %d %s", code, errOut.String())
	}

	var reportPayload map[string]any
	if err := json.Unmarshal(out.Bytes(), &reportPayload); err != nil {
		t.Fatalf("parse report payload: %v", err)
	}
	actionPaths, ok := reportPayload["action_paths"].([]any)
	if !ok || len(actionPaths) != 1 {
		t.Fatalf("expected one action path, got %v", reportPayload["action_paths"])
	}
	firstPath, ok := actionPaths[0].(map[string]any)
	if !ok {
		t.Fatalf("unexpected action path type: %T", actionPaths[0])
	}
	for key, prefix := range map[string]string{
		"path_id":            "path-",
		"org":                "org-",
		"repo":               "repo-",
		"agent_id":           "agent-",
		"location":           "loc-",
		"execution_identity": "identity-",
	} {
		value, _ := firstPath[key].(string)
		if !strings.HasPrefix(value, prefix) {
			t.Fatalf("expected %s to be redacted with prefix %q, got %q", key, prefix, value)
		}
	}
	targets, ok := firstPath["matched_production_targets"].([]any)
	if !ok || len(targets) != 1 {
		t.Fatalf("expected one redacted production target, got %v", firstPath["matched_production_targets"])
	}
	targetValue, _ := targets[0].(string)
	if !strings.HasPrefix(targetValue, "target-") {
		t.Fatalf("expected redacted production target, got %q", targetValue)
	}

	summary, ok := reportPayload["summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary object, got %T", reportPayload["summary"])
	}
	controlFirst, ok := summary["action_path_to_control_first"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary action_path_to_control_first, got %v", summary["action_path_to_control_first"])
	}
	path, ok := controlFirst["path"].(map[string]any)
	if !ok {
		t.Fatalf("expected control-first path object, got %v", controlFirst["path"])
	}
	repo, _ := path["repo"].(string)
	if !strings.HasPrefix(repo, "repo-") {
		t.Fatalf("expected redacted control-first repo, got %q", repo)
	}
	assessmentSummary, ok := summary["assessment_summary"].(map[string]any)
	if !ok {
		t.Fatalf("expected summary assessment_summary, got %v", summary["assessment_summary"])
	}
	reviewFirst, ok := assessmentSummary["identity_to_review_first"].(map[string]any)
	if !ok {
		t.Fatalf("expected redacted identity_to_review_first, got %v", assessmentSummary["identity_to_review_first"])
	}
	identityValue, _ := reviewFirst["execution_identity"].(string)
	if !strings.HasPrefix(identityValue, "identity-") {
		t.Fatalf("expected redacted review identity, got %q", identityValue)
	}
	exposureGroups, ok := reportPayload["exposure_groups"].([]any)
	if !ok || len(exposureGroups) == 0 {
		t.Fatalf("expected redacted exposure_groups, got %v", reportPayload["exposure_groups"])
	}
	firstGroup, ok := exposureGroups[0].(map[string]any)
	if !ok {
		t.Fatalf("unexpected exposure_groups item type: %T", exposureGroups[0])
	}
	groupID, _ := firstGroup["group_id"].(string)
	if !strings.HasPrefix(groupID, "group-") {
		t.Fatalf("expected redacted exposure group id, got %q", groupID)
	}
}
