///|
/// 失败发生的检查阶段;读取失败不能仅凭 SqliteError 判断为格式损坏。
/// 失败时实际执行的检查阶段;不从错误文本猜测阶段或位置。
pub(all) enum DiagnosticPhase {
  Configuration
  ReadPage
  PageLayout
  TreeTraversal
  CellPayload
  OverflowChain
  RecordDecode
  SchemaDecode
  FreelistCheck
  PtrmapCheck
  PageOwnershipCheck
} derive(Debug, Eq)

///|
pub extend DiagnosticPhase with @debug.Debug::{to_repr}

///|
pub extend DiagnosticPhase with Eq::{equal, not_equal}

///|
/// 偏移从页面起点计数,下标从零计数;不能确定位置时保留 None。
/// 可用的失败位置。页号从 1 开始,byte_offset 相对页面,cell_index 从 0 开始;未知字段为 None,初始化失败不产生该报告。
pub(all) struct DiagnosticLocation {
  phase : DiagnosticPhase
  page_number : Int?
  byte_offset : Int?
  cell_index : Int?
  page_diagnostic : PageDiagnostic?
} derive(Debug)

///|
pub extend DiagnosticLocation with @debug.Debug::{to_repr}

///|
/// 单树检查与可用的失败位置;inspection 的状态语义与原同步报告一致。
pub(all) struct BTreeInspectionDetails {
  inspection : BTreeInspection
  location : DiagnosticLocation?
} derive(Debug)

///|
pub extend BTreeInspectionDetails with @debug.Debug::{to_repr}

///|
/// 全库问题及观察到的位置;缺失位置不代表页 0,不从根页或字符串推断。
pub(all) struct LocatedDatabaseIssue {
  issue : DatabaseIssue
  location : DiagnosticLocation?
} derive(Debug)

///|
pub extend LocatedDatabaseIssue with @debug.Debug::{to_repr}

///|
/// issues 与 inspection.issues 同序同长度,位置来自同一次检查,不重新读取源。
/// 全库报告与按问题顺序对应的位置;不会额外重读数据库。
pub(all) struct DatabaseInspectionDetails {
  inspection : DatabaseInspection
  issues : Array[LocatedDatabaseIssue]
} derive(Debug)

///|
pub extend DatabaseInspectionDetails with @debug.Debug::{to_repr}

///|
priv struct InspectionTrace {
  mut phase : DiagnosticPhase
  mut page_number : Int?
  mut byte_offset : Int?
  mut cell_index : Int?
  mut page_diagnostic : PageDiagnostic?
}

///|
fn InspectionTrace::new() -> InspectionTrace {
  {
    phase: Configuration,
    page_number: None,
    byte_offset: None,
    cell_index: None,
    page_diagnostic: None,
  }
}

///|
fn InspectionTrace::at(
  self : InspectionTrace,
  phase : DiagnosticPhase,
  page_number : Int?,
  byte_offset? : Int? = None,
  cell_index? : Int? = None,
) -> Unit {
  self.phase = phase
  self.page_number = page_number
  self.byte_offset = byte_offset
  self.cell_index = cell_index
  self.page_diagnostic = None
}

///|
fn InspectionTrace::snapshot(self : InspectionTrace) -> DiagnosticLocation {
  {
    phase: self.phase,
    page_number: self.page_number,
    byte_offset: self.byte_offset,
    cell_index: self.cell_index,
    page_diagnostic: self.page_diagnostic,
  }
}

///|
/// 与 inspect_btree 使用相同扫描和状态语义,额外保留失败上下文。
/// 返回单树报告及实际失败位置,不进行第二次扫描;位置未知为 None。
pub fn Database::inspect_btree_details(
  self : Database,
  root : Int,
  limit? : Int = self.limits.max_rows,
  max_total_payload_bytes? : UInt64 = 67108864UL,
) -> BTreeInspectionDetails {
  let trace = InspectionTrace::new()
  let observer : ScanObserver = {
    claim: (_, _, _) => (),
    charge_payload: _ => (),
    trace,
  }
  let mut records_decoded = 0
  try
    self.scan_btree_observed(
      root,
      _ => {
        records_decoded = records_decoded + 1
        true
      },
      observer,
      limit~,
      max_total_payload_bytes~,
    )
  catch {
    error =>
      {
        inspection: inspection_failure(root, records_decoded, error),
        location: Some(trace.snapshot()),
      }
  } noraise {
    summary =>
      {
        inspection: {
          root_page: root,
          status: if summary.completion == Complete {
            InspectionStatus::Complete
          } else {
            Incomplete
          },
          records_decoded,
          summary: Some(summary),
          error: None,
        },
        location: None,
      }
  }
}

///|
/// 一次全库检查同时保留每个问题的可用位置,不从错误字符串猜测;语义与 inspect_database 相同。
pub fn Database::inspect_database_details(
  self : Database,
  max_total_payload_bytes? : UInt64 = 67108864UL,
  max_issues? : Int = 100,
) -> DatabaseInspectionDetails {
  let issues : Array[LocatedDatabaseIssue] = []
  let inspection = self.inspect_database_traced(
    issues,
    max_total_payload_bytes~,
    max_issues~,
  )
  { inspection, issues, }
}