///|
pub enum FileOffsetKind {
  ModuleHeader
  SectionHeader
  SectionPayload
  CodeCount
  FunctionSizePrefix
  FunctionBodyBytes
  DataEntryCount
  DataEntryMetadata
  DataPayload
} derive(Eq, Debug)

///|
/// A half-open file range and the query's byte position within that range.
pub struct FileOffsetRegion {
  kind : FileOffsetKind
  file_offset : Int
  size : Int
  relative_offset : Int
} derive(Eq, Debug)

///|
pub struct FileOffsetReport {
  file_size : Int
  sha256 : String
  offset : Int
  section_index : Int?
  section : Section?
  function : FunctionStat?
  data_segment : DataSegment?
  region : FileOffsetRegion
  scope : String
}

///|
pub enum FileOffsetError {
  FileOffsetOutOfRange(Int, Int)
  FileOffsetInputError(WasmError)
  InconsistentFileOffsetRange(Int, String)
}

///|
pub fn describe_file_offset_error(error : FileOffsetError) -> String {
  match error {
    FileOffsetOutOfRange(offset, size) =>
      "file offset \{offset} is outside [0, \{size}); EOF has no byte"
    FileOffsetInputError(error) => describe_error(error)
    InconsistentFileOffsetRange(offset, reason) =>
      "inconsistent file range ownership for offset \{offset}: " + reason
  }
}

///|
priv struct FileOffsetPart {
  kind : FileOffsetKind
  range : DataByteRange
  function : FunctionStat?
  data_segment : DataSegment?
}

///|
fn file_offset_range(
  file_size : Int,
  start : Int,
  size : Int,
  query : Int,
) -> Result[DataByteRange, FileOffsetError] {
  if start < 0 || size < 0 || start > file_size || size > file_size - start {
    return Err(InconsistentFileOffsetRange(query, "range exceeds input"))
  }
  Ok({ offset: start, size, })
}

///|
fn file_offset_contains(range : DataByteRange, query : Int) -> Bool {
  query >= range.offset && query - range.offset < range.size
}

///|
/// Both ranges have already been checked against the input.
fn file_offset_within(inner : DataByteRange, outer : DataByteRange) -> Bool {
  inner.offset >= outer.offset &&
  inner.offset - outer.offset <= outer.size &&
  inner.size <= outer.size - (inner.offset - outer.offset)
}

///|
/// Require a contiguous partition, including zero-sized bodies/payloads. Never
/// fall back to section_payload on a gap, overlap or missing detailed owner.
fn file_offset_validate_partition(
  parts : Array[FileOffsetPart],
  parent : DataByteRange,
  file_size : Int,
  query : Int,
) -> Result[Unit, FileOffsetError] {
  let mut cursor = parent.offset
  for part in parts {
    let range = match
      file_offset_range(file_size, part.range.offset, part.range.size, query) {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    if range.offset != cursor || !file_offset_within(range, parent) {
      return Err(
        InconsistentFileOffsetRange(query, "non-contiguous detailed ranges"),
      )
    }
    cursor = range.offset + range.size
  }
  if cursor - parent.offset != parent.size {
    return Err(
      InconsistentFileOffsetRange(query, "detailed ranges do not cover payload"),
    )
  }
  Ok(())
}

///|
fn file_offset_partition(
  parts : Array[FileOffsetPart],
  parent : DataByteRange,
  file_size : Int,
  query : Int,
) -> Result[FileOffsetPart, FileOffsetError] {
  match file_offset_validate_partition(parts, parent, file_size, query) {
    Err(error) => return Err(error)
    Ok(_) => ()
  }
  for part in parts {
    if file_offset_contains(part.range, query) {
      return Ok(part)
    }
  }
  Err(InconsistentFileOffsetRange(query, "no detailed owner"))
}

///|
fn file_offset_code_parts(
  data : Bytes,
  parsed : WasmModule,
  section : Section,
  payload : DataByteRange,
  query : Int,
) -> Result[Array[FileOffsetPart], FileOffsetError] {
  let stats = match collect_function_stats(parsed, data) {
    Ok(value) => value
    Err(error) => return Err(FileOffsetInputError(error))
  }
  let reader = match reader_at_payload(data, section) {
    Ok(value) => value
    Err(error) => return Err(FileOffsetInputError(error))
  }
  let count = match read_u32_as_int(reader) {
    Ok(value) => value
    Err(error) => return Err(FileOffsetInputError(error))
  }
  if count != stats.length() {
    return Err(
      InconsistentFileOffsetRange(
        query, "code count differs from local definitions",
      ),
    )
  }
  let parts : Array[FileOffsetPart] = [
    {
      kind: CodeCount,
      range: {
        offset: payload.offset,
        size: reader.position() - payload.offset,
      },
      function: None,
      data_segment: None,
    },
  ]
  for stat in stats {
    let encoded = match
      file_offset_range(data.length(), stat.offset, stat.total_size, query) {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    if !file_offset_within(encoded, payload) ||
      stat.body_size < 0 ||
      stat.body_size >= stat.total_size {
      return Err(
        InconsistentFileOffsetRange(query, "invalid local function range"),
      )
    }
    let prefix = stat.total_size - stat.body_size
    parts.push({
      kind: FunctionSizePrefix,
      range: { offset: stat.offset, size: prefix, },
      function: Some(stat),
      data_segment: None,
    })
    parts.push({
      kind: FunctionBodyBytes,
      range: { offset: stat.offset + prefix, size: stat.body_size, },
      function: Some(stat),
      data_segment: None,
    })
  }
  match file_offset_validate_partition(parts, payload, data.length(), query) {
    Err(error) => Err(error)
    Ok(_) => Ok(parts)
  }
}

///|
fn file_offset_data_parts(
  data : Bytes,
  section : Section,
  payload : DataByteRange,
  query : Int,
) -> Result[Array[FileOffsetPart], FileOffsetError] {
  let details = match inspect_data_segments(data) {
    Ok(value) => value
    Err(error) => return Err(FileOffsetInputError(error))
  }
  match details.section {
    Some(found) if found.offset == section.offset &&
      found.total_size == section.total_size => ()
    _ =>
      return Err(
        InconsistentFileOffsetRange(
          query, "data inspector selected another section",
        ),
      )
  }
  let parts : Array[FileOffsetPart] = [
    {
      kind: DataEntryCount,
      range: { offset: payload.offset, size: details.count_size, },
      function: None,
      data_segment: None,
    },
  ]
  for entry in details.entries {
    let encoded = match
      file_offset_range(
        data.length(),
        entry.encoding.offset,
        entry.encoding.size,
        query,
      ) {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    let content = match
      file_offset_range(
        data.length(),
        entry.payload.offset,
        entry.payload.size,
        query,
      ) {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    if !file_offset_within(encoded, payload) ||
      !file_offset_within(content, encoded) {
      return Err(
        InconsistentFileOffsetRange(query, "data entry escapes its owner"),
      )
    }
    let metadata_size = content.offset - encoded.offset
    if content.size != encoded.size - metadata_size || metadata_size < 2 {
      return Err(
        InconsistentFileOffsetRange(
          query, "data payload does not end at entry end",
        ),
      )
    }
    parts.push({
      kind: DataEntryMetadata,
      range: { offset: encoded.offset, size: metadata_size, },
      function: None,
      data_segment: Some(entry),
    })
    parts.push({
      kind: DataPayload,
      range: content,
      function: None,
      data_segment: Some(entry),
    })
  }
  match file_offset_validate_partition(parts, payload, data.length(), query) {
    Err(error) => Err(error)
    Ok(_) => Ok(parts)
  }
}

///|
/// Parse structural ranges, then refine only the selected code/data payload.
/// No function instruction decoding, graph construction or ordinary analysis.
pub fn locate_file_offset(
  data : Bytes,
  offset : Int,
) -> Result[FileOffsetReport, FileOffsetError] {
  let parsed = match parse_module(data) {
    Ok(value) => value
    Err(error) => return Err(FileOffsetInputError(error))
  }
  if offset < 0 || offset >= data.length() {
    return Err(FileOffsetOutOfRange(offset, data.length()))
  }
  let mut selected_section : (Int, Section)? = None
  let mut cursor = 8
  for ordinal, section in parsed.sections {
    let range = match
      file_offset_range(
        data.length(),
        section.offset,
        section.total_size,
        offset,
      ) {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    if range.offset != cursor ||
      section.payload_size < 0 ||
      section.payload_size > range.size ||
      range.size - section.payload_size < 2 {
      return Err(
        InconsistentFileOffsetRange(
          offset, "invalid physical section partition",
        ),
      )
    }
    if file_offset_contains(range, offset) {
      if selected_section is Some(_) {
        return Err(
          InconsistentFileOffsetRange(offset, "multiple physical sections"),
        )
      }
      selected_section = Some((ordinal, section))
    }
    cursor = range.offset + range.size
  }
  if cursor != data.length() {
    return Err(
      InconsistentFileOffsetRange(offset, "sections do not cover file"),
    )
  }
  let (section_index, section, part) = match selected_section {
    None if offset < 8 =>
      (
        None,
        None,
        FileOffsetPart::{
          kind: ModuleHeader,
          range: { offset: 0, size: 8, },
          function: None,
          data_segment: None,
        },
      )
    None =>
      return Err(InconsistentFileOffsetRange(offset, "no physical section"))
    Some((ordinal, section)) => {
      let header_size = section.total_size - section.payload_size
      let header = DataByteRange::{ offset: section.offset, size: header_size, }
      let payload = DataByteRange::{
        offset: section.offset + header_size,
        size: section.payload_size,
      }
      let part = if file_offset_contains(header, offset) {
        FileOffsetPart::{
          kind: SectionHeader,
          range: header,
          function: None,
          data_segment: None,
        }
      } else {
        match section.kind {
          Code =>
            match file_offset_code(data, parsed, section, payload, offset) {
              Ok(value) => value
              Err(error) => return Err(error)
            }
          Data =>
            match file_offset_data(data, section, payload, offset) {
              Ok(value) => value
              Err(error) => return Err(error)
            }
          _ =>
            FileOffsetPart::{
              kind: SectionPayload,
              range: payload,
              function: None,
              data_segment: None,
            }
        }
      }
      (Some(ordinal), Some(section), part)
    }
  }
  if !file_offset_contains(part.range, offset) {
    return Err(
      InconsistentFileOffsetRange(offset, "query outside selected region"),
    )
  }
  Ok({
    file_size: data.length(),
    sha256: experiment_sha256(data),
    offset,
    section_index,
    section,
    function: part.function,
    data_segment: part.data_segment,
    region: {
      kind: part.kind,
      file_offset: part.range.offset,
      size: part.range.size,
      relative_offset: offset - part.range.offset,
    },
    scope: "Original-file byte ownership with half-open ranges; physical section ordinals include custom sections, function indices include imports, data indices retain original order. Only selected code/data payloads are refined. Function bodies include locals and instructions without opcode decoding; other payload interiors remain undivided. data_count is the data entry count field, not the separate datacount section. Offset expressions are not evaluated. Base module checks scan all section/code ranges; selected code adds local stats and best-effort names, selected data reuses a full data inspection and its module checks. SHA-256 covers the whole input. This is not full Wasm semantic validation, a runtime address, source attribution or deletion benefit.",
  })
}

///|
fn file_offset_code(
  data : Bytes,
  parsed : WasmModule,
  section : Section,
  payload : DataByteRange,
  query : Int,
) -> Result[FileOffsetPart, FileOffsetError] {
  let parts = match
    file_offset_code_parts(data, parsed, section, payload, query) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  file_offset_partition(parts, payload, data.length(), query)
}

///|
fn file_offset_data(
  data : Bytes,
  section : Section,
  payload : DataByteRange,
  query : Int,
) -> Result[FileOffsetPart, FileOffsetError] {
  let parts = match file_offset_data_parts(data, section, payload, query) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  file_offset_partition(parts, payload, data.length(), query)
}