import { describe, expect, it } from "vitest";
import type { CandidateEvidenceItem, ProblemUnit } from "../src/types/stage2ProblemUnits";
import { LifecycleRegistry, resolveLifecycle } from "../src/services/lifecycleRuntime";

function buildProblemUnit(input: {
  id: string;
  type?: ProblemUnit["problem_unit_type"];
  mechanismSummary?: string;
  businessDefectClass?: string;
  accounts?: string[];
  actualState?: string;
  expectedState?: string;
  periodCloseRisk?: boolean;
}): ProblemUnit {
  return {
    schema_version: "problem_unit_v0_1",
    problem_unit_id: input.id,
    problem_unit_type: input.type ?? "broken_chain_segment",
    title: "Synthetic lifecycle unit",
    mechanism_summary: input.mechanismSummary ?? "Synthetic lifecycle mechanism",
    business_defect_class: input.businessDefectClass ?? "broken_lifecycle",
    severity: {
      score: 0.64,
      grade: "medium"
    },
    confidence: {
      score: 0.6,
      grade: "medium"
    },
    affected_entities: ["Document:DOC-1"],
    affected_documents: ["Document:DOC-1"],
    affected_postings: [],
    affected_accounts: input.accounts ?? ["51"],
    affected_counterparties: [],
    affected_contracts: [],
    ...(input.actualState
      ? {
          actual_state: input.actualState
        }
      : {}),
    ...(input.expectedState
      ? {
          expected_state: input.expectedState
        }
      : {}),
    ...(input.periodCloseRisk
      ? {
          period_impact: {
            is_period_sensitive: true,
            impact_class: "close_risk" as const
          }
        }
      : {}),
    evidence_pack: ["cand-1"],
    entity_backlinks: [{ entity: "Document", id: "DOC-1" }],
    snapshot_limitations: []
  };
}

function buildCandidate(input: {
  id: string;
  anomalies?: string[];
  relations?: string[];
  confidence?: "high" | "medium" | "low";
}): CandidateEvidenceItem {
  return {
    schema_version: "candidate_evidence_v0_1",
    candidate_id: input.id,
    route: "hybrid_store_plus_live",
    source_ref: {
      schema_version: "evidence_source_ref_v1",
      namespace: "snapshot_2020",
      entity: "Document",
      id: "DOC-1",
      period: "2020-06",
      canonical_ref: "evidence_source_ref_v1|snapshot_2020|document|doc-1|2020-06"
    },
    relation_pattern_hits: input.relations ?? [],
    anomaly_patterns: input.anomalies ?? [],
    entity_backlinks: [{ entity: "Document", id: "DOC-1" }],
    confidence_hint: input.confidence ?? "medium"
  };
}

describe("stage3 lifecycle registry and resolver wave2", () => {
  it("exposes all lifecycle domains in the registry", () => {
    const domains = LifecycleRegistry.listDomains();
    expect(domains).toEqual([
      "bank_settlement",
      "customer_settlement",
      "deferred_expense",
      "fixed_asset",
      "vat_flow",
      "period_close"
    ]);

    for (const domain of domains) {
      const model = LifecycleRegistry.getDomain(domain);
      expect(model.lifecycle_domain).toBe(domain);
      expect(model.states.length).toBeGreaterThan(0);
      expect(model.defects.some((definition) => definition.defect_code === "stale_active_state")).toBe(true);
    }
  });

  it("infers lifecycle domain for all covered stage3 domains", () => {
    const cases: Array<{
      name: string;
      unit: ProblemUnit;
      candidates: CandidateEvidenceItem[];
      expectedDomain: string;
    }> = [
      {
        name: "bank settlement",
        unit: buildProblemUnit({ id: "domain-bank", accounts: ["51"], mechanismSummary: "bank settlement reconciliation" }),
        candidates: [buildCandidate({ id: "cand-bank", relations: ["payment_to_settlement"] })],
        expectedDomain: "bank_settlement"
      },
      {
        name: "customer settlement",
        unit: buildProblemUnit({ id: "domain-customer", accounts: ["62"], mechanismSummary: "customer receivable chain" }),
        candidates: [buildCandidate({ id: "cand-customer", relations: ["settlement_to_invoice"] })],
        expectedDomain: "customer_settlement"
      },
      {
        name: "deferred expense",
        unit: buildProblemUnit({ id: "domain-97", accounts: ["97"], mechanismSummary: "deferred writeoff path" }),
        candidates: [buildCandidate({ id: "cand-97", relations: ["deferred_writeoff"] })],
        expectedDomain: "deferred_expense"
      },
      {
        name: "fixed asset",
        unit: buildProblemUnit({ id: "domain-os", accounts: ["01"], mechanismSummary: "fixed asset depreciation" }),
        candidates: [buildCandidate({ id: "cand-os", relations: ["depreciation_register_movement"] })],
        expectedDomain: "fixed_asset"
      },
      {
        name: "vat flow",
        unit: buildProblemUnit({ id: "domain-vat", accounts: ["68"], mechanismSummary: "vat deduction chain" }),
        candidates: [buildCandidate({ id: "cand-vat", anomalies: ["cross_branch_inconsistency"] })],
        expectedDomain: "vat_flow"
      },
      {
        name: "period close",
        unit: buildProblemUnit({
          id: "domain-close",
          type: "period_risk_cluster",
          mechanismSummary: "period close blocker",
          periodCloseRisk: true
        }),
        candidates: [buildCandidate({ id: "cand-close", anomalies: ["period_close_risk"] })],
        expectedDomain: "period_close"
      }
    ];

    for (const item of cases) {
      const resolution = resolveLifecycle({
        unit: item.unit,
        candidates: item.candidates
      });
      expect(resolution.lifecycle_domain, item.name).toBe(item.expectedDomain);
    }
  });

  it("normalizes unknown explicit states against registry and records limitations", () => {
    const resolution = resolveLifecycle({
      unit: buildProblemUnit({
        id: "normalize-invalid-states",
        accounts: ["01"],
        mechanismSummary: "fixed asset depreciation",
        actualState: "legacy_state_unmapped",
        expectedState: "legacy_target_unmapped"
      }),
      candidates: [buildCandidate({ id: "cand-normalize", relations: ["depreciation_register_movement"] })]
    });

    expect(resolution.lifecycle_domain).toBe("fixed_asset");
    expect(resolution.resolved_current_state).toBe("depreciation_active");
    expect(resolution.resolved_expected_state).toBe("disposed");
    expect(resolution.snapshot_limitations).toContain("actual_state_not_in_registry_normalized");
    expect(resolution.snapshot_limitations).toContain("expected_state_not_in_registry_normalized");
  });

  it("infers missing transition from registry transition path", () => {
    const resolution = resolveLifecycle({
      unit: buildProblemUnit({
        id: "missing-transition",
        accounts: ["51"],
        actualState: "bank_recorded",
        expectedState: "settlement_closed"
      }),
      candidates: [buildCandidate({ id: "cand-missing", anomalies: ["missing_link", "no_continuation"] })]
    });

    expect(resolution.missing_transitions[0]).toBe("bank_recorded->settlement_closed");
  });

  it("builds previous state chain from registry model", () => {
    const resolution = resolveLifecycle({
      unit: buildProblemUnit({
        id: "previous-chain",
        accounts: ["51"],
        actualState: "bank_recorded",
        expectedState: "settlement_closed"
      }),
      candidates: [buildCandidate({ id: "cand-prev", relations: ["payment_to_settlement"] })]
    });

    expect(resolution.resolved_previous_states).toEqual(["initiated_payment"]);
  });
});
