use quick_xml::{
  Decoder, Reader,
  escape::unescape,
  events::{BytesCData, BytesRef, BytesText, Event, attributes::Attribute},
};
use std::io::BufRead;

use super::{SdkError, invalid_field_value, unexpected_eof, unexpected_tag};

pub struct IoReader<R: BufRead> {
  reader: Reader<R>,
  buf: Vec<u8>,
  current: Option<Event<'static>>,
  pending: Option<Event<'static>>,
}

pub(crate) enum IoTagEvent {
  Start(quick_xml::events::BytesStart<'static>, bool),
  End(quick_xml::events::BytesEnd<'static>),
  Decl(bool),
  Eof,
  Other,
}

pub enum SliceTagEvent<'de> {
  Start(quick_xml::events::BytesStart<'de>, bool),
  End(quick_xml::events::BytesEnd<'de>),
  Decl(bool),
  Eof,
  Other,
}

impl<R: BufRead> IoReader<R> {
  pub fn new(reader: Reader<R>) -> Self {
    Self {
      reader,
      buf: Vec::new(),
      current: None,
      pending: None,
    }
  }

  #[inline]
  pub fn next(&mut self) -> Result<Event<'static>, SdkError> {
    self.current = None;
    if let Some(event) = self.pending.take() {
      return Ok(event);
    }

    self.buf.clear();
    Ok(self.reader.read_event_into(&mut self.buf)?.into_owned())
  }

  #[inline]
  pub fn next_borrowed(&mut self) -> Result<Event<'_>, SdkError> {
    self.current = None;
    if let Some(event) = self.pending.take() {
      self.current = Some(event);
      return Ok(self.current.as_ref().expect("current event").borrow());
    }

    self.buf.clear();
    Ok(self.reader.read_event_into(&mut self.buf)?)
  }

  #[inline]
  pub fn read_text(
    &mut self,
    end: quick_xml::name::QName<'_>,
  ) -> Result<std::borrow::Cow<'_, str>, SdkError> {
    self.current = None;
    self.buf.clear();
    let text = self
      .reader
      .read_text_into(end, &mut self.buf)?
      .xml10_content()?;
    unescape_text(text)
  }

  #[inline]
  pub(crate) fn next_tag_event(&mut self) -> Result<IoTagEvent, SdkError> {
    self.current = None;
    if let Some(event) = self.pending.take() {
      return Ok(Self::tag_event_from_owned(event));
    }

    self.buf.clear();
    Ok(match self.reader.read_event_into(&mut self.buf)? {
      Event::Start(e) => IoTagEvent::Start(e.into_owned(), false),
      Event::Empty(e) => IoTagEvent::Start(e.into_owned(), true),
      Event::End(e) => IoTagEvent::End(e.into_owned()),
      Event::Decl(e) => IoTagEvent::Decl(matches!(
        e.standalone(),
        Some(Ok(value)) if value.as_ref().eq_ignore_ascii_case(b"yes")
      )),
      Event::Eof => IoTagEvent::Eof,
      _ => IoTagEvent::Other,
    })
  }

  #[inline]
  pub fn unread(&mut self, event: Event<'static>) -> Result<(), SdkError> {
    self.current = None;
    if self.pending.is_some() {
      return Err(SdkError::CommonError(
        "xml reader unread buffer already occupied".to_string(),
      ));
    }

    self.pending = Some(event);
    Ok(())
  }

  #[inline]
  pub fn decoder(&self) -> Decoder {
    self.reader.decoder()
  }

  #[inline]
  fn tag_event_from_owned(event: Event<'static>) -> IoTagEvent {
    match event {
      Event::Start(e) => IoTagEvent::Start(e, false),
      Event::Empty(e) => IoTagEvent::Start(e, true),
      Event::End(e) => IoTagEvent::End(e),
      Event::Decl(e) => IoTagEvent::Decl(matches!(
        e.standalone(),
        Some(Ok(value)) if value.as_ref().eq_ignore_ascii_case(b"yes")
      )),
      Event::Eof => IoTagEvent::Eof,
      _ => IoTagEvent::Other,
    }
  }
}

pub struct SliceReader<'de> {
  reader: Reader<&'de [u8]>,
  pending: Option<Event<'de>>,
}

impl<'de> SliceReader<'de> {
  pub fn new(reader: Reader<&'de [u8]>) -> Self {
    Self {
      reader,
      pending: None,
    }
  }

  #[inline]
  pub fn next(&mut self) -> Result<Event<'de>, SdkError> {
    if let Some(event) = self.pending.take() {
      return Ok(event);
    }

    Ok(self.reader.read_event()?)
  }

  #[inline]
  pub fn read_text(
    &mut self,
    end: quick_xml::name::QName<'_>,
  ) -> Result<std::borrow::Cow<'de, str>, SdkError> {
    let text = self.reader.read_text(end)?.xml10_content()?;
    unescape_text(text)
  }

  #[inline]
  pub fn next_tag_event(&mut self) -> Result<SliceTagEvent<'de>, SdkError> {
    if let Some(event) = self.pending.take() {
      return Ok(Self::tag_event_from_event(event));
    }

    Ok(Self::tag_event_from_event(self.reader.read_event()?))
  }

  #[inline]
  pub fn unread(&mut self, event: Event<'de>) -> Result<(), SdkError> {
    if self.pending.is_some() {
      return Err(SdkError::CommonError(
        "xml reader unread buffer already occupied".to_string(),
      ));
    }

    self.pending = Some(event);
    Ok(())
  }

  #[inline]
  pub fn decoder(&self) -> Decoder {
    self.reader.decoder()
  }

  #[inline]
  fn tag_event_from_event(event: Event<'de>) -> SliceTagEvent<'de> {
    match event {
      Event::Start(e) => SliceTagEvent::Start(e, false),
      Event::Empty(e) => SliceTagEvent::Start(e, true),
      Event::End(e) => SliceTagEvent::End(e),
      Event::Decl(e) => SliceTagEvent::Decl(matches!(
        e.standalone(),
        Some(Ok(value)) if value.as_ref().eq_ignore_ascii_case(b"yes")
      )),
      Event::Eof => SliceTagEvent::Eof,
      _ => SliceTagEvent::Other,
    }
  }
}

pub fn resolve_zip_file_path(path: &str) -> String {
  let mut stack = Vec::new();
  for component in path.split('/') {
    match component {
      "" | "." => {}
      ".." => {
        stack.pop();
      }
      _ => {
        stack.push(component);
      }
    }
  }
  stack.join("/")
}

pub fn resolve_relationship_target_path(parent_path: &str, target: &str) -> String {
  if target.starts_with('/') {
    resolve_zip_file_path(target)
  } else {
    let mut combined = String::with_capacity(parent_path.len() + target.len());
    combined.push_str(parent_path);
    combined.push_str(target);
    resolve_zip_file_path(&combined)
  }
}

#[inline]
pub(crate) fn from_reader_inner<R: BufRead>(reader: R) -> Result<IoReader<R>, SdkError> {
  let mut xml_reader = Reader::from_reader(reader);
  xml_reader.config_mut().check_end_names = false;
  Ok(IoReader::new(xml_reader))
}

#[inline]
pub(crate) fn from_bytes_inner(bytes: &[u8]) -> Result<SliceReader<'_>, SdkError> {
  let mut xml_reader = Reader::from_reader(bytes);
  xml_reader.config_mut().check_end_names = false;
  Ok(SliceReader::new(xml_reader))
}

pub(crate) fn decode_utf16_xml_bytes(bytes: &[u8]) -> Result<Option<Vec<u8>>, SdkError> {
  let (endian, bytes) = match bytes {
    [0xFF, 0xFE, rest @ ..] => (Utf16Endian::Little, rest),
    [0xFE, 0xFF, rest @ ..] => (Utf16Endian::Big, rest),
    [b'<', 0, b'?', 0, ..] => (Utf16Endian::Little, bytes),
    [0, b'<', 0, b'?', ..] => (Utf16Endian::Big, bytes),
    _ => return Ok(None),
  };

  if bytes.len() % 2 != 0 {
    return Err(SdkError::CommonError(
      "invalid UTF-16 XML byte length".to_string(),
    ));
  }

  let code_units = bytes.chunks_exact(2).map(|chunk| match endian {
    Utf16Endian::Little => u16::from_le_bytes([chunk[0], chunk[1]]),
    Utf16Endian::Big => u16::from_be_bytes([chunk[0], chunk[1]]),
  });
  let xml = std::char::decode_utf16(code_units)
    .collect::<Result<String, _>>()
    .map_err(|err| SdkError::CommonError(format!("invalid UTF-16 XML: {err}")))?;

  Ok(Some(normalize_utf16_xml_decl(xml).into_bytes()))
}

#[derive(Clone, Copy)]
enum Utf16Endian {
  Little,
  Big,
}

fn normalize_utf16_xml_decl(mut xml: String) -> String {
  let Some(decl_end) = xml.find("?>").map(|end| end + 2) else {
    return xml;
  };
  if !xml[..decl_end].starts_with("<?xml") {
    return xml;
  }

  let Some(encoding_pos) = find_ascii_ignore_case(&xml[..decl_end], "encoding") else {
    return xml;
  };
  let bytes = xml.as_bytes();
  let mut pos = encoding_pos + "encoding".len();
  while pos < decl_end && bytes[pos].is_ascii_whitespace() {
    pos += 1;
  }
  if pos >= decl_end || bytes[pos] != b'=' {
    return xml;
  }
  pos += 1;
  while pos < decl_end && bytes[pos].is_ascii_whitespace() {
    pos += 1;
  }
  if pos >= decl_end || (bytes[pos] != b'"' && bytes[pos] != b'\'') {
    return xml;
  }

  let quote = bytes[pos];
  let value_start = pos + 1;
  let Some(value_end) = bytes[value_start..decl_end]
    .iter()
    .position(|&b| b == quote)
    .map(|offset| value_start + offset)
  else {
    return xml;
  };
  xml.replace_range(value_start..value_end, "UTF-8");
  xml
}

fn find_ascii_ignore_case(haystack: &str, needle: &str) -> Option<usize> {
  haystack
    .as_bytes()
    .windows(needle.len())
    .position(|window| window.eq_ignore_ascii_case(needle.as_bytes()))
}

#[inline]
pub(crate) fn from_str_inner(s: &str) -> Result<SliceReader<'_>, SdkError> {
  let mut xml_reader = Reader::from_str(s);
  xml_reader.config_mut().check_end_names = false;
  Ok(SliceReader::new(xml_reader))
}

#[inline(always)]
pub(crate) fn attr_raw_value<'a>(attr: &'a Attribute<'a>) -> Option<&'a [u8]> {
  let value = attr.value.as_ref();
  if value.contains(&b'&') {
    None
  } else {
    Some(value)
  }
}

#[inline]
pub(crate) fn decode_attr_value(
  attr: &Attribute<'_>,
  decoder: Decoder,
) -> Result<String, SdkError> {
  if let Some(value) = attr_raw_value(attr) {
    Ok(decoder.decode(value)?.into_owned())
  } else {
    Ok(unescape(&decoder.decode(attr.value.as_ref())?)?.into_owned())
  }
}

macro_rules! define_unsigned_decimal_attr_parser {
  ($attr_name:ident, $bytes_name:ident, $ty:ty) => {
    #[inline]
    pub(crate) fn $attr_name(
      attr: &Attribute<'_>,
      decoder: Decoder,
      ty: &'static str,
      field: &'static str,
    ) -> Result<$ty, SdkError> {
      if let Some(value) = attr_raw_value(attr) {
        $bytes_name(value, ty, field)
      } else {
        let value = decode_attr_value(attr, decoder)?;
        $bytes_name(value.as_bytes(), ty, field)
      }
    }

    #[inline]
    fn $bytes_name(value: &[u8], ty: &'static str, field: &'static str) -> Result<$ty, SdkError> {
      let digits = match value {
        [b'+', rest @ ..] => rest,
        _ => value,
      };
      if digits.is_empty() {
        return Err(invalid_field_value_bytes(ty, field, value));
      }

      let mut parsed: $ty = 0;
      for &digit in digits {
        if !digit.is_ascii_digit() {
          return Err(invalid_field_value_bytes(ty, field, value));
        }

        parsed = parsed
          .checked_mul(10)
          .and_then(|current| current.checked_add((digit - b'0') as $ty))
          .ok_or_else(|| invalid_field_value_bytes(ty, field, value))?;
      }

      Ok(parsed)
    }
  };
}

macro_rules! define_signed_decimal_attr_parser {
  ($attr_name:ident, $bytes_name:ident, $ty:ty) => {
    #[inline]
    pub(crate) fn $attr_name(
      attr: &Attribute<'_>,
      decoder: Decoder,
      ty: &'static str,
      field: &'static str,
    ) -> Result<$ty, SdkError> {
      if let Some(value) = attr_raw_value(attr) {
        $bytes_name(value, ty, field)
      } else {
        let value = decode_attr_value(attr, decoder)?;
        $bytes_name(value.as_bytes(), ty, field)
      }
    }

    #[inline]
    fn $bytes_name(value: &[u8], ty: &'static str, field: &'static str) -> Result<$ty, SdkError> {
      let (negative, digits) = match value {
        [b'-', rest @ ..] => (true, rest),
        [b'+', rest @ ..] => (false, rest),
        _ => (false, value),
      };
      if digits.is_empty() {
        return Err(invalid_field_value_bytes(ty, field, value));
      }

      let mut parsed: $ty = 0;
      for &digit in digits {
        if !digit.is_ascii_digit() {
          return Err(invalid_field_value_bytes(ty, field, value));
        }

        parsed = parsed
          .checked_mul(10)
          .and_then(|current| {
            if negative {
              current.checked_sub((digit - b'0') as $ty)
            } else {
              current.checked_add((digit - b'0') as $ty)
            }
          })
          .ok_or_else(|| invalid_field_value_bytes(ty, field, value))?;
      }

      Ok(parsed)
    }
  };
}

define_unsigned_decimal_attr_parser!(parse_u8_attr, parse_u8_bytes, u8);
define_signed_decimal_attr_parser!(parse_i8_attr, parse_i8_bytes, i8);
define_unsigned_decimal_attr_parser!(parse_u16_attr, parse_u16_bytes, u16);
define_signed_decimal_attr_parser!(parse_i16_attr, parse_i16_bytes, i16);
define_unsigned_decimal_attr_parser!(parse_u32_attr, parse_u32_bytes, u32);
define_signed_decimal_attr_parser!(parse_i32_attr, parse_i32_bytes, i32);
define_unsigned_decimal_attr_parser!(parse_u64_attr, parse_u64_bytes, u64);
define_signed_decimal_attr_parser!(parse_i64_attr, parse_i64_bytes, i64);

#[inline]
pub(crate) fn parse_twips_measure_attr(
  attr: &Attribute<'_>,
  decoder: Decoder,
) -> Result<crate::simple_type::TwipsMeasureValue, SdkError> {
  if let Some(value) = attr_raw_value(attr)
    && let Ok(value) = crate::simple_type::TwipsMeasureValue::from_bytes(value)
  {
    return Ok(value);
  }
  let value = decode_attr_value(attr, decoder)?;
  parse_twips_measure_value(value)
}

#[inline]
pub(crate) fn parse_twips_measure_value(
  value: String,
) -> Result<crate::simple_type::TwipsMeasureValue, SdkError> {
  crate::simple_type::TwipsMeasureValue::from_bytes(value.as_bytes())
    .map_err(|_| invalid_field_value("TwipsMeasureValue", "value", value))
}

#[inline]
pub(crate) fn parse_signed_twips_measure_attr(
  attr: &Attribute<'_>,
  decoder: Decoder,
) -> Result<crate::simple_type::SignedTwipsMeasureValue, SdkError> {
  if let Some(value) = attr_raw_value(attr)
    && let Ok(value) = crate::simple_type::SignedTwipsMeasureValue::from_bytes(value)
  {
    return Ok(value);
  }
  let value = decode_attr_value(attr, decoder)?;
  parse_signed_twips_measure_value(value)
}

#[inline]
pub(crate) fn parse_signed_twips_measure_value(
  value: String,
) -> Result<crate::simple_type::SignedTwipsMeasureValue, SdkError> {
  crate::simple_type::SignedTwipsMeasureValue::from_bytes(value.as_bytes())
    .map_err(|_| invalid_field_value("SignedTwipsMeasureValue", "value", value))
}

#[inline]
pub(crate) fn parse_decimal_number_or_percent_attr(
  attr: &Attribute<'_>,
  decoder: Decoder,
) -> Result<crate::simple_type::DecimalNumberOrPercentValue, SdkError> {
  if let Some(value) = attr_raw_value(attr)
    && let Ok(value) = crate::simple_type::DecimalNumberOrPercentValue::from_bytes(value)
  {
    return Ok(value);
  }
  let value = decode_attr_value(attr, decoder)?;
  parse_decimal_number_or_percent_value(value)
}

#[inline]
pub(crate) fn parse_decimal_number_or_percent_value(
  value: String,
) -> Result<crate::simple_type::DecimalNumberOrPercentValue, SdkError> {
  crate::simple_type::DecimalNumberOrPercentValue::from_bytes(value.as_bytes())
    .map_err(|_| invalid_field_value("DecimalNumberOrPercentValue", "value", value))
}

#[inline]
pub(crate) fn parse_measurement_or_percent_attr(
  attr: &Attribute<'_>,
  decoder: Decoder,
) -> Result<crate::simple_type::MeasurementOrPercentValue, SdkError> {
  if let Some(value) = attr_raw_value(attr)
    && let Ok(value) = crate::simple_type::MeasurementOrPercentValue::from_bytes(value)
  {
    return Ok(value);
  }
  let value = decode_attr_value(attr, decoder)?;
  parse_measurement_or_percent_value(value)
}

#[inline]
pub(crate) fn parse_measurement_or_percent_value(
  value: String,
) -> Result<crate::simple_type::MeasurementOrPercentValue, SdkError> {
  crate::simple_type::MeasurementOrPercentValue::from_bytes(value.as_bytes())
    .map_err(|_| invalid_field_value("MeasurementOrPercentValue", "value", value))
}

#[inline]
pub(crate) fn parse_i32_zero_on_overflow_attr(
  attr: &Attribute<'_>,
  decoder: Decoder,
  ty: &'static str,
  field: &'static str,
) -> Result<i32, SdkError> {
  if let Some(value) = attr_raw_value(attr) {
    parse_i32_zero_on_overflow_bytes(value, ty, field)
  } else {
    let value = decode_attr_value(attr, decoder)?;
    parse_i32_zero_on_overflow_bytes(value.as_bytes(), ty, field)
  }
}

#[inline]
fn parse_i32_zero_on_overflow_bytes(
  value: &[u8],
  ty: &'static str,
  field: &'static str,
) -> Result<i32, SdkError> {
  let (negative, digits) = match value {
    [b'-', rest @ ..] => (true, rest),
    [b'+', rest @ ..] => (false, rest),
    _ => (false, value),
  };
  if digits.is_empty() {
    return Err(invalid_field_value_bytes(ty, field, value));
  }

  let limit = if negative {
    i32::MAX as u32 + 1
  } else {
    i32::MAX as u32
  };
  let mut parsed: u32 = 0;
  for &digit in digits {
    if !digit.is_ascii_digit() {
      return Err(invalid_field_value_bytes(ty, field, value));
    }

    parsed = match parsed
      .checked_mul(10)
      .and_then(|current| current.checked_add((digit - b'0') as u32))
    {
      Some(parsed) if parsed <= limit => parsed,
      _ => return Ok(0),
    };
  }

  if negative {
    if parsed == i32::MAX as u32 + 1 {
      Ok(i32::MIN)
    } else {
      Ok(-(parsed as i32))
    }
  } else {
    Ok(parsed as i32)
  }
}

#[inline]
pub(crate) fn parse_attr_value<T>(
  attr: &Attribute<'_>,
  decoder: Decoder,
  ty: &'static str,
  field: &'static str,
) -> Result<T, SdkError>
where
  T: std::str::FromStr,
{
  if let Some(value) = attr_raw_value(attr)
    && let Ok(value) = std::str::from_utf8(value)
    && let Ok(value) = value.parse::<T>()
  {
    Ok(value)
  } else {
    let value = decode_attr_value(attr, decoder)?;
    parse_value(value.as_ref(), ty, field)
  }
}

#[inline]
pub(crate) fn parse_enum_attr<T>(
  attr: &Attribute<'_>,
  decoder: Decoder,
  _ty: &'static str,
) -> Result<T, SdkError>
where
  T: crate::sdk::SdkEnum,
{
  if let Some(value) = attr_raw_value(attr) {
    return T::from_xml_bytes(value);
  }

  let value = decode_attr_value(attr, decoder)?;
  T::from_xml_bytes(value.as_bytes())
}

#[inline]
pub(crate) fn parse_value<T>(
  value: &str,
  ty: &'static str,
  field: &'static str,
) -> Result<T, SdkError>
where
  T: std::str::FromStr,
{
  value
    .parse::<T>()
    .map_err(|_| invalid_field_value(ty, field, value))
}

#[inline]
pub(crate) fn parse_text_child_value<T>(
  value: &str,
  ty: &'static str,
  field: &'static str,
) -> Result<T, SdkError>
where
  T: std::str::FromStr,
{
  match value.parse::<T>() {
    Ok(value) => Ok(value),
    Err(_) => {
      let trimmed = value.trim_matches([' ', '\t', '\n', '\r']);
      if trimmed.len() == value.len() {
        return Err(invalid_field_value(ty, field, value));
      }

      trimmed
        .parse::<T>()
        .map_err(|_| invalid_field_value(ty, field, value))
    }
  }
}

#[inline]
pub(crate) fn parse_list_attr<T>(
  attr: &Attribute<'_>,
  decoder: Decoder,
  ty: &'static str,
  field: &'static str,
) -> Result<Vec<T>, SdkError>
where
  T: std::str::FromStr,
{
  let value = decode_attr_value(attr, decoder)?;
  parse_list_value(value.as_ref(), ty, field)
}

#[inline]
pub(crate) fn parse_list_value<T>(
  value: &str,
  ty: &'static str,
  field: &'static str,
) -> Result<Vec<T>, SdkError>
where
  T: std::str::FromStr,
{
  value
    .split_whitespace()
    .map(|item| parse_value::<T>(item, ty, field))
    .collect()
}

#[inline]
fn invalid_field_value_bytes(ty: &'static str, field: &'static str, value: &[u8]) -> SdkError {
  invalid_field_value(ty, field, String::from_utf8_lossy(value).into_owned())
}

#[inline]
pub(crate) fn push_xml_text(
  value: &mut Option<String>,
  text: BytesText<'_>,
) -> Result<(), SdkError> {
  let text = text.xml10_content()?;
  if let Some(value) = value {
    value.push_str(text.as_ref());
  } else {
    *value = Some(text.into_owned());
  }

  Ok(())
}

#[inline]
pub(crate) fn push_xml_cdata(
  value: &mut Option<String>,
  text: BytesCData<'_>,
) -> Result<(), SdkError> {
  let text = text.xml10_content()?;
  if let Some(value) = value {
    value.push_str(text.as_ref());
  } else {
    *value = Some(text.into_owned());
  }

  Ok(())
}

#[inline]
pub(crate) fn push_xml_general_ref(
  value: &mut Option<String>,
  text: BytesRef<'_>,
  ty: &'static str,
  field: &'static str,
) -> Result<(), SdkError> {
  let entity = text.xml10_content()?;
  let entity = resolve_general_ref_entity(entity.as_ref())
    .ok_or_else(|| invalid_field_value(ty, field, entity.to_string()))?;

  if let Some(value) = value {
    value.push_str(entity.as_ref());
  } else {
    *value = Some(entity.into_owned());
  }

  Ok(())
}

#[inline]
fn unescape_text<'a>(
  text: std::borrow::Cow<'a, str>,
) -> Result<std::borrow::Cow<'a, str>, SdkError> {
  match text {
    std::borrow::Cow::Borrowed(text) => Ok(unescape(text)?),
    std::borrow::Cow::Owned(text) => match unescape(&text)? {
      std::borrow::Cow::Borrowed(_) => Ok(std::borrow::Cow::Owned(text)),
      std::borrow::Cow::Owned(text) => Ok(std::borrow::Cow::Owned(text)),
    },
  }
}

#[inline]
fn resolve_general_ref_entity(entity: &str) -> Option<std::borrow::Cow<'_, str>> {
  if let Some(entity) = quick_xml::escape::resolve_predefined_entity(entity) {
    return Some(std::borrow::Cow::Borrowed(entity));
  }

  if let Some(hex) = entity
    .strip_prefix("#x")
    .or_else(|| entity.strip_prefix("#X"))
  {
    let code_point = u32::from_str_radix(hex, 16).ok()?;
    let ch = char::from_u32(code_point)?;
    return Some(std::borrow::Cow::Owned(ch.to_string()));
  }

  if let Some(decimal) = entity.strip_prefix('#') {
    let code_point = decimal.parse::<u32>().ok()?;
    let ch = char::from_u32(code_point)?;
    return Some(std::borrow::Cow::Owned(ch.to_string()));
  }

  None
}

#[cfg(feature = "flat-opc")]
pub(crate) fn read_outer_xml_borrowed<'de>(
  xml_reader: &mut SliceReader<'de>,
  start: quick_xml::events::BytesStart<'de>,
  empty_tag: bool,
) -> Result<String, SdkError> {
  let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));

  if empty_tag {
    writer.write_event(Event::Empty(start))?;
  } else {
    writer.write_event(Event::Start(start))?;

    let mut depth = 1usize;
    loop {
      let event = xml_reader.next()?;
      match &event {
        Event::Start(_) => {
          depth += 1;
        }
        Event::End(_) => {
          depth -= 1;
        }
        Event::Eof => return Err(unexpected_eof("read_outer_xml")),
        _ => {}
      }

      writer.write_event(event)?;

      if depth == 0 {
        break;
      }
    }
  }

  String::from_utf8(writer.into_inner().into_inner())
    .map_err(|err| SdkError::CommonError(format!("invalid utf-8 xml fragment: {err}")))
}

const RAW_EMPTY_XML_EXTRA_LEN: usize = 3;
const RAW_ELEMENT_XML_EXTRA_LEN: usize = 5;

#[inline]
pub(crate) fn read_raw_empty_xml_borrowed_bytes<'de>(
  start: quick_xml::events::BytesStart<'de>,
) -> Result<Box<[u8]>, SdkError> {
  let start: &[u8] = start.as_ref();
  let mut xml = Vec::with_capacity(start.len() + RAW_EMPTY_XML_EXTRA_LEN);
  xml.push(b'<');
  xml.extend_from_slice(start);
  xml.extend_from_slice(b"/>");
  Ok(xml.into_boxed_slice())
}

#[inline]
pub(crate) fn read_raw_empty_xml_borrowed<'de>(
  start: quick_xml::events::BytesStart<'de>,
) -> Result<String, SdkError> {
  String::from_utf8(read_raw_empty_xml_borrowed_bytes(start)?.into_vec())
    .map_err(|err| SdkError::CommonError(format!("invalid utf-8 xml fragment: {err}")))
}

#[inline]
pub(crate) fn read_raw_element_xml_borrowed_bytes<'de>(
  xml_reader: &mut SliceReader<'de>,
  start: quick_xml::events::BytesStart<'de>,
) -> Result<Box<[u8]>, SdkError> {
  let start_bytes: &[u8] = start.as_ref();
  let end_name = start.name();
  let inner = xml_reader.reader.read_text(end_name)?;
  let inner: &[u8] = inner.as_ref();
  let end_name = end_name.as_ref();

  let mut xml = Vec::with_capacity(
    start_bytes.len() + inner.len() + end_name.len() + RAW_ELEMENT_XML_EXTRA_LEN,
  );
  xml.push(b'<');
  xml.extend_from_slice(start_bytes);
  xml.push(b'>');
  xml.extend_from_slice(inner);
  xml.extend_from_slice(b"</");
  xml.extend_from_slice(end_name);
  xml.push(b'>');
  Ok(xml.into_boxed_slice())
}

#[inline]
pub(crate) fn read_raw_element_xml_borrowed<'de>(
  xml_reader: &mut SliceReader<'de>,
  start: quick_xml::events::BytesStart<'de>,
) -> Result<String, SdkError> {
  String::from_utf8(read_raw_element_xml_borrowed_bytes(xml_reader, start)?.into_vec())
    .map_err(|err| SdkError::CommonError(format!("invalid utf-8 xml fragment: {err}")))
}

#[cfg(feature = "flat-opc")]
pub(crate) fn read_outer_xml_io<R: BufRead>(
  xml_reader: &mut IoReader<R>,
  start: quick_xml::events::BytesStart<'static>,
  empty_tag: bool,
) -> Result<String, SdkError> {
  let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));

  if empty_tag {
    writer.write_event(Event::Empty(start))?;
  } else {
    writer.write_event(Event::Start(start))?;

    let mut depth = 1usize;
    loop {
      let event = xml_reader.next()?;
      match &event {
        Event::Start(_) => {
          depth += 1;
        }
        Event::End(_) => {
          depth -= 1;
        }
        Event::Eof => return Err(unexpected_eof("read_outer_xml_io")),
        _ => {}
      }

      writer.write_event(event)?;

      if depth == 0 {
        break;
      }
    }
  }

  String::from_utf8(writer.into_inner().into_inner())
    .map_err(|err| SdkError::CommonError(format!("invalid utf-8 xml fragment: {err}")))
}

#[inline]
pub(crate) fn read_raw_empty_xml_io_bytes(
  start: quick_xml::events::BytesStart<'static>,
) -> Result<Box<[u8]>, SdkError> {
  let start: &[u8] = start.as_ref();
  let mut xml = Vec::with_capacity(start.len() + RAW_EMPTY_XML_EXTRA_LEN);
  xml.push(b'<');
  xml.extend_from_slice(start);
  xml.extend_from_slice(b"/>");
  Ok(xml.into_boxed_slice())
}

#[inline]
pub(crate) fn read_raw_empty_xml_io(
  start: quick_xml::events::BytesStart<'static>,
) -> Result<String, SdkError> {
  String::from_utf8(read_raw_empty_xml_io_bytes(start)?.into_vec())
    .map_err(|err| SdkError::CommonError(format!("invalid utf-8 xml fragment: {err}")))
}

#[inline]
pub(crate) fn read_raw_element_xml_io_bytes<R: BufRead>(
  xml_reader: &mut IoReader<R>,
  start: quick_xml::events::BytesStart<'static>,
) -> Result<Box<[u8]>, SdkError> {
  let start_bytes: &[u8] = start.as_ref();
  let end_name = start.name();
  let inner = xml_reader
    .reader
    .read_text_into(end_name, &mut xml_reader.buf)?;
  let inner: &[u8] = inner.as_ref();
  let end_name = end_name.as_ref();

  let mut xml = Vec::with_capacity(
    start_bytes.len() + inner.len() + end_name.len() + RAW_ELEMENT_XML_EXTRA_LEN,
  );
  xml.push(b'<');
  xml.extend_from_slice(start_bytes);
  xml.push(b'>');
  xml.extend_from_slice(inner);
  xml.extend_from_slice(b"</");
  xml.extend_from_slice(end_name);
  xml.push(b'>');
  Ok(xml.into_boxed_slice())
}

#[inline]
pub(crate) fn read_raw_element_xml_io<R: BufRead>(
  xml_reader: &mut IoReader<R>,
  start: quick_xml::events::BytesStart<'static>,
) -> Result<String, SdkError> {
  String::from_utf8(read_raw_element_xml_io_bytes(xml_reader, start)?.into_vec())
    .map_err(|err| SdkError::CommonError(format!("invalid utf-8 xml fragment: {err}")))
}

#[cfg(feature = "mce")]
const MC_ALTERNATE_CONTENT_NAMES: &[&[u8]] = &[b"mc:AlternateContent", b"AlternateContent"];
#[cfg(feature = "mce")]
const MC_CHOICE_NAMES: &[&[u8]] = &[b"mc:Choice", b"Choice"];
#[cfg(feature = "mce")]
const MC_FALLBACK_NAMES: &[&[u8]] = &[b"mc:Fallback", b"Fallback"];

#[cfg(feature = "mce")]
pub(crate) fn mce_choice_replacement_child_bytes(
  xml: &[u8],
  settings: &crate::sdk::MarkupCompatibilityProcessSettings,
  context: &crate::sdk::MceContext,
) -> Result<Option<Vec<Box<[u8]>>>, SdkError> {
  let mut reader = Reader::from_reader(xml);
  reader.config_mut().check_end_names = false;
  let mut fallback = None;

  loop {
    match reader.read_event()? {
      Event::Start(e) if qname_in(e.name().as_ref(), MC_ALTERNATE_CONTENT_NAMES) => {
        let namespaces = namespaces_from_context_with(context, &e)?;
        loop {
          match reader.read_event()? {
            Event::Start(e) if qname_in(e.name().as_ref(), MC_CHOICE_NAMES) => {
              let choice_namespaces = namespaces_with(&namespaces, &e)?;
              let requires = attr_value(&reader, &e, b"Requires")?;
              let children = read_mce_container_children_bytes(&mut reader, MC_CHOICE_NAMES)?;
              if choice_requires_supported(
                requires.as_deref(),
                &choice_namespaces,
                settings.target_file_format_version,
              )? {
                return Ok(Some(children));
              }
            }
            Event::Empty(e) if qname_in(e.name().as_ref(), MC_CHOICE_NAMES) => {
              let choice_namespaces = namespaces_with(&namespaces, &e)?;
              let requires = attr_value(&reader, &e, b"Requires")?;
              if choice_requires_supported(
                requires.as_deref(),
                &choice_namespaces,
                settings.target_file_format_version,
              )? {
                return Ok(Some(Vec::new()));
              }
            }
            Event::Start(e) if qname_in(e.name().as_ref(), MC_FALLBACK_NAMES) => {
              fallback = Some(read_mce_container_children_bytes(
                &mut reader,
                MC_FALLBACK_NAMES,
              )?);
            }
            Event::Empty(e) if qname_in(e.name().as_ref(), MC_FALLBACK_NAMES) => {
              fallback = Some(Vec::new());
            }
            Event::Start(_) => {
              skip_element(&mut reader)?;
            }
            Event::End(e) if qname_in(e.name().as_ref(), MC_ALTERNATE_CONTENT_NAMES) => {
              return Ok(Some(fallback.unwrap_or_default()));
            }
            Event::Eof => return Err(unexpected_eof("mce AlternateContent")),
            _ => {}
          }
        }
      }
      Event::Empty(e) if qname_in(e.name().as_ref(), MC_ALTERNATE_CONTENT_NAMES) => {
        return Ok(Some(Vec::new()));
      }
      Event::Start(e) => {
        return mce_unknown_element_replacement_bytes(&mut reader, e, context, false);
      }
      Event::Empty(e) => {
        return mce_unknown_element_replacement_bytes(&mut reader, e, context, true);
      }
      Event::Eof => return Ok(None),
      _ => {}
    }
  }
}

#[cfg(feature = "mce")]
fn mce_unknown_element_replacement_bytes(
  reader: &mut Reader<&[u8]>,
  start: quick_xml::events::BytesStart<'_>,
  context: &crate::sdk::MceContext,
  empty_tag: bool,
) -> Result<Option<Vec<Box<[u8]>>>, SdkError> {
  let qname = start.name();
  let qname = qname.as_ref();
  let namespaces = namespaces_from_context_with(context, &start)?;
  let is_ignorable = qname_prefix(qname).is_some_and(|prefix| {
    namespaces
      .iter()
      .rev()
      .find_map(|namespace| {
        let (namespace_prefix, namespace_uri) = namespace.parts();
        (namespace_prefix == prefix)
          .then_some(namespace_uri)
          .filter(|ns| context.is_ignorable_namespace_bytes(ns))
      })
      .is_some()
  });

  if is_ignorable && context.is_process_content_qname_bytes(qname) {
    if empty_tag {
      return Ok(Some(Vec::new()));
    }
    let end_name = start.name().as_ref().to_vec();
    return read_mce_container_children_bytes(reader, &[end_name.as_slice()]).map(Some);
  }

  if is_ignorable {
    if !empty_tag {
      skip_element(reader)?;
    }
    return Ok(Some(Vec::new()));
  }

  if !qname.contains(&b':') {
    if !empty_tag {
      skip_element(reader)?;
    }
    return Ok(None);
  }

  if !empty_tag {
    skip_element(reader)?;
  }
  Ok(None)
}

#[cfg(feature = "mce")]
fn read_mce_container_children_bytes(
  reader: &mut Reader<&[u8]>,
  end_names: &[&[u8]],
) -> Result<Vec<Box<[u8]>>, SdkError> {
  let mut children = Vec::new();
  loop {
    match reader.read_event()? {
      Event::Start(e) => {
        children.push(read_outer_xml_from_str_reader_bytes(reader, e, false)?);
      }
      Event::Empty(e) => {
        children.push(read_outer_xml_from_str_reader_bytes(reader, e, true)?);
      }
      Event::End(e) if qname_in(e.name().as_ref(), end_names) => return Ok(children),
      Event::Eof => return Err(unexpected_eof("mce choice/fallback")),
      _ => {}
    }
  }
}

#[cfg(feature = "mce")]
fn read_outer_xml_from_str_reader_bytes(
  reader: &mut Reader<&[u8]>,
  start: quick_xml::events::BytesStart<'_>,
  empty_tag: bool,
) -> Result<Box<[u8]>, SdkError> {
  let mut writer = quick_xml::Writer::new(std::io::Cursor::new(Vec::new()));
  if empty_tag {
    writer.write_event(Event::Empty(start))?;
    return Ok(writer.into_inner().into_inner().into_boxed_slice());
  }

  writer.write_event(Event::Start(start))?;
  let mut depth = 1usize;
  loop {
    let event = reader.read_event()?;
    match &event {
      Event::Start(_) => depth += 1,
      Event::End(_) => depth -= 1,
      Event::Eof => return Err(unexpected_eof("mce selected child")),
      _ => {}
    }
    writer.write_event(event)?;
    if depth == 0 {
      break;
    }
  }

  Ok(writer.into_inner().into_inner().into_boxed_slice())
}

#[cfg(feature = "mce")]
fn qname_prefix(qname: &[u8]) -> Option<&[u8]> {
  qname
    .iter()
    .position(|byte| *byte == b':')
    .map(|index| &qname[..index])
}

#[cfg(feature = "mce")]
fn skip_element(reader: &mut Reader<&[u8]>) -> Result<(), SdkError> {
  let mut depth = 1usize;
  loop {
    match reader.read_event()? {
      Event::Start(_) => depth += 1,
      Event::End(_) => {
        depth -= 1;
        if depth == 0 {
          return Ok(());
        }
      }
      Event::Eof => return Err(unexpected_eof("mce skipped element")),
      _ => {}
    }
  }
}

#[cfg(feature = "mce")]
fn attr_value(
  reader: &Reader<&[u8]>,
  start: &quick_xml::events::BytesStart<'_>,
  name: &[u8],
) -> Result<Option<Box<[u8]>>, SdkError> {
  for attr in start.attributes() {
    let attr = attr?;
    if attr.key.as_ref() == name {
      return if let Some(value) = attr_raw_value(&attr) {
        Ok(Some(value.into()))
      } else {
        Ok(Some(
          decode_attr_value(&attr, reader.decoder())?
            .into_bytes()
            .into_boxed_slice(),
        ))
      };
    }
  }
  Ok(None)
}

#[cfg(feature = "mce")]
fn choice_requires_supported(
  requires: Option<&[u8]>,
  namespaces: &[crate::common::XmlNamespace],
  target: crate::sdk::FileFormatVersion,
) -> Result<bool, SdkError> {
  let Some(requires) = requires else {
    return Ok(false);
  };
  for prefix in requires
    .split(u8::is_ascii_whitespace)
    .filter(|part| !part.is_empty())
  {
    let Some(ns) = namespaces.iter().rev().find_map(|namespace| {
      let (namespace_prefix, namespace_uri) = namespace.parts();
      (namespace_prefix == prefix).then_some(namespace_uri)
    }) else {
      return Ok(false);
    };
    if !namespace_supported(ns, target) {
      return Ok(false);
    }
  }
  Ok(true)
}

#[cfg(feature = "mce")]
fn namespaces_with(
  namespaces: &[crate::common::XmlNamespace],
  start: &quick_xml::events::BytesStart<'_>,
) -> Result<Vec<crate::common::XmlNamespace>, SdkError> {
  let mut merged = namespaces.to_vec();
  merged.extend(namespace_decls(start)?);
  Ok(merged)
}

#[cfg(feature = "mce")]
fn namespaces_from_context_with(
  context: &crate::sdk::MceContext,
  start: &quick_xml::events::BytesStart<'_>,
) -> Result<Vec<crate::common::XmlNamespace>, SdkError> {
  let mut namespaces = context.namespaces().to_vec();
  namespaces.extend(namespace_decls(start)?);
  Ok(namespaces)
}

#[cfg(feature = "mce")]
fn namespace_decls(
  start: &quick_xml::events::BytesStart<'_>,
) -> Result<Vec<crate::common::XmlNamespace>, SdkError> {
  let mut namespaces = Vec::new();
  for attr in start.attributes() {
    let attr = attr?;
    let key = attr.key.as_ref();
    if let Some(prefix) = key.strip_prefix(b"xmlns:") {
      namespaces.push(crate::common::XmlNamespace::raw(
        prefix,
        attr.value.as_ref(),
      ));
    }
  }
  Ok(namespaces)
}

#[cfg(feature = "mce")]
fn namespace_supported(ns: &[u8], target: crate::sdk::FileFormatVersion) -> bool {
  crate::sdk::namespace_supported(ns, target)
}

#[cfg(feature = "mce")]
fn qname_in(name: &[u8], expected: &[&[u8]]) -> bool {
  if expected.contains(&name) {
    return true;
  }

  let mut index = name.len();
  while index > 0 {
    index -= 1;
    if name[index] == b':' {
      return expected.contains(&&name[index + 1..]);
    }
  }

  false
}

#[inline]
pub(crate) fn read_root_start_borrowed<'de>(
  reader: &mut SliceReader<'de>,
  owner: &'static str,
  tag_qname: &'static [u8],
  local_name: &'static [u8],
  namespace_uri: Option<&'static [u8]>,
) -> Result<
  (
    quick_xml::events::BytesStart<'de>,
    bool,
    crate::common::XmlHeaderType,
  ),
  SdkError,
> {
  let mut xml_header = crate::common::XmlHeaderType::None;
  loop {
    match reader.next_tag_event()? {
      SliceTagEvent::Decl(standalone) => {
        xml_header = if standalone {
          crate::common::XmlHeaderType::Standalone
        } else {
          crate::common::XmlHeaderType::Plain
        };
      }
      SliceTagEvent::Start(e, empty) => {
        if root_start_matches(&e, tag_qname, local_name, namespace_uri)? {
          return Ok((e, empty, xml_header));
        }
        return Err(unexpected_tag(owner, owner, e.name().as_ref()));
      }
      SliceTagEvent::Eof => return Err(unexpected_eof(owner)),
      SliceTagEvent::End(_) | SliceTagEvent::Other => {}
    }
  }
}

#[inline]
pub(crate) fn read_root_start_io<R: std::io::BufRead>(
  reader: &mut IoReader<R>,
  owner: &'static str,
  tag_qname: &'static [u8],
  local_name: &'static [u8],
  namespace_uri: Option<&'static [u8]>,
) -> Result<
  (
    quick_xml::events::BytesStart<'static>,
    bool,
    crate::common::XmlHeaderType,
  ),
  SdkError,
> {
  let mut xml_header = crate::common::XmlHeaderType::None;
  loop {
    match reader.next_tag_event()? {
      IoTagEvent::Decl(standalone) => {
        xml_header = if standalone {
          crate::common::XmlHeaderType::Standalone
        } else {
          crate::common::XmlHeaderType::Plain
        };
      }
      IoTagEvent::Start(e, empty) => {
        if root_start_matches(&e, tag_qname, local_name, namespace_uri)? {
          return Ok((e, empty, xml_header));
        }
        return Err(unexpected_tag(owner, owner, e.name().as_ref()));
      }
      IoTagEvent::Eof => return Err(unexpected_eof(owner)),
      IoTagEvent::End(_) | IoTagEvent::Other => {}
    }
  }
}

#[inline]
pub(crate) fn read_root_start_borrowed_no_header<'de>(
  reader: &mut SliceReader<'de>,
  owner: &'static str,
  tag_qname: &'static [u8],
  local_name: &'static [u8],
  namespace_uri: Option<&'static [u8]>,
) -> Result<(quick_xml::events::BytesStart<'de>, bool), SdkError> {
  loop {
    match reader.next_tag_event()? {
      SliceTagEvent::Start(e, empty) => {
        if root_start_matches(&e, tag_qname, local_name, namespace_uri)? {
          return Ok((e, empty));
        }
        return Err(unexpected_tag(owner, owner, e.name().as_ref()));
      }
      SliceTagEvent::Eof => return Err(unexpected_eof(owner)),
      SliceTagEvent::Decl(_) | SliceTagEvent::End(_) | SliceTagEvent::Other => {}
    }
  }
}

#[inline]
pub(crate) fn read_root_start_io_no_header<R: std::io::BufRead>(
  reader: &mut IoReader<R>,
  owner: &'static str,
  tag_qname: &'static [u8],
  local_name: &'static [u8],
  namespace_uri: Option<&'static [u8]>,
) -> Result<(quick_xml::events::BytesStart<'static>, bool), SdkError> {
  loop {
    match reader.next_tag_event()? {
      IoTagEvent::Start(e, empty) => {
        if root_start_matches(&e, tag_qname, local_name, namespace_uri)? {
          return Ok((e, empty));
        }
        return Err(unexpected_tag(owner, owner, e.name().as_ref()));
      }
      IoTagEvent::Eof => return Err(unexpected_eof(owner)),
      IoTagEvent::Decl(_) | IoTagEvent::End(_) | IoTagEvent::Other => {}
    }
  }
}

#[inline]
fn root_start_matches(
  start: &quick_xml::events::BytesStart<'_>,
  _tag_qname: &[u8],
  local_name: &[u8],
  _namespace_uri: Option<&[u8]>,
) -> Result<bool, SdkError> {
  Ok(start.local_name().into_inner() == local_name)
}

pub(crate) fn root_element_matches_namespace_local(
  bytes: &[u8],
  namespace_uri: &[u8],
  local_name: &[u8],
) -> Result<bool, SdkError> {
  if let Some(bytes) = decode_utf16_xml_bytes(bytes)? {
    return root_element_matches_namespace_local(&bytes, namespace_uri, local_name);
  }

  let mut reader = Reader::from_reader(bytes);
  loop {
    match reader.read_event()? {
      Event::Start(e) | Event::Empty(e) => {
        let name = e.name();
        let name = name.as_ref();
        let (prefix, root_local_name) = split_qname(name);
        return Ok(
          root_local_name == local_name && root_namespace_matches(&e, prefix, namespace_uri)?,
        );
      }
      Event::Eof => return Ok(false),
      _ => {}
    }
  }
}

fn split_qname(qname: &[u8]) -> (Option<&[u8]>, &[u8]) {
  if let Some(index) = qname.iter().position(|byte| *byte == b':') {
    (Some(&qname[..index]), &qname[index + 1..])
  } else {
    (None, qname)
  }
}

fn root_namespace_matches(
  start: &quick_xml::events::BytesStart<'_>,
  prefix: Option<&[u8]>,
  namespace_uri: &[u8],
) -> Result<bool, SdkError> {
  for attr in start.attributes() {
    let attr = attr?;
    let key = attr.key.as_ref();
    if match prefix {
      Some(prefix) => key.strip_prefix(b"xmlns:") == Some(prefix),
      None => key == b"xmlns",
    } {
      return Ok(attr.value.as_ref() == namespace_uri);
    }
  }
  Ok(false)
}

#[inline]
pub(crate) fn write_escaped_str<W: std::io::Write>(
  writer: &mut W,
  value: &str,
) -> std::io::Result<()> {
  match quick_xml::escape::escape(value) {
    std::borrow::Cow::Borrowed(value) => writer.write_all(value.as_bytes()),
    std::borrow::Cow::Owned(value) => writer.write_all(value.as_bytes()),
  }
}

#[inline]
pub(crate) fn write_escaped_content_str<W: std::io::Write>(
  writer: &mut W,
  value: &str,
) -> std::io::Result<()> {
  match quick_xml::escape::minimal_escape(value) {
    std::borrow::Cow::Borrowed(value) => writer.write_all(value.as_bytes()),
    std::borrow::Cow::Owned(value) => writer.write_all(value.as_bytes()),
  }
}

#[inline]
pub(crate) fn write_escaped_content_text<W: std::io::Write, T: std::fmt::Display + ?Sized>(
  writer: &mut W,
  value: &T,
) -> std::io::Result<()> {
  write_escaped_content_str(writer, &value.to_string())
}

#[inline]
pub(crate) fn write_escaped_text<W: std::io::Write, T: std::fmt::Display + ?Sized>(
  writer: &mut W,
  value: &T,
) -> std::io::Result<()> {
  write_escaped_str(writer, &value.to_string())
}

#[inline]
pub(crate) fn write_attr_value<W: std::io::Write, T: std::fmt::Display + ?Sized>(
  writer: &mut W,
  attr_name: &str,
  value: &T,
) -> std::io::Result<()> {
  writer.write_all(b" ")?;
  writer.write_all(attr_name.as_bytes())?;
  writer.write_all(b"=\"")?;
  write_escaped_text(writer, value)?;
  writer.write_all(b"\"")
}

#[inline]
pub(crate) fn write_attr_value_str<W: std::io::Write>(
  writer: &mut W,
  attr_name: &str,
  value: &str,
) -> std::io::Result<()> {
  writer.write_all(b" ")?;
  writer.write_all(attr_name.as_bytes())?;
  writer.write_all(b"=\"")?;
  write_escaped_str(writer, value)?;
  writer.write_all(b"\"")
}

#[inline]
pub(crate) fn write_list_attr_value<W, T>(
  writer: &mut W,
  attr_name: &str,
  values: &[T],
) -> std::io::Result<()>
where
  W: std::io::Write,
  T: std::fmt::Display,
{
  writer.write_all(b" ")?;
  writer.write_all(attr_name.as_bytes())?;
  writer.write_all(b"=\"")?;
  write_list_value(writer, values)?;
  writer.write_all(b"\"")
}

#[inline]
pub(crate) fn write_list_value<W, T>(writer: &mut W, values: &[T]) -> std::io::Result<()>
where
  W: std::io::Write,
  T: std::fmt::Display,
{
  let mut iter = values.iter();
  if let Some(first) = iter.next() {
    write_escaped_text(writer, first)?;
    for value in iter {
      writer.write_all(b" ")?;
      write_escaped_text(writer, value)?;
    }
  }
  Ok(())
}

#[inline]
pub(crate) fn write_list_text_content_value<W, T>(
  writer: &mut W,
  values: &[T],
) -> std::io::Result<()>
where
  W: std::io::Write,
  T: std::fmt::Display,
{
  let mut iter = values.iter();
  if let Some(first) = iter.next() {
    write_escaped_content_text(writer, first)?;
    for value in iter {
      writer.write_all(b" ")?;
      write_escaped_content_text(writer, value)?;
    }
  }
  Ok(())
}

#[inline]
pub(crate) fn write_twips_measure_value<W: std::io::Write>(
  writer: &mut W,
  value: &crate::simple_type::TwipsMeasureValue,
) -> std::io::Result<()> {
  match value {
    crate::simple_type::TwipsMeasureValue::Twips(value) => write_escaped_text(writer, value),
    crate::simple_type::TwipsMeasureValue::UniversalMeasure(value) => {
      write_escaped_str(writer, value.to_lexical().as_str())
    }
  }
}

#[inline]
pub(crate) fn write_signed_twips_measure_value<W: std::io::Write>(
  writer: &mut W,
  value: &crate::simple_type::SignedTwipsMeasureValue,
) -> std::io::Result<()> {
  match value {
    crate::simple_type::SignedTwipsMeasureValue::Twips(value) => write_escaped_text(writer, value),
    crate::simple_type::SignedTwipsMeasureValue::UniversalMeasure(value) => {
      write_escaped_str(writer, value.to_lexical().as_str())
    }
  }
}

#[inline]
pub(crate) fn write_decimal_number_or_percent_value<W: std::io::Write>(
  writer: &mut W,
  value: &crate::simple_type::DecimalNumberOrPercentValue,
) -> std::io::Result<()> {
  match value {
    crate::simple_type::DecimalNumberOrPercentValue::DecimalNumber(value) => {
      write_escaped_text(writer, value)
    }
    crate::simple_type::DecimalNumberOrPercentValue::Percent(value) => write_escaped_str(
      writer,
      crate::units::format_percent_lexical(*value).as_str(),
    ),
  }
}

#[inline]
pub(crate) fn write_measurement_or_percent_value<W: std::io::Write>(
  writer: &mut W,
  value: &crate::simple_type::MeasurementOrPercentValue,
) -> std::io::Result<()> {
  match value {
    crate::simple_type::MeasurementOrPercentValue::DecimalNumberOrPercent(value) => {
      write_decimal_number_or_percent_value(writer, value)
    }
    crate::simple_type::MeasurementOrPercentValue::UniversalMeasure(value) => {
      write_escaped_str(writer, value.to_lexical().as_str())
    }
  }
}

#[inline]
pub(crate) fn write_twips_measure_attr<W: std::io::Write>(
  writer: &mut W,
  attr_name: &str,
  value: &crate::simple_type::TwipsMeasureValue,
) -> std::io::Result<()> {
  write_simple_union_attr(writer, attr_name, |writer| {
    write_twips_measure_value(writer, value)
  })
}

#[inline]
pub(crate) fn write_signed_twips_measure_attr<W: std::io::Write>(
  writer: &mut W,
  attr_name: &str,
  value: &crate::simple_type::SignedTwipsMeasureValue,
) -> std::io::Result<()> {
  write_simple_union_attr(writer, attr_name, |writer| {
    write_signed_twips_measure_value(writer, value)
  })
}

#[inline]
pub(crate) fn write_decimal_number_or_percent_attr<W: std::io::Write>(
  writer: &mut W,
  attr_name: &str,
  value: &crate::simple_type::DecimalNumberOrPercentValue,
) -> std::io::Result<()> {
  write_simple_union_attr(writer, attr_name, |writer| {
    write_decimal_number_or_percent_value(writer, value)
  })
}

#[inline]
pub(crate) fn write_measurement_or_percent_attr<W: std::io::Write>(
  writer: &mut W,
  attr_name: &str,
  value: &crate::simple_type::MeasurementOrPercentValue,
) -> std::io::Result<()> {
  write_simple_union_attr(writer, attr_name, |writer| {
    write_measurement_or_percent_value(writer, value)
  })
}

#[inline]
fn write_simple_union_attr<W, F>(
  writer: &mut W,
  attr_name: &str,
  write_value: F,
) -> std::io::Result<()>
where
  W: std::io::Write,
  F: FnOnce(&mut W) -> std::io::Result<()>,
{
  writer.write_all(b" ")?;
  writer.write_all(attr_name.as_bytes())?;
  writer.write_all(b"=\"")?;
  write_value(writer)?;
  writer.write_all(b"\"")
}

#[inline]
pub(crate) fn write_xmlns_attr<W: std::io::Write>(
  writer: &mut W,
  prefix: Option<&[u8]>,
  uri: &[u8],
) -> std::io::Result<()> {
  writer.write_all(b" xmlns")?;
  if let Some(prefix) = prefix
    && !prefix.is_empty()
  {
    writer.write_all(b":")?;
    writer.write_all(prefix)?;
  }
  writer.write_all(b"=\"")?;
  writer.write_all(uri)?;
  writer.write_all(b"\"")
}

#[cfg(test)]
mod tests {
  use quick_xml::{Reader, events::Event};

  use super::decode_attr_value;

  #[test]
  fn attribute_decode_preserves_literal_newlines_for_round_trip() {
    let mut reader = Reader::from_str("<x a=\"one\ntwo &amp; three\"/>");
    let Event::Empty(event) = reader.read_event().expect("empty event") else {
      panic!("expected empty element");
    };
    let attr = event
      .attributes()
      .with_checks(false)
      .next()
      .expect("attribute")
      .expect("valid attribute");

    let value = decode_attr_value(&attr, reader.decoder()).expect("decoded attribute");

    assert_eq!(value, "one\ntwo & three");
  }
}
