///|
/// The document formats supported by the `office` facade.
pub(all) enum DocumentFormat {
  Xlsx
  Docx
} derive(Debug, Eq)

///|
/// A deterministic, lowercase name suitable for CLI and JSON output.
pub fn DocumentFormat::name(self : DocumentFormat) -> String {
  match self {
    Xlsx => "xlsx"
    Docx => "docx"
  }
}

///|
pub impl Show for DocumentFormat with fn output(self, logger) {
  logger.write_string(self.name())
}

///|
/// Errors produced while identifying a supported Office package.
pub(all) suberror OfficeError {
  UnsupportedFileExtension(String)
  InvalidPackage(String)
  FormatMismatch(expected~ : DocumentFormat, actual~ : DocumentFormat)
  ResourceLimit(kind~ : String, limit~ : Int, actual~ : Int)
  Cancelled
} derive(Debug, Eq)

///|
pub impl Show for OfficeError with fn output(self, logger) {
  match self {
    UnsupportedFileExtension(path) =>
      logger.write_string(
        "unsupported file extension for '\{path}' (expected .xlsx or .docx)",
      )
    InvalidPackage(message) =>
      logger.write_string("invalid Office package: " + message)
    FormatMismatch(expected~, actual~) =>
      logger.write_string(
        "file extension says \{expected.name()}, but package content is \{actual.name()}",
      )
    ResourceLimit(kind~, limit~, actual~) =>
      logger.write_string(
        "Office \{kind} exceeds limit \{limit} (actual \{actual})",
      )
    Cancelled => logger.write_string("Office format detection was cancelled")
  }
}

///|
const DEFAULT_DETECT_MAX_PACKAGE_BYTES : Int = 64 * 1024 * 1024

///|
const DEFAULT_DETECT_MAX_ARCHIVE_ENTRIES : Int = 4096

///|
const DEFAULT_DETECT_MAX_ENTRY_BYTES : Int = 32 * 1024 * 1024

///|
const DEFAULT_DETECT_MAX_TOTAL_BYTES : Int = 128 * 1024 * 1024

///|
const DEFAULT_DETECT_MAX_XML_PART_BYTES : Int = 16 * 1024 * 1024

///|
const DEFAULT_DETECT_MAX_XML_TOTAL_UNITS : Int = 64 * 1024 * 1024

///|
const XLSX_MAIN_CONTENT_TYPE : String = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"

///|
const DOCX_MAIN_CONTENT_TYPE : String = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"

///|
const CONTENT_TYPES_ROOT : String = "{http://schemas.openxmlformats.org/package/2006/content-types}Types"

///|
const CONTENT_TYPES_OVERRIDE : String = "{http://schemas.openxmlformats.org/package/2006/content-types}Override"

///|
const CONTENT_TYPES_DEFAULT : String = "{http://schemas.openxmlformats.org/package/2006/content-types}Default"

///|
const RELATIONSHIPS_ROOT : String = "{http://schemas.openxmlformats.org/package/2006/relationships}Relationships"

///|
const RELATIONSHIP_ELEMENT : String = "{http://schemas.openxmlformats.org/package/2006/relationships}Relationship"

///|
const RELATIONSHIPS_CONTENT_TYPE : String = "application/vnd.openxmlformats-package.relationships+xml"

///|
/// ECMA-376 Part 2 §7.3.6 limits a Central Directory File Header, excluding
/// its four-byte ZIP signature, to 65,535 bytes.
const MAX_OPC_CENTRAL_DIRECTORY_FILE_HEADER_BYTES : Int = 65_535

///|
const TRANSITIONAL_OFFICE_DOCUMENT_REL : String = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"

///|
const STRICT_OFFICE_DOCUMENT_REL : String = "http://purl.oclc.org/ooxml/officeDocument/relationships/officeDocument"

///|
const TRANSITIONAL_WORKBOOK_ROOT : String = "{http://schemas.openxmlformats.org/spreadsheetml/2006/main}workbook"

///|
const STRICT_WORKBOOK_ROOT : String = "{http://purl.oclc.org/ooxml/spreadsheetml/main}workbook"

///|
const TRANSITIONAL_DOCUMENT_ROOT : String = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}document"

///|
const STRICT_DOCUMENT_ROOT : String = "{http://purl.oclc.org/ooxml/wordprocessingml/main}document"

///|
fn media_type_equal(left : StringView, right : StringView) -> Bool {
  left.to_owned().to_lower() == right.to_owned().to_lower()
}

///|
fn is_media_type_token_char(char : Char) -> Bool {
  char
  is ('a'..='z'
  | 'A'..='Z'
  | '0'..='9'
  | '!'
  | '#'
  | '$'
  | '%'
  | '&'
  | '\''
  | '*'
  | '+'
  | '-'
  | '.'
  | '^'
  | '_'
  | '`'
  | '|'
  | '~')
}

///|
fn is_media_type_quoted_char(char : Char) -> Bool {
  char
  is ('\t'
  | ' '
  | '\u{21}'
  | '\u{23}'..='\u{5b}'
  | '\u{5d}'..='\u{7e}'
  | '\u{80}'..='\u{ff}')
}

///|
fn is_media_type_quoted_pair_char(char : Char) -> Bool {
  char is ('\t' | ' ' | '\u{21}'..='\u{7e}' | '\u{80}'..='\u{ff}')
}

///|
// RFC 7230 token and quoted-string rules, composed as the RFC 7231 media-type
// grammar. OPC uses this syntax for every Default and Override declaration.
fn is_valid_media_type(value : StringView) -> Bool {
  let chars = value.to_array()
  let mut index = 0
  while index < chars.length() && is_media_type_token_char(chars[index]) {
    index = index + 1
  }
  if index == 0 || index >= chars.length() || chars[index] != '/' {
    return false
  }
  index = index + 1
  let subtype_start = index
  while index < chars.length() && is_media_type_token_char(chars[index]) {
    index = index + 1
  }
  if index == subtype_start {
    return false
  }
  while index < chars.length() {
    while index < chars.length() && chars[index] is (' ' | '\t') {
      index = index + 1
    }
    if index >= chars.length() || chars[index] != ';' {
      return false
    }
    index = index + 1
    while index < chars.length() && chars[index] is (' ' | '\t') {
      index = index + 1
    }
    let parameter_start = index
    while index < chars.length() && is_media_type_token_char(chars[index]) {
      index = index + 1
    }
    if index == parameter_start ||
      index >= chars.length() ||
      chars[index] != '=' {
      return false
    }
    index = index + 1
    if index < chars.length() && chars[index] == '"' {
      index = index + 1
      let mut closed = false
      while index < chars.length() {
        match chars[index] {
          '"' => {
            closed = true
            index = index + 1
            break
          }
          '\\' => {
            index = index + 1
            if index >= chars.length() ||
              !is_media_type_quoted_pair_char(chars[index]) {
              return false
            }
          }
          char => if !is_media_type_quoted_char(char) { return false }
        }
        index = index + 1
      }
      if !closed {
        return false
      }
    } else {
      let value_start = index
      while index < chars.length() && is_media_type_token_char(chars[index]) {
        index = index + 1
      }
      if index == value_start {
        return false
      }
    }
  }
  true
}

///|
fn main_format_from_content_type(content_type : StringView) -> DocumentFormat? {
  if media_type_equal(content_type, XLSX_MAIN_CONTENT_TYPE) {
    Some(Xlsx)
  } else if media_type_equal(content_type, DOCX_MAIN_CONTENT_TYPE) {
    Some(Docx)
  } else {
    None
  }
}

///|
fn xml_element_has_child_element(element : @xml.XmlElement) -> Bool {
  element.children.any(child => child is XmlElement(_))
}

///|
fn unexpected_xml_attribute(
  element : @xml.XmlElement,
  allowed : ArrayView[String],
) -> String? {
  for name, _ in element.attributes {
    if !allowed.any(expected => expected == name) {
      return Some(name)
    }
  }
  None
}

///|
fn format_from_extension(path : StringView) -> DocumentFormat raise OfficeError {
  let lower = path.to_owned().to_lower()
  if lower.has_suffix(".xlsx") {
    Xlsx
  } else if lower.has_suffix(".docx") {
    Docx
  } else {
    raise UnsupportedFileExtension(path.to_owned())
  }
}

///|
fn opc_part_key(name : StringView) -> String {
  @opc.part_name_key(name)
}

///|
priv struct ArchivePartIndex {
  exact : StableStringMap[BytesView]
  canonical : StableStringMap[BytesView]
}

///|
priv struct DecodedXmlPart {
  text : String
  source_encoding : @xml.XmlSourceEncoding
}

///|
/// Shared fail-closed state for one format-detection pass. XML trees are
/// cached so repeated identity and structural checks do not parse or
/// materialize the same package metadata more than once.
priv struct OfficeFormatContext {
  max_xml_part_bytes : Int
  xml_work_limit : Int
  xml_budget : @xml.XmlReadBudget
  cancelled : () -> Bool
  xml_parts : StableStringMap[@xml.XmlElement]
  relationship_parts : StableStringMap[@xml.XmlElement]
}

///|
fn bounded_scale(value : Int, factor : Int) -> Int {
  if value <= 0 || factor <= 0 {
    0
  } else {
    let scaled = value.to_int64() * factor.to_int64()
    if scaled > 2147483647L {
      2147483647
    } else {
      scaled.to_int()
    }
  }
}

///|
fn OfficeFormatContext::new(
  max_xml_part_bytes : Int,
  max_xml_total_units : Int,
  cancelled? : () -> Bool = () => false,
) -> OfficeFormatContext {
  let xml_work_limit = bounded_scale(max_xml_total_units, 4)
  {
    max_xml_part_bytes: if max_xml_part_bytes > 0 {
      max_xml_part_bytes
    } else {
      0
    },
    xml_work_limit,
    xml_budget: @xml.xml_read_budget(
      max_source_units=if max_xml_total_units > 0 {
        max_xml_total_units
      } else {
        0
      },
      max_tokens=xml_work_limit,
      max_materialized_chars=xml_work_limit,
      max_token_chars=if max_xml_part_bytes > 0 {
        max_xml_part_bytes
      } else {
        0
      },
      cancelled~,
    ),
    cancelled,
    xml_parts: SortedMap([]),
    relationship_parts: SortedMap([]),
  }
}

///|
fn OfficeFormatContext::checkpoint(
  self : OfficeFormatContext,
) -> Unit raise OfficeError {
  if (self.cancelled)() {
    raise Cancelled
  }
}

///|
fn archive_entry_logical_part_name(name : StringView) -> String? {
  if name == "[Content_Types].xml" {
    Some("[Content_Types].xml")
  } else {
    @opc.logical_part_name_from_zip_item_name(name)
  }
}

///|
fn archive_part_index(
  archive : @zip.Archive,
  context : OfficeFormatContext,
) -> ArchivePartIndex raise OfficeError {
  let exact : StableStringMap[BytesView] = SortedMap([])
  let canonical : StableStringMap[BytesView] = SortedMap([])
  let physical : StableStringMap[Bool] = SortedMap([])
  for entry in archive.entries() {
    context.checkpoint()
    let physical_name = entry.name()
    if physical.contains(physical_name) {
      raise InvalidPackage("duplicate entry name: \{physical_name}")
    }
    physical[physical_name] = true
    if physical_name.to_lower() == "[content_types].xml" &&
      physical_name != "[Content_Types].xml" {
      raise InvalidPackage(
        "reserved content-types manifest has invalid physical spelling: \{physical_name}",
      )
    }
    let logical_name = archive_entry_logical_part_name(physical_name)
    guard logical_name is Some(name) else { continue }
    if opc_part_key(name) == opc_part_key("[Content_Types].xml") &&
      physical_name != "[Content_Types].xml" {
      raise InvalidPackage(
        "reserved content-types manifest has invalid physical spelling: \{physical_name}",
      )
    }
    if exact.contains(name) {
      raise InvalidPackage("duplicate logical part name: \{name}")
    }
    exact[name] = entry.data()
    let key = opc_part_key(name)
    if canonical.contains(key) {
      raise InvalidPackage("duplicate entry name: \{key}")
    }
    canonical[key] = entry.data()
  }
  { exact, canonical }
}

///|
fn ArchivePartIndex::find(self : ArchivePartIndex, name : String) -> BytesView? {
  match self.exact.get(name) {
    Some(bytes) => Some(bytes)
    None => self.canonical.get(opc_part_key(name))
  }
}

///|
fn decode_xml_part(
  parts : ArchivePartIndex,
  name : String,
  context : OfficeFormatContext,
) -> DecodedXmlPart raise OfficeError {
  context.checkpoint()
  let bytes = match parts.find(name) {
    Some(bytes) => bytes
    None => raise InvalidPackage("missing required part: \{name}")
  }
  if bytes.length() > context.max_xml_part_bytes {
    raise ResourceLimit(
      kind="xml_part_bytes",
      limit=context.max_xml_part_bytes,
      actual=bytes.length(),
    )
  }
  let (text, source_encoding) : (String, @xml.XmlSourceEncoding) = match bytes {
    [0xff, 0xfe, ..] =>
      (
        @utf16.decode(bytes, ignore_bom=true) catch {
          _ =>
            raise InvalidPackage(
              "\{name} is not valid UTF-8 or UTF-16 XML text",
            )
        },
        Utf16LittleEndianWithBom,
      )
    [0xfe, 0xff, ..] =>
      (
        @utf16.decode(bytes, ignore_bom=true, endianness=Big) catch {
          _ =>
            raise InvalidPackage(
              "\{name} is not valid UTF-8 or UTF-16 XML text",
            )
        },
        Utf16BigEndianWithBom,
      )
    [b'<', 0x00, ..] =>
      (
        @utf16.decode(bytes) catch {
          _ =>
            raise InvalidPackage(
              "\{name} is not valid UTF-8 or UTF-16 XML text",
            )
        },
        Utf16LittleEndian,
      )
    [0x00, b'<', ..] =>
      (
        @utf16.decode(bytes, endianness=Big) catch {
          _ =>
            raise InvalidPackage(
              "\{name} is not valid UTF-8 or UTF-16 XML text",
            )
        },
        Utf16BigEndian,
      )
    _ =>
      (
        @utf8.decode(bytes, ignore_bom=true) catch {
          _ =>
            raise InvalidPackage(
              "\{name} is not valid UTF-8 or UTF-16 XML text",
            )
        },
        Utf8,
      )
  }
  context.checkpoint()
  { text, source_encoding }
}

///|
fn read_xml_part(
  parts : ArchivePartIndex,
  name : String,
  context : OfficeFormatContext,
) -> @xml.XmlElement raise OfficeError {
  match context.xml_parts.get(opc_part_key(name)) {
    Some(value) => return value
    None => ()
  }
  let decoded = decode_xml_part(parts, name, context)
  let root = @xml.read_xml_string_strict_encoded_limited(
    decoded.text,
    decoded.source_encoding,
    context.xml_budget,
  ) catch {
    _ if (context.cancelled)() => raise Cancelled
    ResourceLimit(_) =>
      raise ResourceLimit(
        kind="xml_preflight_work_units",
        limit=context.xml_work_limit,
        actual=if context.xml_work_limit < 2147483647 {
          context.xml_work_limit + 1
        } else {
          context.xml_work_limit
        },
      )
    _ => raise InvalidPackage("\{name} is not well-formed XML")
  }
  context.checkpoint()
  context.xml_parts[opc_part_key(name)] = root
  root
}

///|
fn read_relationships_part(
  parts : ArchivePartIndex,
  name : String,
  context : OfficeFormatContext,
) -> @xml.XmlElement raise OfficeError {
  match context.relationship_parts.get(opc_part_key(name)) {
    Some(value) => return value
    None => ()
  }
  let decoded = decode_xml_part(parts, name, context)
  let root = @opc.read_opc_relationships_string_encoded_limited(
    decoded.text,
    decoded.source_encoding,
    context.xml_budget,
  ) catch {
    _ if (context.cancelled)() => raise Cancelled
    ResourceLimit(_) =>
      raise ResourceLimit(
        kind="xml_preflight_work_units",
        limit=context.xml_work_limit,
        actual=if context.xml_work_limit < 2147483647 {
          context.xml_work_limit + 1
        } else {
          context.xml_work_limit
        },
      )
    _ => raise InvalidPackage("\{name} is not well-formed XML")
  }
  context.checkpoint()
  context.relationship_parts[opc_part_key(name)] = root
  root
}

///|
fn has_uri_scheme(target : StringView) -> Bool {
  let mut index = 0
  for char in target {
    match char {
      ':' => return index > 0
      '/' | '?' | '#' => return false
      _ => ()
    }
    let valid = if index == 0 {
      char is ('a'..='z' | 'A'..='Z')
    } else {
      char is ('a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '-' | '.')
    }
    if !valid {
      return false
    }
    index = index + 1
  }
  false
}

///|
fn is_ascii_hex_digit(char : Char) -> Bool {
  char is ('0'..='9' | 'A'..='F' | 'a'..='f')
}

///|
fn is_uri_unreserved(char : Char) -> Bool {
  char is ('a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '.' | '_' | '~')
}

///|
fn is_uri_sub_delimiter(char : Char) -> Bool {
  char is ('!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=')
}

///|
fn is_uri_pchar(char : Char) -> Bool {
  is_uri_unreserved(char) || is_uri_sub_delimiter(char) || char is (':' | '@')
}

///|
fn is_uri_query_or_fragment_char(char : Char) -> Bool {
  is_uri_pchar(char) || char is ('/' | '?')
}

///|
fn is_uri_userinfo_char(char : Char) -> Bool {
  is_uri_unreserved(char) || is_uri_sub_delimiter(char) || char == ':'
}

///|
fn is_uri_reg_name_char(char : Char) -> Bool {
  is_uri_unreserved(char) || is_uri_sub_delimiter(char)
}

///|
fn is_iri_ucschar(char : Char) -> Bool {
  let code = char.to_int()
  (code >= 0xa0 && code <= 0xd7ff) ||
  (code >= 0xf900 && code <= 0xfdcf) ||
  (code >= 0xfdf0 && code <= 0xffef) ||
  (code >= 0x10000 && code <= 0x1fffd) ||
  (code >= 0x20000 && code <= 0x2fffd) ||
  (code >= 0x30000 && code <= 0x3fffd) ||
  (code >= 0x40000 && code <= 0x4fffd) ||
  (code >= 0x50000 && code <= 0x5fffd) ||
  (code >= 0x60000 && code <= 0x6fffd) ||
  (code >= 0x70000 && code <= 0x7fffd) ||
  (code >= 0x80000 && code <= 0x8fffd) ||
  (code >= 0x90000 && code <= 0x9fffd) ||
  (code >= 0xa0000 && code <= 0xafffd) ||
  (code >= 0xb0000 && code <= 0xbfffd) ||
  (code >= 0xc0000 && code <= 0xcfffd) ||
  (code >= 0xd0000 && code <= 0xdfffd) ||
  (code >= 0xe1000 && code <= 0xefffd)
}

///|
fn is_iri_private(char : Char) -> Bool {
  let code = char.to_int()
  (code >= 0xe000 && code <= 0xf8ff) ||
  (code >= 0xf0000 && code <= 0xffffd) ||
  (code >= 0x100000 && code <= 0x10fffd)
}

///|
fn is_iri_unreserved(char : Char) -> Bool {
  is_uri_unreserved(char) || is_iri_ucschar(char)
}

///|
fn is_iri_pchar(char : Char) -> Bool {
  is_iri_unreserved(char) || is_uri_sub_delimiter(char) || char is (':' | '@')
}

///|
fn is_iri_query_char(char : Char) -> Bool {
  is_iri_pchar(char) || is_iri_private(char) || char is ('/' | '?')
}

///|
fn is_iri_fragment_char(char : Char) -> Bool {
  is_iri_pchar(char) || char is ('/' | '?')
}

///|
fn is_iri_userinfo_char(char : Char) -> Bool {
  is_iri_unreserved(char) || is_uri_sub_delimiter(char) || char == ':'
}

///|
fn is_iri_reg_name_char(char : Char) -> Bool {
  is_iri_unreserved(char) || is_uri_sub_delimiter(char)
}

///|
fn is_valid_uri_chars(
  chars : ArrayView[Char],
  start : Int,
  end : Int,
  allowed : (Char) -> Bool,
) -> Bool {
  let mut index = start
  while index < end {
    match chars[index] {
      '%' => {
        if index + 2 >= end ||
          !is_ascii_hex_digit(chars[index + 1]) ||
          !is_ascii_hex_digit(chars[index + 2]) {
          return false
        }
        index = index + 3
      }
      char => {
        if !allowed(char) {
          return false
        }
        index = index + 1
      }
    }
  }
  true
}

///|
fn is_valid_ipv4_literal(
  chars : ArrayView[Char],
  start : Int,
  end : Int,
) -> Bool {
  let mut index = start
  let mut components = 0
  while index < end {
    if components == 4 {
      return false
    }
    let component_start = index
    let mut value = 0
    while index < end && chars[index].is_ascii_digit() {
      value = value * 10 + chars[index].to_int() - '0'.to_int()
      index = index + 1
    }
    let digits = index - component_start
    if digits == 0 ||
      digits > 3 ||
      value > 255 ||
      (digits > 1 && chars[component_start] == '0') {
      return false
    }
    components = components + 1
    if index == end {
      break
    }
    if chars[index] != '.' {
      return false
    }
    index = index + 1
  }
  components == 4
}

///|
// RFC 3986 IP-literal validation. A compressed `::` must replace at least one
// 16-bit group; an IPv4 tail occupies the final two groups.
fn is_valid_ipv6_literal(
  chars : ArrayView[Char],
  start : Int,
  end : Int,
) -> Bool {
  if start >= end {
    return false
  }
  let mut index = start
  let mut groups = 0
  let mut compressed = false
  if chars[index] == ':' {
    if index + 1 >= end || chars[index + 1] != ':' {
      return false
    }
    compressed = true
    index = index + 2
    if index == end {
      return true
    }
  }
  while index < end {
    let group_start = index
    let mut has_dot = false
    while index < end && chars[index] != ':' {
      if chars[index] == '.' {
        has_dot = true
      }
      index = index + 1
    }
    if has_dot {
      if index != end || !is_valid_ipv4_literal(chars, group_start, end) {
        return false
      }
      groups = groups + 2
    } else {
      let digits = index - group_start
      if digits == 0 || digits > 4 {
        return false
      }
      if !chars[group_start:index].all(is_ascii_hex_digit) {
        return false
      }
      groups = groups + 1
    }
    if index == end {
      break
    }
    if index + 1 < end && chars[index + 1] == ':' {
      if compressed {
        return false
      }
      compressed = true
      index = index + 2
      if index == end {
        break
      }
    } else {
      index = index + 1
      if index == end {
        return false
      }
    }
  }
  if compressed {
    groups < 8
  } else {
    groups == 8
  }
}

///|
fn is_valid_ipv_future_literal(
  chars : ArrayView[Char],
  start : Int,
  end : Int,
) -> Bool {
  if start >= end || !(chars[start] is ('v' | 'V')) {
    return false
  }
  let mut index = start + 1
  let version_start = index
  while index < end && is_ascii_hex_digit(chars[index]) {
    index = index + 1
  }
  if index == version_start || index >= end || chars[index] != '.' {
    return false
  }
  index = index + 1
  let address_start = index
  while index < end &&
        (
          is_uri_unreserved(chars[index]) ||
          is_uri_sub_delimiter(chars[index]) ||
          chars[index] == ':'
        ) {
    index = index + 1
  }
  index == end && index > address_start
}

///|
fn is_valid_uri_authority(
  chars : ArrayView[Char],
  start : Int,
  end : Int,
  allow_iri_chars : Bool,
) -> Bool {
  let mut host_start = start
  let mut userinfo_at = -1
  for index in start..= 0 {
        return false
      }
      userinfo_at = index
    }
  }
  if userinfo_at >= 0 {
    let valid_userinfo = if allow_iri_chars {
      is_valid_uri_chars(chars, start, userinfo_at, is_iri_userinfo_char)
    } else {
      is_valid_uri_chars(chars, start, userinfo_at, is_uri_userinfo_char)
    }
    if !valid_userinfo {
      return false
    }
    host_start = userinfo_at + 1
  }
  if host_start < end && chars[host_start] == '[' {
    let close = match chars[host_start + 1:end].search(']') {
      Some(offset) => host_start + 1 + offset
      None => -1
    }
    if close < 0 ||
      (
        !is_valid_ipv6_literal(chars, host_start + 1, close) &&
        !is_valid_ipv_future_literal(chars, host_start + 1, close)
      ) {
      return false
    }
    if close + 1 == end {
      return true
    }
    if chars[close + 1] != ':' {
      return false
    }
    return chars[close + 2:end].all(char => char.is_ascii_digit())
  }
  let mut port_at = -1
  for index in host_start.. {
        if port_at >= 0 {
          return false
        }
        port_at = index
      }
      '[' | ']' => return false
      _ => ()
    }
  }
  let host_end = if port_at >= 0 { port_at } else { end }
  let valid_reg_name = if allow_iri_chars {
    is_valid_uri_chars(chars, host_start, host_end, is_iri_reg_name_char)
  } else {
    is_valid_uri_chars(chars, host_start, host_end, is_uri_reg_name_char)
  }
  if !valid_reg_name {
    return false
  }
  if port_at >= 0 && !chars[port_at + 1:end].all(char => char.is_ascii_digit()) {
    return false
  }
  true
}

///|
fn is_valid_uri_scheme(chars : ArrayView[Char], start : Int, end : Int) -> Bool {
  if start >= end || !(chars[start] is ('a'..='z' | 'A'..='Z')) {
    return false
  }
  chars[start + 1:end].all(char => {
    char is ('a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '-' | '.')
  })
}

///|
// RFC 3986 URI references and RFC 3987 IRI references have the same component
// structure. IRI mode additionally permits `ucschar` in the components named
// by RFC 3987 and `iprivate` in the query only.
fn is_valid_resource_identifier_reference(
  value : StringView,
  allow_iri_chars : Bool,
) -> Bool {
  if value == "" {
    return false
  }
  let chars = value.to_array()
  let mut query_at = chars.length()
  let mut fragment_at = chars.length()
  for index, char in chars {
    match char {
      '#' => {
        if fragment_at < chars.length() {
          return false
        }
        fragment_at = index
      }
      '?' if query_at == chars.length() && fragment_at == chars.length() =>
        query_at = index
      _ => ()
    }
  }
  let hierarchy_end = if query_at < fragment_at {
    query_at
  } else {
    fragment_at
  }
  let mut scheme_at = -1
  for index in 0.. {
        scheme_at = index
        break
      }
      '/' => break
      _ => ()
    }
  }
  let mut path_start = 0
  if scheme_at >= 0 {
    if !is_valid_uri_scheme(chars, 0, scheme_at) {
      return false
    }
    path_start = scheme_at + 1
  }
  if chars[path_start:hierarchy_end] is ['/', '/', ..] {
    let authority_start = path_start + 2
    let authority_end = match chars[authority_start:hierarchy_end].search('/') {
      Some(offset) => authority_start + offset
      None => hierarchy_end
    }
    if !is_valid_uri_authority(
        chars, authority_start, authority_end, allow_iri_chars,
      ) {
      return false
    }
    path_start = authority_end
  }
  let valid_path = if allow_iri_chars {
    is_valid_uri_chars(chars, path_start, hierarchy_end, fn(char) {
      is_iri_pchar(char) || char == '/'
    })
  } else {
    is_valid_uri_chars(chars, path_start, hierarchy_end, fn(char) {
      is_uri_pchar(char) || char == '/'
    })
  }
  if !valid_path {
    return false
  }
  let valid_query = if allow_iri_chars {
    is_valid_uri_chars(chars, query_at + 1, fragment_at, is_iri_query_char)
  } else {
    is_valid_uri_chars(
      chars,
      query_at + 1,
      fragment_at,
      is_uri_query_or_fragment_char,
    )
  }
  if query_at < chars.length() && !valid_query {
    return false
  }
  let valid_fragment = if allow_iri_chars {
    is_valid_uri_chars(
      chars,
      fragment_at + 1,
      chars.length(),
      is_iri_fragment_char,
    )
  } else {
    is_valid_uri_chars(
      chars,
      fragment_at + 1,
      chars.length(),
      is_uri_query_or_fragment_char,
    )
  }
  if fragment_at < chars.length() && !valid_fragment {
    return false
  }
  true
}

///|
fn is_valid_iri_reference(value : StringView) -> Bool {
  is_valid_resource_identifier_reference(value, true)
}

///|
fn is_valid_office_file_path_chars(
  chars : ArrayView[Char],
  start : Int,
  end : Int,
) -> Bool {
  is_valid_uri_chars(chars, start, end, fn(char) {
    is_iri_pchar(char) || char == '/' || char == '\\'
  })
}

///|
/// Index of the first `\` or `/` at or after `start`, or `chars.length()` when
/// the remainder holds neither separator.
fn next_path_separator(chars : ArrayView[Char], start : Int) -> Int {
  match chars[start:].search_by(char => char is ('\\' | '/')) {
    Some(offset) => start + offset
    None => chars.length()
  }
}

///|
fn is_valid_office_unc_path(chars : ArrayView[Char]) -> Bool {
  guard chars.length() >= 5 && chars is ['\\', '\\', ..] else { return false }
  let server_end = next_path_separator(chars, 2)
  if server_end == 2 ||
    server_end == chars.length() ||
    !is_valid_uri_chars(chars, 2, server_end, is_iri_reg_name_char) {
    return false
  }
  let share_start = server_end + 1
  let share_end = next_path_separator(chars, share_start)
  share_end > share_start &&
  is_valid_uri_chars(chars, share_start, share_end, is_iri_pchar) &&
  is_valid_office_file_path_chars(chars, share_end, chars.length())
}

///|
fn is_valid_office_windows_file_iri(value : StringView) -> Bool {
  let owned = value.to_owned()
  if !owned.to_lower().has_prefix("file:///") {
    return false
  }
  let chars = owned["file:///".length():].to_array()
  if chars is ['a'..='z' | 'A'..='Z', ':', '\\' | '/', ..] {
    return is_valid_office_file_path_chars(chars, 2, chars.length())
  }
  is_valid_office_unc_path(chars)
}

///|
fn is_valid_external_relationship_target(value : StringView) -> Bool {
  is_valid_iri_reference(value) || is_valid_office_windows_file_iri(value)
}

///|
fn is_absolute_relationship_type(value : StringView) -> Bool {
  has_uri_scheme(value) && is_valid_iri_reference(value) && !value.contains("#")
}

///|
fn is_xml_ncname_start(char : Char) -> Bool {
  let code = char.to_int()
  char.is_ascii_alphabetic() ||
  char == '_' ||
  (code >= 0xc0 && code <= 0xd6) ||
  (code >= 0xd8 && code <= 0xf6) ||
  (code >= 0xf8 && code <= 0x2ff) ||
  (code >= 0x370 && code <= 0x37d) ||
  (code >= 0x37f && code <= 0x1fff) ||
  (code >= 0x200c && code <= 0x200d) ||
  (code >= 0x2070 && code <= 0x218f) ||
  (code >= 0x2c00 && code <= 0x2fef) ||
  (code >= 0x3001 && code <= 0xd7ff) ||
  (code >= 0xf900 && code <= 0xfdcf) ||
  (code >= 0xfdf0 && code <= 0xfffd) ||
  (code >= 0x10000 && code <= 0xeffff)
}

///|
fn is_xml_ncname_char(char : Char) -> Bool {
  let code = char.to_int()
  is_xml_ncname_start(char) ||
  char == '-' ||
  char == '.' ||
  char.is_ascii_digit() ||
  code == 0xb7 ||
  (code >= 0x300 && code <= 0x36f) ||
  (code >= 0x203f && code <= 0x2040)
}

///|
fn is_xml_ncname(value : StringView) -> Bool {
  let chars = value.to_array()
  guard chars is [first, .. rest] && is_xml_ncname_start(first) else {
    return false
  }
  rest.all(is_xml_ncname_char)
}

///|
fn normalize_internal_target(target : StringView) -> String? {
  @opc.resolve_part_target("", target)
}

///|
fn normalize_declared_part_name(part_name : StringView) -> String? {
  guard part_name.has_prefix("/") else { return None }
  guard normalize_internal_target(part_name) is Some(normalized) else {
    return None
  }
  if part_name[1:] == normalized {
    Some(normalized)
  } else {
    None
  }
}

///|
fn part_extension(name : String) -> String? {
  match name.rev_find(".") {
    Some(dot) =>
      match name.rev_find("/") {
        Some(slash) if slash > dot => None
        _ if dot + 1 < name.length() =>
          Some(name[dot + 1:].to_owned().to_lower())
        _ => None
      }
    None => None
  }
}

///|
fn relationships_base_dir(part : String) -> String? {
  match @opc.relationship_source_part_name(part) {
    Some("") => Some("")
    Some(source) =>
      match source.rev_find("/") {
        Some(slash) => Some(source[:slash + 1].to_owned())
        None => Some("")
      }
    None => None
  }
}

///|
fn resolve_relationship_target(base : String, target : StringView) -> String? {
  @opc.resolve_part_target(base, target)
}

///|
priv struct StructuralFindings {
  mut first : String?
  mut count : Int
}

///|
fn StructuralFindings::new() -> StructuralFindings {
  { first: None, count: 0 }
}

///|
fn bounded_structural_finding(value : String, maximum? : Int = 512) -> String {
  let output = StringBuilder::new()
  let mut count = 0
  for character in value {
    if count >= maximum {
      output.write_char('…') |> ignore
      break
    }
    output.write_char(character) |> ignore
    count += 1
  }
  output.to_string()
}

///|
fn StructuralFindings::push(
  self : StructuralFindings,
  finding : String,
) -> Unit {
  self.count += 1
  if self.first is None {
    self.first = Some(bounded_structural_finding(finding))
  }
}

///|
test "structural findings retain only one bounded diagnostic" {
  let findings = StructuralFindings::new()
  findings.push("x".repeat(1024))
  for _ in 0..<100_000 {
    findings.push("discarded")
  }
  assert_eq(findings.count, 100_001)
  assert_eq(findings.first.unwrap().length(), 513)
}

///|
fn validate_package_structure(
  archive : @zip.Archive,
  part_index : ArchivePartIndex,
  main_part : String,
  require_main_relationships : Bool,
  context : OfficeFormatContext,
) -> StructuralFindings raise OfficeError {
  let findings = StructuralFindings::new()
  let parts : StableStringMap[Int] = SortedMap([])
  let part_names : StableStringMap[String] = SortedMap([])
  let package_parts : StableStringMap[Bool] = SortedMap([])
  let document_parts : StableStringMap[Bool] = SortedMap([])
  let part_name_registry = @opc.PartNameRegistry::new()
  for entry in archive.entries() {
    context.checkpoint()
    let physical_name = entry.name()
    let is_directory = physical_name.has_suffix("/")
    let item_path = if is_directory && physical_name.length() > 0 {
      physical_name[:physical_name.length() - 1]
    } else {
      physical_name
    }
    let logical_item = if !is_directory {
      archive_entry_logical_part_name(physical_name)
    } else {
      @opc.logical_part_name_from_zip_item_name(item_path)
    }
    match logical_item {
      Some(logical_name) if !is_directory => {
        let key = opc_part_key(logical_name)
        if key != opc_part_key("[Content_Types].xml") {
          match part_name_registry.register(logical_name, physical_name) {
            Some(Derivable(existing)) =>
              findings.push(
                "part name is derivable from another part name: \{existing} and \{physical_name}",
              )
            _ => ()
          }
        }
        parts[key] = parts.get(key).unwrap_or(0) + 1
        if !part_names.contains(key) {
          part_names[key] = physical_name
        }
        if key != opc_part_key("[Content_Types].xml") {
          package_parts[key] = true
          if !@opc.is_relationship_part_name(logical_name) {
            document_parts[key] = true
          }
        }
      }
      Some(_) => ()
      None => {
        if physical_name == "" {
          findings.push("empty entry name")
          continue
        }
        if physical_name.has_prefix("/") {
          findings.push("entry name must not start with '/': \{physical_name}")
        }
        if physical_name.contains("\\") {
          findings.push("entry name contains a backslash: \{physical_name}")
        }
        let mut structural_error = false
        for segment in item_path.split("/") {
          match segment {
            "" => {
              findings.push(
                "entry name contains an empty segment: \{physical_name}",
              )
              structural_error = true
              break
            }
            "." | ".." => {
              findings.push(
                "entry name contains a '\{segment}' segment: \{physical_name}",
              )
              structural_error = true
              break
            }
            _ => ()
          }
        }
        if !structural_error {
          findings.push(
            "entry name is not a valid OPC URI path: \{physical_name}",
          )
        }
      }
    }
  }
  for key, count in parts {
    if count > 1 {
      findings.push("duplicate entry name: \{part_names[key]}")
    }
  }
  if !document_parts.contains(opc_part_key(main_part)) {
    findings.push("missing required part: \{main_part}")
  }
  let main_relationships = @opc.relationships_part_name_for_source(main_part)
  if require_main_relationships &&
    !parts.contains(opc_part_key(main_relationships)) {
    findings.push("missing required part: \{main_relationships}")
  }

  let defaults : StableStringMap[String] = SortedMap([])
  let overrides : StableStringMap[String] = SortedMap([])
  let content_types = read_xml_part(part_index, "[Content_Types].xml", context)
  match unexpected_xml_attribute(content_types, []) {
    Some(attribute) =>
      findings.push(
        "[Content_Types].xml: Types has undeclared attribute: \{attribute}",
      )
    None => ()
  }
  for child in content_types.children {
    context.checkpoint()
    match child {
      XmlText(text) =>
        if !text.trim().is_empty() {
          findings.push("[Content_Types].xml contains unexpected text")
        }
      XmlElement(_) => ()
    }
  }
  for child in content_types.children {
    context.checkpoint()
    guard child is XmlElement(element) else { continue }
    match element.name {
      CONTENT_TYPES_DEFAULT => {
        match unexpected_xml_attribute(element, ["Extension", "ContentType"]) {
          Some(attribute) =>
            findings.push(
              "[Content_Types].xml: Default has undeclared attribute: \{attribute}",
            )
          None => ()
        }
        if !element.children.is_empty() {
          findings.push("[Content_Types].xml: Default element is not empty")
        }
        match
          (
            element.attributes.get("Extension"),
            element.attributes.get("ContentType"),
          ) {
          (Some(extension), Some(content_type)) if extension != "" &&
            content_type != "" => {
            if !@opc.is_valid_content_type_extension(extension) {
              findings.push(
                "[Content_Types].xml: Default has an invalid Extension: \{extension}",
              )
              continue
            }
            if !is_valid_media_type(content_type) {
              findings.push(
                "[Content_Types].xml: Default has an invalid ContentType: \{content_type}",
              )
              continue
            }
            let extension = extension.to_lower()
            if defaults.contains(extension) {
              findings.push("duplicate content-type default: \{extension}")
            } else {
              defaults[extension] = content_type
            }
          }
          _ =>
            findings.push(
              "[Content_Types].xml: Default missing Extension or ContentType",
            )
        }
      }
      CONTENT_TYPES_OVERRIDE => {
        match unexpected_xml_attribute(element, ["PartName", "ContentType"]) {
          Some(attribute) =>
            findings.push(
              "[Content_Types].xml: Override has undeclared attribute: \{attribute}",
            )
          None => ()
        }
        if !element.children.is_empty() {
          findings.push("[Content_Types].xml: Override element is not empty")
        }
        match
          (
            element.attributes.get("PartName"),
            element.attributes.get("ContentType"),
          ) {
          (Some(part_name), Some(content_type)) if content_type != "" => {
            if !is_valid_media_type(content_type) {
              findings.push(
                "[Content_Types].xml: Override has an invalid ContentType: \{content_type}",
              )
              continue
            }
            if part_name.has_prefix("/") &&
              opc_part_key(part_name[1:]) == opc_part_key("[Content_Types].xml") {
              findings.push(
                "content-type override names the reserved manifest: \{part_name}",
              )
              continue
            }
            match normalize_declared_part_name(part_name) {
              Some(target) => {
                let target_key = opc_part_key(target)
                if overrides.contains(target_key) {
                  findings.push("duplicate content-type override: \{part_name}")
                } else {
                  overrides[target_key] = content_type
                }
                if !package_parts.contains(target_key) {
                  findings.push(
                    "content-type override names a missing part: \{part_name}",
                  )
                }
              }
              None =>
                findings.push(
                  "content-type override has an invalid PartName: \{part_name}",
                )
            }
          }
          _ =>
            findings.push(
              "[Content_Types].xml: Override missing PartName or ContentType",
            )
        }
      }
      _ =>
        findings.push(
          "[Content_Types].xml contains an unexpected element: \{element.name}",
        )
    }
  }
  for entry in archive.entries() {
    context.checkpoint()
    let physical_name = entry.name()
    guard archive_entry_logical_part_name(physical_name) is Some(name) else {
      continue
    }
    if opc_part_key(name) == opc_part_key("[Content_Types].xml") {
      continue
    }
    let key = opc_part_key(name)
    let content_type = match overrides.get(key) {
      Some(content_type) => Some(content_type)
      None =>
        match part_extension(name) {
          Some(extension) => defaults.get(extension)
          None => None
        }
    }
    match content_type {
      None => findings.push("part has no declared content type: \{name}")
      Some(content_type) =>
        if @opc.is_relationship_part_name(name) &&
          !media_type_equal(content_type, RELATIONSHIPS_CONTENT_TYPE) {
          findings.push(
            "relationship part has an invalid content type: \{name} -> \{content_type}",
          )
        }
    }
  }

  for entry in archive.entries() {
    context.checkpoint()
    guard archive_entry_logical_part_name(entry.name())
      is Some(relationship_part) else {
      continue
    }
    let relationship_key = opc_part_key(relationship_part)
    if !@opc.is_relationship_part_name(relationship_part) {
      continue
    }
    let base = match relationships_base_dir(relationship_key) {
      Some(base) => base
      None => {
        findings.push(
          "relationship part is not under an _rels directory: \{relationship_part}",
        )
        continue
      }
    }
    match @opc.relationship_source_part_name(relationship_part) {
      Some("") => ()
      Some(source) =>
        if @opc.is_relationship_part_name(source) {
          findings.push(
            "relationship part must not describe another relationship part: \{relationship_part} -> \{source}",
          )
          continue
        } else if !document_parts.contains(opc_part_key(source)) {
          findings.push(
            "relationship part has no source part: \{relationship_part} -> \{source}",
          )
        }
      None => {
        findings.push(
          "relationship part does not identify a source part: \{relationship_part}",
        )
        continue
      }
    }
    let relationships = read_relationships_part(
      part_index, relationship_part, context,
    )
    if relationships.name != RELATIONSHIPS_ROOT {
      findings.push("\{relationship_part} root element is not Relationships")
      continue
    }
    for attribute, _ in relationships.attributes {
      findings.push(
        "\{relationship_part} Relationships has an undeclared attribute: \{attribute}",
      )
    }
    for child in relationships.children {
      context.checkpoint()
      match child {
        XmlText(text) =>
          if !text.trim().is_empty() {
            findings.push("\{relationship_part} contains unexpected text")
          }
        XmlElement(_) => ()
      }
    }
    let ids : StableStringMap[Bool] = SortedMap([])
    for child in relationships.children {
      context.checkpoint()
      guard child is XmlElement(element) else { continue }
      if element.name != RELATIONSHIP_ELEMENT {
        findings.push(
          "\{relationship_part} contains an unexpected element: \{element.name}",
        )
        continue
      }
      for attribute, _ in element.attributes {
        if !(attribute is ("Id" | "Type" | "Target" | "TargetMode")) {
          findings.push(
            "relationship in \{relationship_part} has an undeclared attribute: \{attribute}",
          )
        }
      }
      // CT_Relationship is simpleContent extending xsd:string, so text is
      // schema-valid. Only nested elements violate its content model.
      if xml_element_has_child_element(element) {
        findings.push(
          "relationship in \{relationship_part} contains a child element",
        )
      }
      let id = @xml.collapse_xml_schema_whitespace(
        element.attributes.get("Id").unwrap_or(""),
      )
      if id != "" {
        if !is_xml_ncname(id) {
          findings.push(
            "relationship in \{relationship_part} has an invalid Id: \{id}",
          )
        } else if ids.contains(id) {
          findings.push(
            "duplicate relationship Id in \{relationship_part}: \{id}",
          )
        } else {
          ids[id] = true
        }
      } else {
        findings.push("relationship in \{relationship_part} has no Id")
      }
      let rel_type = @xml.collapse_xml_schema_whitespace(
        element.attributes.get("Type").unwrap_or(""),
      )
      if is_absolute_relationship_type(rel_type) {
        ()
      } else if rel_type != "" && has_uri_scheme(rel_type) {
        findings.push(
          "relationship in \{relationship_part} has an invalid Type IRI: \{rel_type}",
        )
      } else if rel_type != "" {
        findings.push(
          "relationship in \{relationship_part} has a non-absolute Type: \{rel_type}",
        )
      } else {
        findings.push("relationship in \{relationship_part} has no Type")
      }
      let target = @xml.collapse_xml_schema_whitespace(
        element.attributes.get("Target").unwrap_or(""),
      )
      if target == "" {
        findings.push("relationship in \{relationship_part} has no Target")
        continue
      }
      let internal = match element.attributes.get("TargetMode") {
        None | Some("Internal") => true
        Some("External") => false
        Some(mode) => {
          findings.push(
            "relationship in \{relationship_part} has invalid TargetMode: \{mode}",
          )
          false
        }
      }
      if internal {
        match resolve_relationship_target(base, target) {
          Some(resolved) => {
            let resolved_key = opc_part_key(resolved)
            if document_parts.contains(resolved_key) {
              ()
            } else if parts.contains(resolved_key) {
              findings.push(
                "relationship in \{relationship_part} points to package metadata: \{target} -> \{resolved}",
              )
            } else {
              findings.push(
                "relationship in \{relationship_part} points to a missing part: \{target} -> \{resolved}",
              )
            }
          }
          None =>
            findings.push(
              "relationship in \{relationship_part} has an invalid Target: \{target}",
            )
        }
      } else if !is_valid_external_relationship_target(target) {
        findings.push(
          "relationship in \{relationship_part} has an invalid external Target: \{target}",
        )
      }
    }
  }
  findings
}

///|
fn is_office_document_relationship(rel_type : StringView) -> Bool {
  rel_type == TRANSITIONAL_OFFICE_DOCUMENT_REL ||
  rel_type == STRICT_OFFICE_DOCUMENT_REL
}

///|
fn validate_main_identity(
  part_index : ArchivePartIndex,
  format : DocumentFormat,
  declared_main_part : String,
  context : OfficeFormatContext,
) -> Unit raise OfficeError {
  let label = if format is Xlsx { "XLSX" } else { "DOCX" }
  let root_rels = read_relationships_part(part_index, "_rels/.rels", context)
  if root_rels.name != RELATIONSHIPS_ROOT {
    raise InvalidPackage("_rels/.rels root element is not Relationships")
  }
  let main_targets : Array[(String, String)] = []
  for child in root_rels.children {
    context.checkpoint()
    guard child is XmlElement(element) && element.name == RELATIONSHIP_ELEMENT else {
      continue
    }
    let rel_type = @xml.collapse_xml_schema_whitespace(
      element.attributes.get("Type").unwrap_or(""),
    )
    guard is_office_document_relationship(rel_type) else { continue }
    match element.attributes.get("TargetMode") {
      None | Some("Internal") => ()
      Some(_) =>
        raise InvalidPackage(
          "\{label} officeDocument relationship is not internal",
        )
    }
    let id = @xml.collapse_xml_schema_whitespace(
      element.attributes.get("Id").unwrap_or(""),
    )
    if id == "" {
      raise InvalidPackage("\{label} officeDocument relationship has no Id")
    }
    let target = @xml.collapse_xml_schema_whitespace(
      element.attributes.get("Target").unwrap_or(""),
    )
    if target == "" {
      raise InvalidPackage("\{label} officeDocument relationship has no Target")
    }
    let normalized = match normalize_internal_target(target) {
      Some(target) => target
      None =>
        raise InvalidPackage(
          "\{label} officeDocument relationship has an invalid Target",
        )
    }
    main_targets.push((normalized, rel_type))
  }
  let (main_target, main_relationship_type) = match main_targets {
    [] =>
      raise InvalidPackage(
        "\{label} root relationships do not declare an officeDocument relationship",
      )
    [target] => target
    _ =>
      raise InvalidPackage(
        "\{label} root relationships declare multiple officeDocument relationships",
      )
  }
  if opc_part_key(main_target) != opc_part_key(declared_main_part) {
    raise InvalidPackage(
      "\{label} officeDocument relationship targets '\{main_target}', but content types declare '\{declared_main_part}'",
    )
  }
  let main = read_xml_part(part_index, main_target, context)
  let expected_main_relationship = match (format, main.name) {
    (Xlsx, TRANSITIONAL_WORKBOOK_ROOT) | (Docx, TRANSITIONAL_DOCUMENT_ROOT) =>
      Some(TRANSITIONAL_OFFICE_DOCUMENT_REL)
    (Xlsx, STRICT_WORKBOOK_ROOT) | (Docx, STRICT_DOCUMENT_ROOT) =>
      Some(STRICT_OFFICE_DOCUMENT_REL)
    _ => None
  }
  match expected_main_relationship {
    None => {
      let expected = if format is Xlsx { "workbook" } else { "document" }
      raise InvalidPackage("\{label} main part root element is not \{expected}")
    }
    Some(expected) if main_relationship_type != expected =>
      raise InvalidPackage(
        "\{label} officeDocument relationship dialect does not match the main OOXML namespace",
      )
    Some(_) => ()
  }
}

///|
fn default_main_target_from_root(
  part_index : ArchivePartIndex,
  context : OfficeFormatContext,
) -> String? raise OfficeError {
  let root_rels = read_relationships_part(part_index, "_rels/.rels", context)
  if root_rels.name != RELATIONSHIPS_ROOT {
    return None
  }
  let targets : Array[String] = []
  for child in root_rels.children {
    context.checkpoint()
    guard child is XmlElement(element) && element.name == RELATIONSHIP_ELEMENT else {
      continue
    }
    let rel_type = @xml.collapse_xml_schema_whitespace(
      element.attributes.get("Type").unwrap_or(""),
    )
    guard is_office_document_relationship(rel_type) else { continue }
    let internal = match element.attributes.get("TargetMode") {
      None | Some("Internal") => true
      _ => false
    }
    guard internal else { continue }
    let target = @xml.collapse_xml_schema_whitespace(
      element.attributes.get("Target").unwrap_or(""),
    )
    guard target != "" else { continue }
    guard normalize_internal_target(target) is Some(normalized) else {
      continue
    }
    targets.push(normalized)
  }
  match targets {
    [target] => Some(target)
    _ => None
  }
}

///|
fn format_from_archive(
  archive : @zip.Archive,
  part_index : ArchivePartIndex,
  context : OfficeFormatContext,
) -> (DocumentFormat, String) raise OfficeError {
  let physical_names : StableStringMap[Bool] = SortedMap([])
  for entry in archive.entries() {
    context.checkpoint()
    let name = entry.name()
    if physical_names.contains(name) {
      raise InvalidPackage("duplicate entry name: \{name}")
    }
    physical_names[name] = true
    if entry.central_directory_file_header_size() >
      MAX_OPC_CENTRAL_DIRECTORY_FILE_HEADER_BYTES {
      raise InvalidPackage(
        "central-directory file header exceeds the OPC \{MAX_OPC_CENTRAL_DIRECTORY_FILE_HEADER_BYTES}-byte limit: \{name}",
      )
    }
    let actual_crc = @zip.crc32_cancellable(
      entry.data(),
      cancelled=context.cancelled,
    ) catch {
      _ if (context.cancelled)() => raise Cancelled
      _ => raise InvalidPackage("entry CRC-32 verification failed: \{name}")
    }
    if entry.crc32() != actual_crc {
      raise InvalidPackage(
        "entry data does not match its stored CRC-32: \{name}",
      )
    }
  }
  let content_types = read_xml_part(part_index, "[Content_Types].xml", context)
  if content_types.name != CONTENT_TYPES_ROOT {
    raise InvalidPackage("[Content_Types].xml root element is not Types")
  }
  let xlsx_main_parts : Array[String] = []
  let docx_main_parts : Array[String] = []
  let overridden_parts : StableStringMap[Bool] = SortedMap([])
  let main_defaults : StableStringMap[DocumentFormat] = SortedMap([])
  for child in content_types.children {
    context.checkpoint()
    guard child is XmlElement(element) else { continue }
    match element.name {
      CONTENT_TYPES_DEFAULT => {
        let format = match element.attributes.get("ContentType") {
          Some(content_type) => main_format_from_content_type(content_type)
          None => None
        }
        guard format is Some(format) else { continue }
        guard element.attributes.get("Extension") is Some(extension) &&
          extension != "" else {
          raise InvalidPackage("main content type default has no Extension")
        }
        main_defaults[extension.to_lower()] = format
      }
      CONTENT_TYPES_OVERRIDE => {
        let format = match element.attributes.get("ContentType") {
          Some(content_type) => main_format_from_content_type(content_type)
          None => None
        }
        let part_name = match element.attributes.get("PartName") {
          Some(part_name) => part_name
          None =>
            if format is Some(_) {
              raise InvalidPackage("main content type override has no PartName")
            } else {
              continue
            }
        }
        let normalized = match normalize_declared_part_name(part_name) {
          Some(part_name) => part_name
          None =>
            if format is Some(_) {
              raise InvalidPackage(
                "main content type override has an invalid PartName",
              )
            } else {
              continue
            }
        }
        overridden_parts[opc_part_key(normalized)] = true
        match format {
          Some(Xlsx) => xlsx_main_parts.push(normalized)
          Some(Docx) => docx_main_parts.push(normalized)
          None => ()
        }
      }
      _ => ()
    }
  }
  if !main_defaults.is_empty() {
    match default_main_target_from_root(part_index, context) {
      Some(target) if !overridden_parts.contains(opc_part_key(target)) =>
        match part_extension(target) {
          Some(extension) =>
            match main_defaults.get(extension) {
              Some(Xlsx) => xlsx_main_parts.push(target)
              Some(Docx) => docx_main_parts.push(target)
              None => ()
            }
          None => ()
        }
      _ => ()
    }
  }
  let (format, main_part) = match (xlsx_main_parts, docx_main_parts) {
    ([xlsx], []) => (Xlsx, xlsx)
    ([], [docx]) => (Docx, docx)
    ([], []) =>
      raise InvalidPackage(
        "content types do not declare an XLSX or DOCX main part",
      )
    ([_, ..], [_, ..]) =>
      raise InvalidPackage(
        "content types declare both XLSX and DOCX main parts",
      )
    ([_, _, ..], []) =>
      raise InvalidPackage("content types declare multiple XLSX main parts")
    ([], [_, _, ..]) =>
      raise InvalidPackage("content types declare multiple DOCX main parts")
  }
  (format, main_part)
}

///|
fn structural_findings(
  format : DocumentFormat,
  archive : @zip.Archive,
  part_index : ArchivePartIndex,
  main_part : String,
  context : OfficeFormatContext,
) -> StructuralFindings raise OfficeError {
  validate_package_structure(
    archive,
    part_index,
    main_part,
    format is Xlsx,
    context,
  )
}

///|
/// Identifies a structurally valid XLSX or DOCX package.
///
/// The file extension and the OOXML main-part content type must agree. The
/// corresponding portable package validator then checks the package before the
/// format is returned.
pub fn detect_format(
  path : StringView,
  data : BytesView,
  max_package_bytes? : Int = DEFAULT_DETECT_MAX_PACKAGE_BYTES,
  max_archive_entries? : Int = DEFAULT_DETECT_MAX_ARCHIVE_ENTRIES,
  max_entry_uncompressed_bytes? : Int = DEFAULT_DETECT_MAX_ENTRY_BYTES,
  max_total_uncompressed_bytes? : Int = DEFAULT_DETECT_MAX_TOTAL_BYTES,
  max_xml_part_bytes? : Int = DEFAULT_DETECT_MAX_XML_PART_BYTES,
  max_xml_total_units? : Int = DEFAULT_DETECT_MAX_XML_TOTAL_UNITS,
  cancelled? : () -> Bool = () => false,
) -> DocumentFormat raise OfficeError {
  // Preserve the fail-fast extension contract before parsing package bytes.
  ignore(format_from_extension(path))
  if cancelled() {
    raise Cancelled
  }
  let preserved_limit = if max_package_bytes > 2147483647 - 65_535 {
    2147483647
  } else if max_package_bytes < 0 {
    0
  } else {
    max_package_bytes + 65_535
  }
  let archive = @zip.read_limited(
    data,
    max_package_bytes~,
    max_entries=max_archive_entries,
    max_entry_uncompressed_bytes~,
    max_total_uncompressed_bytes~,
    max_total_preserved_source_bytes=preserved_limit,
    cancelled~,
  ) catch {
    ResourceLimitExceeded(kind~, limit~, actual~) =>
      raise ResourceLimit(kind~, limit~, actual~)
    _ if cancelled() => raise Cancelled
    _ => raise InvalidPackage("archive is not a readable ZIP")
  }
  detect_archive_format(
    path,
    archive,
    max_xml_part_bytes~,
    max_xml_total_units~,
    cancelled~,
  )
}

///|
/// Identifies a structurally valid XLSX or DOCX package from an archive that
/// the caller has already materialized. This lets resource-constrained callers
/// parse with their own ZIP limits without parsing the package a second time.
pub fn detect_archive_format(
  path : StringView,
  archive : @zip.Archive,
  max_xml_part_bytes? : Int = DEFAULT_DETECT_MAX_XML_PART_BYTES,
  max_xml_total_units? : Int = DEFAULT_DETECT_MAX_XML_TOTAL_UNITS,
  cancelled? : () -> Bool = () => false,
) -> DocumentFormat raise OfficeError {
  let expected = format_from_extension(path)
  let context = OfficeFormatContext::new(
    max_xml_part_bytes,
    max_xml_total_units,
    cancelled~,
  )
  context.checkpoint()
  let part_index = archive_part_index(archive, context)
  let (actual, main_part) = format_from_archive(archive, part_index, context)
  if expected != actual {
    raise FormatMismatch(expected~, actual~)
  }
  validate_main_identity(part_index, actual, main_part, context)
  let findings = structural_findings(
    actual, archive, part_index, main_part, context,
  )
  match findings.first {
    Some(first) => {
      let suffix = if findings.count == 1 {
        ""
      } else {
        " (and \{findings.count - 1} more finding(s))"
      }
      raise InvalidPackage(first + suffix)
    }
    None => ()
  }
  context.checkpoint()
  actual
}