///|
pub(all) enum RecordIntegrityCheck {
  AnalysisRecordSelfContained
  WritePlanDeclaresRecord
  BookCommitReady
  ReplayAnchorsRecord
  ExportAnchorsRecord
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) enum RecordIntegrityStatus {
  Pass
  NeedsWork
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct RecordIntegrityFinding {
  run_id : @domain.RunId
  check : RecordIntegrityCheck
  status : RecordIntegrityStatus
  path : String
  message : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct RecordIntegrityReport {
  run_id : @domain.RunId
  ready : Bool
  artifact_count : Int
  findings : Array[RecordIntegrityFinding]
  summary : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn record_integrity_finding(
  run_id : @domain.RunId,
  check : RecordIntegrityCheck,
  status : RecordIntegrityStatus,
  path : String,
  message : String,
) -> RecordIntegrityFinding {
  { run_id, check, status, path, message }
}

///|
fn completed_record_finding(
  bundle : @workflow.AnalysisRunBundle,
) -> RecordIntegrityFinding {
  match bundle.projection {
    Some(projection) => {
      let record = projection.result.record
      let snapshot = record.snapshot
      if record.run_id == bundle.request.run_id &&
        snapshot.symbol == bundle.request.market.symbol &&
        snapshot.timeframe == bundle.request.market.timeframe &&
        record.stage1 is Some(_) &&
        record.stage2 is Some(_) &&
        record.validation.length() > 0 &&
        record.status is Persisted {
        record_integrity_finding(
          bundle.request.run_id,
          AnalysisRecordSelfContained,
          Pass,
          "records/analyses/\{bundle.request.run_id}.json",
          "analysis record carries run id, market snapshot, stage outputs, validation, and persisted status",
        )
      } else {
        record_integrity_finding(
          bundle.request.run_id,
          AnalysisRecordSelfContained,
          NeedsWork,
          "records/analyses/\{bundle.request.run_id}.json",
          "analysis record is missing self-contained run, market, stage, validation, or status evidence",
        )
      }
    }
    None =>
      record_integrity_finding(
        bundle.request.run_id,
        AnalysisRecordSelfContained,
        NeedsWork,
        "analysis-run-bundle.projection",
        "analysis projection is unavailable, so record integrity cannot be proven",
      )
  }
}

///|
fn artifact_declared(
  plan : @book.BookWritePlan,
  schema_id : String,
  path : String,
) -> Bool {
  plan.artifacts.any(fn(artifact) {
    artifact.schema_id == schema_id && artifact.path == path
  })
}

///|
fn write_plan_finding(
  bundle : @workflow.AnalysisRunBundle,
) -> RecordIntegrityFinding {
  match bundle.write_plan {
    Some(plan) => {
      let run_id = bundle.request.run_id
      if artifact_declared(
          plan,
          "analysis-record",
          "records/analyses/\{run_id}.json",
        ) &&
        artifact_declared(
          plan,
          "kline-snapshot",
          "raw/market-data/\{run_id}.json",
        ) {
        record_integrity_finding(
          run_id,
          WritePlanDeclaresRecord,
          Pass,
          "book-write-plan",
          "MoonBook write plan declares the analysis record and raw market snapshot",
        )
      } else {
        record_integrity_finding(
          run_id,
          WritePlanDeclaresRecord,
          NeedsWork,
          "book-write-plan",
          "MoonBook write plan is missing the analysis record or raw snapshot artifact",
        )
      }
    }
    None =>
      record_integrity_finding(
        bundle.request.run_id,
        WritePlanDeclaresRecord,
        NeedsWork,
        "book-write-plan",
        "MoonBook write plan is unavailable",
      )
  }
}

///|
fn ready_analysis_commit(
  report : @book.BookCommitReport,
  run_id : @domain.RunId,
) -> Bool {
  report.commits.any(fn(commit) {
    commit.artifact.schema_id == "analysis-record" &&
    commit.artifact.path == "records/analyses/\{run_id}.json" &&
    commit.status is ArtifactReady &&
    commit.idempotency_key ==
    "\{run_id}|analysis-record|records/analyses/\{run_id}.json"
  })
}

///|
fn book_commit_finding(
  bundle : @workflow.AnalysisRunBundle,
) -> RecordIntegrityFinding {
  match bundle.book_commit {
    Some(report) =>
      if report.status is CommitReady &&
        ready_analysis_commit(report, bundle.request.run_id) {
        record_integrity_finding(
          bundle.request.run_id,
          BookCommitReady,
          Pass,
          "book-commit-report",
          "analysis record commit is ready with a stable idempotency key",
        )
      } else {
        record_integrity_finding(
          bundle.request.run_id,
          BookCommitReady,
          NeedsWork,
          "book-commit-report",
          "analysis record commit is missing, blocked, or lacks a stable idempotency key",
        )
      }
    None =>
      record_integrity_finding(
        bundle.request.run_id,
        BookCommitReady,
        NeedsWork,
        "book-commit-report",
        "book commit report is unavailable",
      )
  }
}

///|
fn replay_finding(
  bundle : @workflow.AnalysisRunBundle,
) -> RecordIntegrityFinding {
  let plan = @replay.prepare_replay_plan(bundle)
  if plan.can_replay &&
    plan.steps.any(fn(step) {
      step.kind is LoadAnalysisRecord &&
      step.path == "records/analyses/\{bundle.request.run_id}.json"
    }) {
    record_integrity_finding(
      bundle.request.run_id,
      ReplayAnchorsRecord,
      Pass,
      "replay-plan",
      "replay plan anchors the durable analysis record",
    )
  } else {
    record_integrity_finding(
      bundle.request.run_id,
      ReplayAnchorsRecord,
      NeedsWork,
      "replay-plan",
      "replay plan does not anchor a replayable analysis record",
    )
  }
}

///|
fn export_finding(
  bundle : @workflow.AnalysisRunBundle,
) -> RecordIntegrityFinding {
  let manifest = @export.prepare_export_manifest(bundle)
  if manifest.items.any(fn(item) {
      item.schema_id == "analysis-record" &&
      item.path == "records/analyses/\{bundle.request.run_id}.json" &&
      item.required
    }) {
    record_integrity_finding(
      bundle.request.run_id,
      ExportAnchorsRecord,
      Pass,
      "export-manifest",
      "audit export includes the durable analysis record",
    )
  } else {
    record_integrity_finding(
      bundle.request.run_id,
      ExportAnchorsRecord,
      NeedsWork,
      "export-manifest",
      "audit export does not include the durable analysis record",
    )
  }
}

///|
fn report_ready(findings : Array[RecordIntegrityFinding]) -> Bool {
  findings.all(fn(finding) { finding.status is Pass })
}

///|
fn report_summary(report : RecordIntegrityReport) -> String {
  let pass_count = report.findings.fold(init=0, fn(count, finding) {
    if finding.status is Pass {
      count + 1
    } else {
      count
    }
  })
  if report.ready {
    "record integrity report passed \{pass_count}/\{report.findings.length()} checks with \{report.artifact_count} artifact(s)"
  } else {
    "record integrity report has \{report.findings.length() - pass_count} check(s) needing work"
  }
}

///|
fn artifact_count(bundle : @workflow.AnalysisRunBundle) -> Int {
  match bundle.write_plan {
    Some(plan) => plan.artifacts.length()
    None => 0
  }
}

///|
pub fn prepare_record_integrity_report(
  bundle : @workflow.AnalysisRunBundle,
) -> RecordIntegrityReport {
  let findings = [
    completed_record_finding(bundle),
    write_plan_finding(bundle),
    book_commit_finding(bundle),
    replay_finding(bundle),
    export_finding(bundle),
  ]
  let report : RecordIntegrityReport = {
    run_id: bundle.request.run_id,
    ready: report_ready(findings),
    artifact_count: artifact_count(bundle),
    findings,
    summary: "",
  }
  { ..report, summary: report_summary(report) }
}