package orch

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

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")
	runID := "council_blog_report_010"

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"council", "start",
		"--run", runID,
		"--target", "Review the council reporting flow.",
	)

	stdout, _, exitCode := executeOrchCommand(
		"--db", dbPath,
		"--json",
		"council", "report",
		"--run", runID,
	)
	if exitCode != 30 {
		t.Fatalf("expected invalid_state exit code 30, got %d\nstdout:\n%s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "invalid_state")

	if msg := orchErrorMessage(t, stdout); !strings.Contains(msg, "run council tally first") {
		t.Fatalf("expected error message to require council tally first, got %q", msg)
	}
}

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")
	runID := "council_blog_report_012"
	seedCouncilReportRun(t, dbPath, runID)

	stdout, _, exitCode := executeOrchCommand(
		"--db", dbPath,
		"--json",
		"council", "report",
		"--run", runID,
		"--show", "consensus,invalid",
	)
	if exitCode != 30 {
		t.Fatalf("expected invalid_input exit code 30, got %d\nstdout:\n%s", exitCode, stdout)
	}
	assertErrorJSON(t, stdout, "invalid_input")

	msg := orchErrorMessage(t, stdout)
	for _, expected := range []string{"consensus", "majority", "minority", "all"} {
		if !strings.Contains(msg, expected) {
			t.Fatalf("expected invalid --show message to mention %q, got %q", expected, msg)
		}
	}
}

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

	dbPath := filepath.Join(t.TempDir(), "coord.db")
	runID := "council_blog_report_011"
	seedOnlyUnanimousCouncilReportRun(t, dbPath, runID)

	reportOut := runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"council", "report",
		"--run", runID,
	)

	var reportResp map[string]any
	mustDecodeJSON(t, reportOut, &reportResp)
	if ok, _ := reportResp["ok"].(bool); !ok {
		t.Fatalf("expected ok=true, got %#v", reportResp)
	}
	if got := nestedString(t, reportResp, "data", "run_id"); got != runID {
		t.Fatalf("expected run id %q, got %q", runID, got)
	}

	show := nestedArray(t, reportResp, "data", "show")
	if len(show) != 1 || show[0] != "consensus" {
		t.Fatalf("expected unanimous-only default show bucket [consensus], got %#v", show)
	}

	summary, ok := nestedValue(t, reportResp, "data", "summary").(map[string]any)
	if !ok {
		t.Fatalf("expected summary object, got %#v", nestedValue(t, reportResp, "data", "summary"))
	}
	if got, _ := summary["consensus"].(float64); got != 1 {
		t.Fatalf("expected one consensus group, got %#v", summary["consensus"])
	}
	if got, _ := summary["majority"].(float64); got != 1 {
		t.Fatalf("expected one majority group, got %#v", summary["majority"])
	}
	if got, _ := summary["minority"].(float64); got != 1 {
		t.Fatalf("expected one minority group, got %#v", summary["minority"])
	}

	groups := nestedArray(t, reportResp, "data", "grouped_recommendations")
	if len(groups) != 1 {
		t.Fatalf("expected one reported recommendation group, got %#v", groups)
	}
	group, ok := groups[0].(map[string]any)
	if !ok {
		t.Fatalf("expected grouped recommendation object, got %#v", groups[0])
	}
	if got, _ := group["bucket"].(string); got != "consensus" {
		t.Fatalf("expected only reported bucket to be consensus, got %#v", group["bucket"])
	}

	artifacts := nestedArray(t, reportResp, "data", "report_artifacts")
	if len(artifacts) != 1 {
		t.Fatalf("expected one report artifact, got %#v", artifacts)
	}
	artifact, ok := artifacts[0].(map[string]any)
	if !ok {
		t.Fatalf("expected report artifact object, got %#v", artifacts[0])
	}
	reportPath, _ := artifact["path"].(string)
	if reportPath == "" {
		t.Fatalf("expected markdown artifact path, got %#v", artifact["path"])
	}
	if _, err := os.Stat(reportPath); err != nil {
		t.Fatalf("expected markdown artifact to exist at %q: %v", reportPath, err)
	}
}

func orchErrorMessage(t *testing.T, raw string) string {
	t.Helper()

	var payload map[string]any
	mustDecodeJSON(t, raw, &payload)
	errorValue, ok := payload["error"].(map[string]any)
	if !ok {
		t.Fatalf("expected error object, got %#v", payload["error"])
	}
	msg, ok := errorValue["message"].(string)
	if !ok {
		t.Fatalf("expected error message string, got %#v", errorValue["message"])
	}
	return msg
}

func seedOnlyUnanimousCouncilReportRun(t *testing.T, dbPath, runID string) {
	t.Helper()

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"council", "start",
		"--run", runID,
		"--target", "Review the council reporting flow.",
		"--only-unanimous",
	)

	completeCouncilReviewer(
		t,
		dbPath,
		runID,
		"architecture-reviewer",
		`{"reviewer_role":"architecture-reviewer","findings":[{"title":"Split contracts","summary":"Transport contracts are mixed into UI code.","proposal":"Move API contract definitions into a dedicated module.","rationale":"This lowers coupling.","confidence":"high","tags":["architecture"],"target_refs":{"repo_path":"."}},{"title":"Share helpers","summary":"Council report rendering paths are repeated.","proposal":"Introduce shared council coordinator helpers for report rendering.","rationale":"This keeps report assembly consistent.","confidence":"medium","tags":["reporting"],"target_refs":{"repo_path":"."}}]}`,
	)
	completeCouncilReviewer(
		t,
		dbPath,
		runID,
		"implementation-reviewer",
		`{"reviewer_role":"implementation-reviewer","findings":[{"title":"Extract contracts","summary":"Shared transport shapes are duplicated.","proposal":"Move API contract definitions into dedicated module","rationale":"This reduces duplication.","confidence":"high","tags":["maintainability"],"target_refs":{"repo_path":"."}},{"title":"Reuse report helpers","summary":"Formatting logic should stay shared.","proposal":"Introduce shared council coordinator helpers for report rendering","rationale":"This avoids formatter drift.","confidence":"medium","tags":["reporting"],"target_refs":{"repo_path":"."}}]}`,
	)
	completeCouncilReviewer(
		t,
		dbPath,
		runID,
		"risk-reviewer",
		`{"reviewer_role":"risk-reviewer","findings":[{"title":"Lock contracts","summary":"Contract drift becomes risky over time.","proposal":"Move API contract definitions into a dedicated module.","rationale":"This reduces integration regressions.","confidence":"high","tags":["risk"],"target_refs":{"repo_path":"."}},{"title":"Cover JSON output","summary":"The council report response should stay stable.","proposal":"Add regression tests for council report JSON output.","rationale":"This catches contract regressions earlier.","confidence":"high","tags":["testing"],"target_refs":{"repo_path":"."}}]}`,
	)

	runOrchCommand(
		t,
		"--db", dbPath,
		"--json",
		"council", "tally",
		"--run", runID,
		"--similarity", "normal",
	)
}
