///|
pub(all) enum ReplayStepKind {
  LoadMarketDataPlan
  LoadMarketSnapshot
  LoadPromptPacket
  LoadModelExchange
  LoadRoutineEventLog
  LoadAnalysisProjection
  LoadStructureLevels
  LoadContextBudget
  LoadBookCommit
  LoadAnalysisRecord
  LoadExperienceMemorySelection
  LoadChartFrame
  LoadReviewQueue
  LoadImportedLegacyRecord
  LoadImportedExperienceMemory
  LoadImportedTradeLog
  LoadImportManifest
  RebuildFollowUpContext
  CompareProjection
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct ReplayStep {
  run_id : @domain.RunId
  kind : ReplayStepKind
  path : String
  schema_id : String
  required : Bool
  purpose : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct ReplayPlan {
  run_id : @domain.RunId
  status : @workflow.AnalysisRunStatus
  source_id : @domain.SourceId
  live_market_required : Bool
  can_replay : Bool
  blocking_reason : String?
  steps : Array[ReplayStep]
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn replay_step(
  run_id : @domain.RunId,
  kind : ReplayStepKind,
  path : String,
  schema_id : String,
  purpose : String,
  required? : Bool = true,
) -> ReplayStep {
  { run_id, kind, path, schema_id, required, purpose }
}

///|
fn step_kind_for_anchor(kind : @followup.FollowUpAnchorKind) -> ReplayStepKind {
  match kind {
    MarketDataPlan => LoadMarketDataPlan
    MarketSnapshot => LoadMarketSnapshot
    PromptPacket => LoadPromptPacket
    ModelExchange => LoadModelExchange
    RoutineEventLog => LoadRoutineEventLog
    AnalysisProjection => LoadAnalysisProjection
    StructureLevels => LoadStructureLevels
    ContextBudget => LoadContextBudget
    BookCommit => LoadBookCommit
    AnalysisRecord => LoadAnalysisRecord
    ExperienceMemorySelection => LoadExperienceMemorySelection
    ChartFrame => LoadChartFrame
    ReviewQueue => LoadReviewQueue
    ImportedLegacyRecord => LoadImportedLegacyRecord
    ImportedExperienceMemory => LoadImportedExperienceMemory
    ImportedTradeLog => LoadImportedTradeLog
    ImportManifest => LoadImportManifest
  }
}

///|
fn append_anchor_steps(
  steps : Array[ReplayStep],
  context : @followup.FollowUpContext,
) -> Unit {
  for anchor in context.anchors {
    steps.push(
      replay_step(
        context.run_id,
        step_kind_for_anchor(anchor.kind),
        anchor.path,
        anchor.schema_id,
        anchor.purpose,
      ),
    )
  }
}

///|
fn has_projection(context : @followup.FollowUpContext) -> Bool {
  context.anchors.any(fn(anchor) { anchor.kind is AnalysisProjection })
}

///|
pub fn prepare_replay_plan(bundle : @workflow.AnalysisRunBundle) -> ReplayPlan {
  let context = @followup.prepare_follow_up_context(bundle)
  let steps : Array[ReplayStep] = []
  append_anchor_steps(steps, context)
  steps.push(
    replay_step(
      context.run_id,
      RebuildFollowUpContext,
      "records/followups/\{context.run_id}.json",
      "followup-context",
      "rebuild follow-up context from durable analysis evidence",
    ),
  )
  let can_replay = bundle.status is Completed && has_projection(context)
  if can_replay {
    steps.push(
      replay_step(
        context.run_id,
        CompareProjection,
        "records/projections/\{context.run_id}.json",
        "dry-execution-projection",
        "compare regenerated deterministic projection against stored evidence",
      ),
    )
  }
  {
    run_id: context.run_id,
    status: bundle.status,
    source_id: context.source_id,
    live_market_required: false,
    can_replay,
    blocking_reason: if can_replay {
      None
    } else {
      Some("stored market snapshot and projection are not available")
    },
    steps,
  }
}