///|
let media_type : String = "application/vnd.oasis.opendocument.spreadsheet"

///|
fn manifest_xml() -> String {
  ""
}

///|
fn styles_xml() -> String {
  "\{preset_styles()}"
}

// ODF needs mimetype first. Use the shared DEFLATE library's CRC directly,
// without pulling in another ZIP implementation and its incompatible version.
// Fixed ASCII paths only, no assets or path input; no timestamps/extras/comments.

///|
fn stored_package(files : Array[(String, Bytes)]) -> Bytes raise OdsError {
  let body = @buffer.Buffer()
  let central = @buffer.Buffer()
  for (path, data) in files {
    let name = @utf8.encode(path[:])
    let offset = body.length()
    let size = data.length()
    if size > 33554432 || offset + size > 33554432 {
      raise Invalid("package exceeds 32 MiB")
    }
    let crc = @checksum.crc32(data[:]).reinterpret_as_int()
    body.write_int_le(0x04034b50)
    for n in [20, 0, 0, 0, 33] {
      body.write_uint16_le(n.to_uint16())
    }
    for n in [crc, size, size] {
      body.write_int_le(n)
    }
    body.write_uint16_le(name.length().to_uint16())
    body.write_uint16_le(0)
    body.write_bytes(name[:])
    body.write_bytes(data[:])
    central.write_int_le(0x02014b50)
    for n in [20, 20, 0, 0, 0, 33] {
      central.write_uint16_le(n.to_uint16())
    }
    for n in [crc, size, size] {
      central.write_int_le(n)
    }
    for n in [name.length(), 0, 0, 0, 0] {
      central.write_uint16_le(n.to_uint16())
    }
    central.write_int_le(0)
    central.write_int_le(offset)
    central.write_bytes(name[:])
  }
  let cd_offset = body.length()
  let cd_size = central.length()
  body.write_bytes(central.to_bytes()[:])
  body.write_int_le(0x06054b50)
  for n in [0, 0, files.length(), files.length()] {
    body.write_uint16_le(n.to_uint16())
  }
  body.write_int_le(cd_size)
  body.write_int_le(cd_offset)
  body.write_uint16_le(0)
  if body.length() > 33554432 {
    raise Invalid("package exceeds 32 MiB")
  }
  body.to_bytes()
}

///|
/// Return a deterministic ODS within the reader's bounded output profile:
/// 4 MiB total uncompressed entry bytes and the existing XML depth/node caps.
/// Grid/text limits alone do not guarantee serializability. Oversized output
/// raises before returning any package; I/O belongs to the caller.
pub fn Workbook::to_ods(self : Workbook) -> Bytes raise OdsError {
  let content = self.content_xml()
  let files = [
    ("mimetype", @utf8.encode(media_type[:])),
    ("content.xml", @utf8.encode(content[:])),
    (
      "styles.xml",
      @utf8.encode(
        styles_xml().replace(
          old="",
          new=self.typed_styles() + "",
        )[:],
      ),
    ),
    ("META-INF/manifest.xml", @utf8.encode(manifest_xml()[:])),
  ]
  let mut total = 0
  for (_, data) in files {
    if data.length() > 4194304 - total {
      raise Invalid("ODS output exceeds 4 MiB reader profile")
    }
    total += data.length()
  }
  read_xml(content) |> ignore
  stored_package(files)
}