///|
pub(all) enum CutoverComparisonKind {
  GoldenFixtureRun
  LiveSnapshotRehearsal
  LegacyReadOnlyRollback
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) enum CutoverComparisonStatus {
  ComparisonPass
  ComparisonNeedsReview
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct CutoverComparisonCase {
  id : String
  kind : CutoverComparisonKind
  status : CutoverComparisonStatus
  legacy_ref : String
  moonfish_ref : String
  evidence_path : String
  message : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct CutoverComparisonInput {
  parity_report : @parity.ParityReport
  golden_workspace : @workspace.AnalysisWorkspace
  live_bundle : @workflow.AnalysisRunBundle
  legacy_read_only_ref : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct CutoverComparisonReport {
  ready : Bool
  pass_count : Int
  review_count : Int
  golden_case_count : Int
  live_case_count : Int
  cases : Array[CutoverComparisonCase]
  summary : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn cutover_comparison_input(
  parity_report : @parity.ParityReport,
  golden_workspace : @workspace.AnalysisWorkspace,
  live_bundle : @workflow.AnalysisRunBundle,
  legacy_read_only_ref? : String = "../paa",
) -> CutoverComparisonInput {
  { parity_report, golden_workspace, live_bundle, legacy_read_only_ref }
}

///|
fn comparison_case(
  id : String,
  kind : CutoverComparisonKind,
  status : CutoverComparisonStatus,
  legacy_ref : String,
  moonfish_ref : String,
  evidence_path : String,
  message : String,
) -> CutoverComparisonCase {
  { id, kind, status, legacy_ref, moonfish_ref, evidence_path, message }
}

///|
fn golden_fixture_case(input : CutoverComparisonInput) -> CutoverComparisonCase {
  let parity_ready = input.parity_report.review_count == 0 &&
    input.parity_report.untraced_case_count == 0 &&
    input.parity_report.fixture_case_count == input.parity_report.cases.length()
  let workspace_ready = input.golden_workspace.bundle.status is Completed &&
    input.golden_workspace.job_state.outcome is Completed &&
    input.golden_workspace.book_artifact_count > 0
  let status = if parity_ready && workspace_ready {
    ComparisonPass
  } else {
    ComparisonNeedsReview
  }
  comparison_case(
    "golden-fixture-dual-run",
    GoldenFixtureRun,
    status,
    input.parity_report.source_fixture_root,
    "records/parity/golden-fixture-report.json",
    "parity-report",
    "golden PA Agent fixtures are compared to completed Moonfish workspace evidence",
  )
}

///|
fn live_snapshot_case(input : CutoverComparisonInput) -> CutoverComparisonCase {
  let request = input.live_bundle.request.market
  let external_source = request.data_source != Fixture
  let completed = input.live_bundle.status is Completed &&
    input.live_bundle.market_fetch.status is SnapshotReady &&
    input.live_bundle.chart_frame is Some(_) &&
    input.live_bundle.write_plan is Some(_)
  let status = if external_source && completed {
    ComparisonPass
  } else {
    ComparisonNeedsReview
  }
  comparison_case(
    "live-snapshot-rehearsal",
    LiveSnapshotRehearsal,
    status,
    "live/\{request.symbol}/\{request.timeframe}",
    "records/comparisons/live-snapshot-rehearsal.json",
    input.live_bundle.market_fetch.plan.raw_path,
    "an external closed-bar snapshot can complete the Moonfish workflow without provider logic in Moondesk",
  )
}

///|
fn rollback_case(input : CutoverComparisonInput) -> CutoverComparisonCase {
  let status = if input.legacy_read_only_ref != "" &&
    input.live_bundle.status is Completed &&
    input.golden_workspace.replay_plan.can_replay {
    ComparisonPass
  } else {
    ComparisonNeedsReview
  }
  comparison_case(
    "legacy-read-only-rollback",
    LegacyReadOnlyRollback,
    status,
    input.legacy_read_only_ref,
    "records/evaluations/rollback-window.json",
    "cutover-plan",
    "PA Agent remains read-only during the rollback window while Moonfish evidence stays replayable",
  )
}

///|
fn count_status(
  cases : Array[CutoverComparisonCase],
  status : CutoverComparisonStatus,
) -> Int {
  cases.fold(init=0, fn(count, item) {
    if item.status == status {
      count + 1
    } else {
      count
    }
  })
}

///|
fn count_kind(
  cases : Array[CutoverComparisonCase],
  kind : CutoverComparisonKind,
) -> Int {
  cases.fold(init=0, fn(count, item) {
    if item.kind == kind {
      count + 1
    } else {
      count
    }
  })
}

///|
pub fn prepare_cutover_comparison_report(
  input : CutoverComparisonInput,
) -> CutoverComparisonReport {
  let cases = [
    golden_fixture_case(input),
    live_snapshot_case(input),
    rollback_case(input),
  ]
  let review_count = count_status(cases, ComparisonNeedsReview)
  let pass_count = count_status(cases, ComparisonPass)
  let ready = review_count == 0
  {
    ready,
    pass_count,
    review_count,
    golden_case_count: count_kind(cases, GoldenFixtureRun),
    live_case_count: count_kind(cases, LiveSnapshotRehearsal),
    cases,
    summary: if ready {
      "cutover comparison passed \{pass_count}/\{cases.length()} evidence checks"
    } else {
      "cutover comparison has \{review_count} evidence check(s) needing review"
    },
  }
}