///|
pub(all) enum LegacySourceKind {
  LegacyAnalysisRecord
  LegacyPendingRecord
  LegacyExperienceEntry
  LegacyTradeLog
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) enum ImportDisposition {
  ImportReady
  NeedsReview
  SkippedUnsupported
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct LegacySourceRef {
  source_path : String
  kind : LegacySourceKind
  schema_version : String
  target_run_id : @domain.RunId?
  trace_id : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct LegacyImportBatchInput {
  batch_id : String
  source_root : String
  sources : Array[LegacySourceRef]
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct LegacyImportPlanItem {
  source : LegacySourceRef
  target_path : String
  target_schema_id : String
  artifact_kind : @book.BookArtifactKind
  disposition : ImportDisposition
  review_message : String?
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct LegacyImportPlan {
  batch_id : String
  source_root : String
  ready_count : Int
  review_count : Int
  skipped_count : Int
  items : Array[LegacyImportPlanItem]
  write_plan : @book.BookWritePlan
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) enum ImportedRecordKind {
  ImportedAnalysis
  ImportedPendingAnalysis
  ImportedExperience
  ImportedTrade
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct ImportedMoonBookRecord {
  trace_id : String
  source_path : String
  target_path : String
  schema_version : String
  schema_id : String
  kind : ImportedRecordKind
  title : String
  inspectable_summary : String
  replay_anchor_path : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct LegacyImportProjection {
  batch_id : String
  record_count : Int
  review_count : Int
  records : Array[ImportedMoonBookRecord]
  review_paths : Array[String]
  write_plan : @book.BookWritePlan
  summary : String
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn legacy_source_ref(
  source_path : String,
  kind : LegacySourceKind,
  trace_id : String,
  schema_version? : String = "pa-agent-v1",
  target_run_id? : @domain.RunId,
) -> LegacySourceRef {
  { source_path, kind, schema_version, target_run_id, trace_id }
}

///|
pub fn legacy_import_batch_input(
  batch_id : String,
  source_root : String,
  sources : Array[LegacySourceRef],
) -> LegacyImportBatchInput {
  { batch_id, source_root, sources }
}

///|
fn source_key(source : LegacySourceRef) -> String {
  match source.target_run_id {
    Some(run_id) => run_id
    None => source.trace_id
  }
}

///|
fn target_schema_id(kind : LegacySourceKind) -> String {
  match kind {
    LegacyAnalysisRecord | LegacyPendingRecord => "analysis-record"
    LegacyExperienceEntry => "legacy-experience-entry"
    LegacyTradeLog => "legacy-trade-log"
  }
}

///|
fn artifact_kind(kind : LegacySourceKind) -> @book.BookArtifactKind {
  match kind {
    LegacyAnalysisRecord | LegacyPendingRecord => ImportedLegacyRecord
    LegacyExperienceEntry => ImportedExperienceMemory
    LegacyTradeLog => ImportedTradeLog
  }
}

///|
fn target_path(source : LegacySourceRef) -> String {
  let key = source_key(source)
  match source.kind {
    LegacyAnalysisRecord => "records/imported/analyses/\{key}.json"
    LegacyPendingRecord => "records/imported/pending/\{key}.json"
    LegacyExperienceEntry => "wiki/imported-experience/\{key}.json"
    LegacyTradeLog => "records/imported/trade-logs/\{key}.json"
  }
}

///|
fn disposition(source : LegacySourceRef) -> (ImportDisposition, String?) {
  if source.source_path == "" {
    (SkippedUnsupported, Some("source path is required"))
  } else if source.trace_id == "" {
    (SkippedUnsupported, Some("trace id is required for idempotent import"))
  } else if source.schema_version == "" {
    (NeedsReview, Some("legacy schema version is unknown"))
  } else {
    match source.kind {
      LegacyAnalysisRecord | LegacyPendingRecord =>
        match source.target_run_id {
          Some(_) => (ImportReady, None)
          None =>
            (
              NeedsReview,
              Some("analysis and pending records need a target run id"),
            )
        }
      LegacyExperienceEntry | LegacyTradeLog => (ImportReady, None)
    }
  }
}

///|
fn plan_item(source : LegacySourceRef) -> LegacyImportPlanItem {
  let (item_disposition, review_message) = disposition(source)
  {
    source,
    target_path: target_path(source),
    target_schema_id: target_schema_id(source.kind),
    artifact_kind: artifact_kind(source.kind),
    disposition: item_disposition,
    review_message,
  }
}

///|
fn count_disposition(
  items : Array[LegacyImportPlanItem],
  value : ImportDisposition,
) -> Int {
  items.fold(init=0, fn(count, item) {
    if item.disposition == value {
      count + 1
    } else {
      count
    }
  })
}

///|
fn import_manifest_ref(batch_id : String) -> @book.BookArtifactRef {
  @book.book_artifact_ref(
    batch_id,
    "records/imports/\{batch_id}.json",
    "legacy-import-plan",
    GeneratedImportManifest,
    "idempotent MoonBook import manifest for legacy PA Agent sources",
  )
}

///|
fn item_artifact_ref(
  batch_id : String,
  item : LegacyImportPlanItem,
) -> @book.BookArtifactRef {
  @book.book_artifact_ref(
    batch_id,
    item.target_path,
    item.target_schema_id,
    item.artifact_kind,
    "imported from \{item.source.source_path}",
  )
}

///|
fn source_kind_label(kind : LegacySourceKind) -> String {
  match kind {
    LegacyAnalysisRecord => "analysis"
    LegacyPendingRecord => "pending analysis"
    LegacyExperienceEntry => "experience memory"
    LegacyTradeLog => "trade log"
  }
}

///|
fn imported_kind(kind : LegacySourceKind) -> ImportedRecordKind {
  match kind {
    LegacyAnalysisRecord => ImportedAnalysis
    LegacyPendingRecord => ImportedPendingAnalysis
    LegacyExperienceEntry => ImportedExperience
    LegacyTradeLog => ImportedTrade
  }
}

///|
fn imported_record(
  source_root : String,
  item : LegacyImportPlanItem,
) -> ImportedMoonBookRecord {
  let source_path = "\{source_root}/\{item.source.source_path}"
  {
    trace_id: item.source.trace_id,
    source_path,
    target_path: item.target_path,
    schema_version: item.source.schema_version,
    schema_id: item.target_schema_id,
    kind: imported_kind(item.source.kind),
    title: "Imported PA Agent \{source_kind_label(item.source.kind)}",
    inspectable_summary: "Imported \{source_kind_label(item.source.kind)} from \{source_path} into \{item.target_path}",
    replay_anchor_path: "records/replays/imported/\{item.source.trace_id}.json",
  }
}

///|
fn review_path(item : LegacyImportPlanItem) -> String {
  "records/reviews/imports/\{item.source.trace_id}.json"
}

///|
fn imported_records(plan : LegacyImportPlan) -> Array[ImportedMoonBookRecord] {
  let records : Array[ImportedMoonBookRecord] = []
  for item in plan.items {
    if item.disposition is ImportReady {
      records.push(imported_record(plan.source_root, item))
    }
  }
  records
}

///|
fn import_review_paths(plan : LegacyImportPlan) -> Array[String] {
  let paths : Array[String] = []
  for item in plan.items {
    if item.disposition is NeedsReview || item.disposition is SkippedUnsupported {
      paths.push(review_path(item))
    }
  }
  paths
}

///|
fn write_plan(
  batch_id : String,
  items : Array[LegacyImportPlanItem],
) -> @book.BookWritePlan {
  let artifacts = [import_manifest_ref(batch_id)]
  for item in items {
    if item.disposition is ImportReady {
      artifacts.push(item_artifact_ref(batch_id, item))
    }
  }
  { run_id: batch_id, artifacts }
}

///|
pub fn prepare_legacy_import_plan(
  input : LegacyImportBatchInput,
) -> LegacyImportPlan {
  let items = input.sources.map(fn(source) { plan_item(source) })
  {
    batch_id: input.batch_id,
    source_root: input.source_root,
    ready_count: count_disposition(items, ImportReady),
    review_count: count_disposition(items, NeedsReview),
    skipped_count: count_disposition(items, SkippedUnsupported),
    items,
    write_plan: write_plan(input.batch_id, items),
  }
}

///|
pub fn materialize_legacy_import_projection(
  plan : LegacyImportPlan,
) -> LegacyImportProjection {
  let records = imported_records(plan)
  let review_paths = import_review_paths(plan)
  {
    batch_id: plan.batch_id,
    record_count: records.length(),
    review_count: review_paths.length(),
    records,
    review_paths,
    write_plan: plan.write_plan,
    summary: if review_paths.is_empty() {
      "legacy import projection materialized \{records.length()} inspectable record(s)"
    } else {
      "legacy import projection has \{review_paths.length()} review path(s)"
    },
  }
}