import fs from "node:fs";
import path from "node:path";
import request from "supertest";
import { afterEach, describe, expect, it, vi } from "vitest";

const FLAG_KEYS = [
  "FEATURE_ASSISTANT_PROBLEM_UNITS_V1",
  "FEATURE_ASSISTANT_ANSWER_POLICY_V11",
  "FEATURE_ASSISTANT_BROAD_GUARD_V1",
  "FEATURE_ASSISTANT_MIN_EVIDENCE_GATE_V1",
  "FEATURE_ASSISTANT_ANTI_GENERIC_RANKING_GUARD_V1",
  "FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1",
  "FEATURE_ASSISTANT_PROBLEM_UNIT_CONTINUITY_V1",
  "FEATURE_ASSISTANT_LIFECYCLE_RUNTIME_V1",
  "FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1"
] as const;

const ORIGINAL_FLAGS: Record<string, string | undefined> = Object.fromEntries(FLAG_KEYS.map((key) => [key, process.env[key]]));

type Stage3LifecycleHints = {
  expected_lifecycle_domain?: string;
  require_current_expected_state_pair?: boolean;
  require_missing_or_invalid_transition?: boolean;
  require_previous_states?: boolean;
  require_terminal_state_mismatch?: boolean;
  require_wrong_closing_document_type?: boolean;
  require_cross_branch_conflict?: boolean;
  require_period_close_impact?: boolean;
  require_lifecycle_mode?: string;
};

type Stage3LifecycleProbeCase = {
  case_id: string;
  turns: Array<{ user_message: string }>;
  expected_hints?: Stage3LifecycleHints;
};

type Stage3LifecycleProbeSuite = {
  suite_id: string;
  scenario_count: number;
  case_ids: string[];
  cases: Stage3LifecycleProbeCase[];
};

function restoreFlags(): void {
  for (const key of FLAG_KEYS) {
    const original = ORIGINAL_FLAGS[key];
    if (original === undefined) {
      delete process.env[key];
    } else {
      process.env[key] = original;
    }
  }
}

async function createAppWithLifecycleFlags() {
  process.env.FEATURE_ASSISTANT_PROBLEM_UNITS_V1 = "1";
  process.env.FEATURE_ASSISTANT_ANSWER_POLICY_V11 = "1";
  process.env.FEATURE_ASSISTANT_BROAD_GUARD_V1 = "1";
  process.env.FEATURE_ASSISTANT_MIN_EVIDENCE_GATE_V1 = "1";
  process.env.FEATURE_ASSISTANT_ANTI_GENERIC_RANKING_GUARD_V1 = "1";
  process.env.FEATURE_ASSISTANT_PROBLEM_CENTRIC_ANSWER_V1 = "1";
  process.env.FEATURE_ASSISTANT_PROBLEM_UNIT_CONTINUITY_V1 = "0";
  process.env.FEATURE_ASSISTANT_LIFECYCLE_RUNTIME_V1 = "1";
  process.env.FEATURE_ASSISTANT_LIFECYCLE_ANSWER_V1 = "1";
  vi.resetModules();
  const { createApp } = await import("../src/server");
  return createApp();
}

function loadSuite(): Stage3LifecycleProbeSuite {
  const suitePath = path.resolve(process.cwd(), "../eval_cases/assistant_stage3_lifecycle_probe_v0_1.json");
  const raw = fs.readFileSync(suitePath, "utf8").replace(/^\uFEFF/, "");
  return JSON.parse(raw) as Stage3LifecycleProbeSuite;
}

function routedRetrievalResults(body: Record<string, unknown>): Record<string, unknown>[] {
  const debug = (body.debug ?? {}) as { retrieval_results?: unknown[] };
  if (!Array.isArray(debug.retrieval_results)) {
    return [];
  }
  return (debug.retrieval_results as Record<string, unknown>[]).filter((item) => String(item.route ?? "") !== "no_route");
}

function collectLifecycleUnits(results: Record<string, unknown>[]): Record<string, unknown>[] {
  const units: Record<string, unknown>[] = [];
  for (const result of results) {
    const problemUnits = Array.isArray(result.problem_units) ? (result.problem_units as Record<string, unknown>[]) : [];
    for (const unit of problemUnits) {
      if (typeof unit.lifecycle_domain === "string" && unit.lifecycle_domain.length > 0) {
        units.push(unit);
      }
    }
  }
  return units;
}

function hasPreviousStates(unit: Record<string, unknown>): boolean {
  const resolution = (unit.lifecycle_resolution ?? {}) as { resolved_previous_states?: unknown };
  return Array.isArray(resolution.resolved_previous_states);
}

describe.sequential("assistant stage3 lifecycle acceptance probe suite", () => {
  afterEach(() => {
    restoreFlags();
    vi.resetModules();
  });

  it("runs stage3 lifecycle probe prompts with separate acceptance checks", async () => {
    const app = await createAppWithLifecycleFlags();
    const suite = loadSuite();

    expect(suite.suite_id).toBe("assistant_stage3_lifecycle_probe");
    expect(suite.scenario_count).toBe(suite.cases.length);
    expect(suite.case_ids.length).toBe(suite.cases.length);

    for (const probeCase of suite.cases) {
      const response = await request(app).post("/api/assistant/message").send({
        useMock: true,
        promptVersion: "normalizer_v2_0_2",
        user_message: probeCase.turns[0]?.user_message ?? ""
      });

      expect(response.status, probeCase.case_id).toBe(200);
      const body = response.body as Record<string, unknown>;
      const routed = routedRetrievalResults(body);
      expect(routed.length, `${probeCase.case_id}: routed retrieval`).toBeGreaterThan(0);

      const lifecycleUnits = collectLifecycleUnits(routed);
      expect(lifecycleUnits.length, `${probeCase.case_id}: lifecycle units`).toBeGreaterThan(0);

      const lifecycleEnrichedTotal = routed.reduce((acc, item) => {
        const summary = (item.problem_unit_summary ?? {}) as { lifecycle_enriched_units?: unknown };
        const count = typeof summary.lifecycle_enriched_units === "number" ? summary.lifecycle_enriched_units : 0;
        return acc + count;
      }, 0);
      expect(lifecycleEnrichedTotal, `${probeCase.case_id}: lifecycle enriched total`).toBeGreaterThan(0);

      const hints = probeCase.expected_hints ?? {};
      if (hints.require_current_expected_state_pair) {
        expect(
          lifecycleUnits.some((unit) => {
            const current = String(unit.current_lifecycle_state ?? "");
            const expected = String(unit.expected_lifecycle_state ?? "");
            return current.length > 0 && expected.length > 0;
          }),
          `${probeCase.case_id}: current/expected pair`
        ).toBe(true);
      }

      if (hints.require_previous_states) {
        expect(lifecycleUnits.some((unit) => hasPreviousStates(unit)), `${probeCase.case_id}: previous states field`).toBe(true);
      }

      if (typeof hints.require_lifecycle_mode === "string" && hints.require_lifecycle_mode.length > 0) {
        const mode = String(((body.debug ?? {}) as { problem_answer_mode?: unknown }).problem_answer_mode ?? "");
        expect(mode, `${probeCase.case_id}: lifecycle mode`).toBe(hints.require_lifecycle_mode);
      }
    }
  });
});
