///|
pub(all) enum ReleaseGate {
  ProductStatusReady
  RehearsalReady
  SuitePackComplete
  HistoricalRecordsAvailable
  LegacyAppArchived
  ExportEvidenceReady
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) enum ReleaseStatus {
  ReleasePass
  ReleaseBlocked
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct ReleaseFinding {
  gate : ReleaseGate
  status : ReleaseStatus
  evidence_path : String
  message : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct ReleaseCertificateInput {
  product_status : @product.MoonfishProductStatus
  rehearsal_report : @product.ProductRehearsalReport
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct ReleaseCertificate {
  ready : Bool
  run_id : String
  release_path : String
  pass_count : Int
  blocked_count : Int
  findings : Array[ReleaseFinding]
  summary : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn release_certificate_input(
  product_status : @product.MoonfishProductStatus,
  rehearsal_report : @product.ProductRehearsalReport,
) -> ReleaseCertificateInput {
  { product_status, rehearsal_report }
}

///|
pub fn release_finding(
  gate : ReleaseGate,
  status : ReleaseStatus,
  evidence_path : String,
  message : String,
) -> ReleaseFinding {
  { gate, status, evidence_path, message }
}

///|
fn tool_declared(
  status : @product.MoonfishProductStatus,
  name : String,
) -> Bool {
  status.pack_manifest.tools.any(fn(tool) { tool.name == name })
}

///|
fn schema_declared(
  status : @product.MoonfishProductStatus,
  id : String,
) -> Bool {
  status.pack_manifest.schemas.any(fn(schema) { schema.id == id })
}

///|
fn rehearsal_pass(
  report : @product.ProductRehearsalReport,
  check : @product.ProductRehearsalCheck,
) -> Bool {
  report.findings.any(fn(finding) {
    finding.check == check && finding.status is RehearsalPass
  })
}

///|
fn decommission_pass(
  status : @product.MoonfishProductStatus,
  gate : @decommission.DecommissionGate,
) -> Bool {
  status.decommission_report.findings.any(fn(finding) {
    finding.gate == gate && finding.status is DecommissionPass
  })
}

///|
fn product_status_finding(
  status : @product.MoonfishProductStatus,
) -> ReleaseFinding {
  if status.acceptance_report.passed &&
    status.readiness_report.ready &&
    status.cutover_plan.ready_to_cutover &&
    status.handoff_report.ready &&
    status.decommission_report.ready {
    release_finding(
      ProductStatusReady,
      ReleasePass,
      "records/product-status/\{status.workspace.bundle.request.run_id}.json",
      "product status proves acceptance, readiness, cutover, handoff, and decommission gates",
    )
  } else {
    release_finding(
      ProductStatusReady,
      ReleaseBlocked,
      "product-status",
      "product status still has blocked launch gates",
    )
  }
}

///|
fn rehearsal_finding(
  report : @product.ProductRehearsalReport,
) -> ReleaseFinding {
  if report.ready &&
    rehearsal_pass(report, DailyWorkflowExecutable) &&
    rehearsal_pass(report, LegacyReadOnlyBoundary) &&
    rehearsal_pass(report, ExportReplayEvidence) {
    release_finding(
      RehearsalReady,
      ReleasePass,
      "records/rehearsals/\{report.run_id}.json",
      "daily workflow, export/replay, and legacy read-only rehearsal checks pass",
    )
  } else {
    release_finding(
      RehearsalReady,
      ReleaseBlocked,
      "product-rehearsal-report",
      "product rehearsal is incomplete",
    )
  }
}

///|
fn suite_pack_finding(
  status : @product.MoonfishProductStatus,
) -> ReleaseFinding {
  if status.pack_manifest.id == "price-action" &&
    schema_declared(status, "release-certificate-input") &&
    schema_declared(status, "release-certificate") &&
    tool_declared(status, "prepare_release_certificate") &&
    tool_declared(status, "prepare_product_rehearsal_report") &&
    tool_declared(status, "prepare_moonfish_product_status") {
    release_finding(
      SuitePackComplete,
      ReleasePass,
      "pack-manifest",
      "suite pack declares product status, rehearsal, and release certificate contracts",
    )
  } else {
    release_finding(
      SuitePackComplete,
      ReleaseBlocked,
      "pack-manifest",
      "suite pack is missing release-time schemas or tools",
    )
  }
}

///|
fn historical_records_finding(
  status : @product.MoonfishProductStatus,
) -> ReleaseFinding {
  if status.import_projection.record_count > 0 &&
    status.import_projection.review_count == 0 &&
    status.import_projection.records.all(fn(record) {
      record.replay_anchor_path != ""
    }) &&
    status.workspace.history.total_count > 0 &&
    status.workspace.replay_plan.can_replay {
    release_finding(
      HistoricalRecordsAvailable,
      ReleasePass,
      "records/imports/\{status.import_projection.batch_id}.json",
      "imported records and current workspace history are inspectable and replayable",
    )
  } else {
    release_finding(
      HistoricalRecordsAvailable,
      ReleaseBlocked,
      "legacy-import-projection",
      "historical records or replay anchors are missing",
    )
  }
}

///|
fn legacy_archive_finding(
  status : @product.MoonfishProductStatus,
) -> ReleaseFinding {
  if status.decommission_report.legacy_app_ref == "../paa" &&
    decommission_pass(status, LegacyReadOnly) &&
    decommission_pass(status, ArchiveManifestReady) &&
    status.decommission_report.archive_items.any(fn(item) {
      item.kind is LegacySourceReference && item.retained
    }) {
    release_finding(
      LegacyAppArchived,
      ReleasePass,
      "../paa",
      "PA Agent is archived as a read-only rollback and audit reference, not an active runtime dependency",
    )
  } else {
    release_finding(
      LegacyAppArchived,
      ReleaseBlocked,
      "../paa",
      "legacy PA Agent archive or read-only boundary is incomplete",
    )
  }
}

///|
fn export_evidence_finding(
  status : @product.MoonfishProductStatus,
  report : @product.ProductRehearsalReport,
) -> ReleaseFinding {
  if status.workspace.export_manifest.item_count > 0 &&
    status.workspace.export_manifest.destination_path != "" &&
    !status.workspace.export_manifest.items.any(fn(item) {
      item.required && item.path == ""
    }) &&
    rehearsal_pass(report, ExportReplayEvidence) {
    release_finding(
      ExportEvidenceReady,
      ReleasePass,
      status.workspace.export_manifest.destination_path,
      "launch evidence can be exported with required replay and operator artifacts",
    )
  } else {
    release_finding(
      ExportEvidenceReady,
      ReleaseBlocked,
      "export-manifest",
      "release export evidence is incomplete",
    )
  }
}

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

///|
pub fn prepare_release_certificate(
  input : ReleaseCertificateInput,
) -> ReleaseCertificate {
  let status = input.product_status
  let rehearsal = input.rehearsal_report
  let findings = [
    product_status_finding(status),
    rehearsal_finding(rehearsal),
    suite_pack_finding(status),
    historical_records_finding(status),
    legacy_archive_finding(status),
    export_evidence_finding(status, rehearsal),
  ]
  let blocked_count = count_status(findings, ReleaseBlocked)
  let pass_count = count_status(findings, ReleasePass)
  let ready = blocked_count == 0
  let run_id = status.workspace.bundle.request.run_id
  {
    ready,
    run_id,
    release_path: "records/releases/\{run_id}.json",
    pass_count,
    blocked_count,
    findings,
    summary: if ready {
      "release certificate ready with \{pass_count}/\{findings.length()} gate(s); Moonfish is the standalone primary workflow"
    } else {
      "release certificate blocked by \{blocked_count} gate(s)"
    },
  }
}