///|
pub(all) enum DecommissionGate {
  PrimaryWorkflowReady
  LegacyReadOnly
  ImportedRecordsPreserved
  KnownDifferencesArchived
  RollbackWindowPreserved
  ArchiveManifestReady
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) enum DecommissionStatus {
  DecommissionPass
  DecommissionBlocked
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) enum ArchiveItemKind {
  MigrationPlan
  KnownDifferenceReport
  ImportedDataManifest
  HandoffRunbook
  RollbackReport
  LegacySourceReference
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct DecommissionFinding {
  gate : DecommissionGate
  status : DecommissionStatus
  path : String
  message : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct ArchiveItem {
  kind : ArchiveItemKind
  path : String
  owner : String
  retained : Bool
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct DecommissionInput {
  cutover_plan : @cutover.CutoverPlan
  handoff_report : @handoff.OperatorHandoffReport
  import_projection : @migration.LegacyImportProjection
  parity_report : @parity.ParityReport
  comparison_report : @comparison.CutoverComparisonReport
  legacy_app_ref : String
  archive_root : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct DecommissionReport {
  ready : Bool
  legacy_app_ref : String
  archive_root : String
  pass_count : Int
  blocked_count : Int
  findings : Array[DecommissionFinding]
  archive_items : Array[ArchiveItem]
  final_note : String
  summary : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn decommission_input(
  cutover_plan : @cutover.CutoverPlan,
  handoff_report : @handoff.OperatorHandoffReport,
  import_projection : @migration.LegacyImportProjection,
  parity_report : @parity.ParityReport,
  comparison_report : @comparison.CutoverComparisonReport,
  legacy_app_ref? : String = "../paa",
  archive_root? : String = "records/decommission",
) -> DecommissionInput {
  {
    cutover_plan,
    handoff_report,
    import_projection,
    parity_report,
    comparison_report,
    legacy_app_ref,
    archive_root,
  }
}

///|
fn finding(
  gate : DecommissionGate,
  status : DecommissionStatus,
  path : String,
  message : String,
) -> DecommissionFinding {
  { gate, status, path, message }
}

///|
fn archive_item(
  kind : ArchiveItemKind,
  path : String,
  owner : String,
  retained? : Bool = true,
) -> ArchiveItem {
  { kind, path, owner, retained }
}

///|
fn primary_workflow_finding(input : DecommissionInput) -> DecommissionFinding {
  if input.cutover_plan.ready_to_cutover && input.handoff_report.ready {
    finding(
      PrimaryWorkflowReady,
      DecommissionPass,
      input.handoff_report.launch_path,
      "Moonfish handoff and cutover plan prove the primary operator workflow is ready",
    )
  } else {
    finding(
      PrimaryWorkflowReady,
      DecommissionBlocked,
      input.handoff_report.launch_path,
      "primary Moonfish workflow is not ready for decommission",
    )
  }
}

///|
fn legacy_read_only_finding(input : DecommissionInput) -> DecommissionFinding {
  if input.legacy_app_ref != "" &&
    input.cutover_plan.checklist.any(fn(step) {
      step.kind is FreezeLegacyApp && step.path == input.legacy_app_ref
    }) &&
    input.comparison_report.cases.any(fn(item) {
      item.kind is LegacyReadOnlyRollback && item.status is ComparisonPass
    }) {
    finding(
      LegacyReadOnly,
      DecommissionPass,
      input.legacy_app_ref,
      "PA Agent is retained only as a read-only rollback and audit reference",
    )
  } else {
    finding(
      LegacyReadOnly,
      DecommissionBlocked,
      input.legacy_app_ref,
      "legacy PA Agent read-only boundary is not proven",
    )
  }
}

///|
fn imported_records_finding(input : DecommissionInput) -> DecommissionFinding {
  if input.import_projection.record_count > 0 &&
    input.import_projection.review_count == 0 &&
    input.import_projection.records.all(fn(record) {
      record.source_path.contains(input.legacy_app_ref) &&
      record.replay_anchor_path != ""
    }) {
    finding(
      ImportedRecordsPreserved,
      DecommissionPass,
      "records/imports/\{input.import_projection.batch_id}.json",
      "legacy records are inspectable in MoonBook and retain replay anchors",
    )
  } else {
    finding(
      ImportedRecordsPreserved,
      DecommissionBlocked,
      "legacy-import-projection",
      "legacy records are missing, under review, or lack replay anchors",
    )
  }
}

///|
fn known_difference_finding(input : DecommissionInput) -> DecommissionFinding {
  if input.parity_report.intentional_difference_count > 0 &&
    input.parity_report.untraced_case_count == 0 &&
    input.parity_report.cases.any(fn(item) {
      item.expectation is IntentionalDifference &&
      item.evidence_refs.any(fn(ref_item) {
        ref_item.kind is KnownDifferenceNote
      })
    }) {
    finding(
      KnownDifferencesArchived,
      DecommissionPass,
      "docs/pa-agent-migration-plan.md#phase-7",
      "intentional differences are traced to archived migration notes",
    )
  } else {
    finding(
      KnownDifferencesArchived,
      DecommissionBlocked,
      "parity-report",
      "known differences are not fully traced",
    )
  }
}

///|
fn rollback_window_finding(input : DecommissionInput) -> DecommissionFinding {
  if input.cutover_plan.rollback_days >= 1 &&
    input.cutover_plan.rollback_plan.any(fn(step) {
      step.kind is MonitorRollbackWindow && step.path == input.legacy_app_ref
    }) {
    finding(
      RollbackWindowPreserved,
      DecommissionPass,
      "cutover-plan.rollback_plan",
      "rollback window keeps PA Agent read-only while Moonfish evidence remains replayable",
    )
  } else {
    finding(
      RollbackWindowPreserved,
      DecommissionBlocked,
      "cutover-plan.rollback_plan",
      "rollback window is not preserved",
    )
  }
}

///|
fn archive_manifest(input : DecommissionInput) -> Array[ArchiveItem] {
  [
    archive_item(MigrationPlan, "docs/pa-agent-migration-plan.md", "MoonBook"),
    archive_item(
      KnownDifferenceReport,
      "records/parity/golden-fixture-report.json",
      "MoonBook",
    ),
    archive_item(
      ImportedDataManifest,
      input.cutover_plan.imported_data_manifest_path,
      "MoonBook",
    ),
    archive_item(
      HandoffRunbook,
      "\{input.archive_root}/operator-handoff.json",
      "MoonBook",
    ),
    archive_item(
      RollbackReport,
      "records/evaluations/rollback-report.json",
      "MoonBook",
    ),
    archive_item(LegacySourceReference, input.legacy_app_ref, "PA Agent"),
  ]
}

///|
fn archive_finding(
  items : Array[ArchiveItem],
  root : String,
) -> DecommissionFinding {
  if root != "" && items.all(fn(item) { item.retained && item.path != "" }) {
    finding(
      ArchiveManifestReady,
      DecommissionPass,
      root,
      "decommission archive manifest retains migration, parity, import, handoff, rollback, and legacy references",
    )
  } else {
    finding(
      ArchiveManifestReady,
      DecommissionBlocked,
      root,
      "decommission archive manifest is incomplete",
    )
  }
}

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

///|
pub fn prepare_decommission_report(
  input : DecommissionInput,
) -> DecommissionReport {
  let archive_items = archive_manifest(input)
  let findings = [
    primary_workflow_finding(input),
    legacy_read_only_finding(input),
    imported_records_finding(input),
    known_difference_finding(input),
    rollback_window_finding(input),
    archive_finding(archive_items, input.archive_root),
  ]
  let blocked_count = count_status(findings, DecommissionBlocked)
  let pass_count = count_status(findings, DecommissionPass)
  let ready = blocked_count == 0
  {
    ready,
    legacy_app_ref: input.legacy_app_ref,
    archive_root: input.archive_root,
    pass_count,
    blocked_count,
    findings,
    archive_items,
    final_note: if ready {
      "PA Agent can remain read-only for rollback and audit while Moonfish is the primary price-action workflow."
    } else {
      "PA Agent decommission is blocked until archive, import, handoff, and rollback evidence pass."
    },
    summary: if ready {
      "decommission report ready with \{pass_count}/\{findings.length()} gate(s) and \{archive_items.length()} archive item(s)"
    } else {
      "decommission report blocked by \{blocked_count} gate(s)"
    },
  }
}