//! Bounded LLSD notation lexer, parser, serializer, and public helper functions.

#![allow(clippy::missing_errors_doc)] // Public Result shapes are fixed by the mapped API.
#![allow(clippy::needless_pass_by_value)] // Owned arguments mirror mapped C# value parameters.
#![allow(clippy::unnecessary_wraps)] // Result return shapes are fixed by the mapped API.
#![allow(clippy::unused_self)] // Parser error helpers retain local cursor context call sites.

use crate::{Error, OSD};
use base64::Engine as _;
use libremetaverse_types::UUID;
use libremetaverse_types::compat::{StringReader, StringWriter, Uri, Utf16CodeUnit};
use std::collections::HashMap;
use std::mem::size_of;

const INDENT: &str = "  ";
const MAX_INPUT_BYTES: usize = OSD::DEFAULT_MAX_BINARY_BYTES;

const fn is_notation_whitespace(unit: u16) -> bool {
    unit == b' ' as u16 || unit == b'\t' as u16 || unit == b'\n' as u16 || unit == b'\r' as u16
}

pub(crate) fn deserialize_string(input: String) -> Result<OSD, Error> {
    if input.len() > MAX_INPUT_BYTES {
        return Err(parse_error(
            0,
            "notation LLSD input exceeds allocation limit",
        ));
    }
    let mut parser = Parser::new(&input);
    if parser.peek_non_whitespace().is_none() {
        return Ok(OSD::Undefined);
    }
    let value = parser.parse_value(0)?;
    value.validate_limits(
        OSD::DEFAULT_MAX_DEPTH,
        OSD::DEFAULT_MAX_NODES,
        OSD::DEFAULT_MAX_BINARY_BYTES,
    )?;
    Ok(value)
}

pub(crate) fn deserialize_reader(reader: StringReader) -> Result<OSD, Error> {
    deserialize_string(reader.0)
}

pub(crate) fn serialize(value: OSD) -> Result<String, Error> {
    serialize_internal(value, false)
}

pub(crate) fn serialize_formatted(value: OSD) -> Result<String, Error> {
    serialize_internal(value, true)
}

pub(crate) fn serialize_stream(value: OSD) -> Result<StringWriter, Error> {
    Ok(StringWriter(serialize(value)?))
}

pub(crate) fn serialize_stream_formatted(value: OSD) -> Result<StringWriter, Error> {
    Ok(StringWriter(serialize_formatted(value)?))
}

fn serialize_internal(value: OSD, formatted: bool) -> Result<String, Error> {
    value.validate_limits(
        OSD::DEFAULT_MAX_DEPTH,
        OSD::DEFAULT_MAX_NODES,
        OSD::DEFAULT_MAX_BINARY_BYTES,
    )?;
    let mut encoder = Encoder::new();
    if formatted {
        encoder.write_formatted(&value, "", 0)?;
    } else {
        encoder.write_compact(&value, 0)?;
    }
    Ok(encoder.finish())
}

pub(crate) fn buffer_characters_equal(
    reader: StringReader,
    buffer: Vec<Utf16CodeUnit>,
    offset: i32,
) -> Result<i32, Error> {
    if reader.0.len() > MAX_INPUT_BYTES || buffer.len() > MAX_INPUT_BYTES {
        return Err(Error::Argument);
    }
    let mut index = usize::try_from(offset).map_err(|_| Error::Argument)?;
    if index > buffer.len() {
        return Err(Error::Argument);
    }
    let input: Vec<_> = reader.0.encode_utf16().collect();
    let mut input_index = 0;
    while index < buffer.len()
        && input
            .get(input_index)
            .is_some_and(|unit| *unit == buffer[index].0)
    {
        index += 1;
        input_index += 1;
    }
    i32::try_from(index).map_err(|_| Error::Argument)
}

pub(crate) fn peek_and_skip_whitespace(reader: StringReader) -> Result<i32, Error> {
    if reader.0.len() > MAX_INPUT_BYTES {
        return Err(Error::Argument);
    }
    Ok(reader
        .0
        .encode_utf16()
        .find(|unit| !is_notation_whitespace(*unit))
        .map_or(-1, i32::from))
}

pub(crate) fn read_and_skip_whitespace(reader: StringReader) -> Result<i32, Error> {
    peek_and_skip_whitespace(reader)
}

pub(crate) fn get_length_in_brackets(reader: StringReader) -> Result<i32, Error> {
    if reader.0.len() > MAX_INPUT_BYTES {
        return Err(Error::Argument);
    }
    let mut parser = Parser::new(&reader.0);
    let length = parser.parse_parenthesized_length("invalid notation LLSD length")?;
    i32::try_from(length).map_err(|_| Error::Argument)
}

pub(crate) fn get_string_delimited_by(
    reader: StringReader,
    delimiter: Utf16CodeUnit,
) -> Result<String, Error> {
    if reader.0.len() > MAX_INPUT_BYTES {
        return Err(Error::Argument);
    }
    let mut parser = Parser::new(&reader.0);
    parser.parse_delimited(delimiter.0)
}

pub(crate) fn escape_character(value: String, delimiter: Utf16CodeUnit) -> Result<String, Error> {
    if value.len() > MAX_INPUT_BYTES {
        return Err(Error::Argument);
    }
    let delimiter = char::from_u32(u32::from(delimiter.0)).ok_or(Error::Argument)?;
    let mut output = String::with_capacity(value.len().saturating_add(2));
    for character in value.chars() {
        if character == '\\' || character == delimiter {
            output.push('\\');
        }
        output.push(character);
        if output.len() > MAX_INPUT_BYTES {
            return Err(Error::Argument);
        }
    }
    Ok(output)
}

pub(crate) fn unescape_character(value: String, delimiter: Utf16CodeUnit) -> Result<String, Error> {
    if value.len() > MAX_INPUT_BYTES {
        return Err(Error::Argument);
    }
    let delimiter = char::from_u32(u32::from(delimiter.0)).ok_or(Error::Argument)?;
    let collapsed = value.replace("\\\\", "\\");
    Ok(collapsed.replace(&format!("\\{delimiter}"), &delimiter.to_string()))
}

struct Parser {
    units: Vec<u16>,
    position: usize,
    nodes: usize,
    allocated: usize,
}

impl Parser {
    fn new(input: &str) -> Self {
        Self {
            units: input.encode_utf16().collect(),
            position: 0,
            nodes: 0,
            allocated: 0,
        }
    }

    fn parse_value(&mut self, depth: usize) -> Result<OSD, Error> {
        if depth > OSD::DEFAULT_MAX_DEPTH {
            return Err(self.error("notation LLSD nesting depth exceeded"));
        }
        self.nodes = self
            .nodes
            .checked_add(1)
            .ok_or_else(|| self.error("notation LLSD node count overflow"))?;
        if self.nodes > OSD::DEFAULT_MAX_NODES {
            return Err(self.error("notation LLSD node limit exceeded"));
        }
        self.skip_whitespace();
        let marker_position = self.position;
        let marker = self.read_unit("missing notation LLSD value marker")?;
        match marker {
            unit if unit == u16::from(b'!') => Ok(OSD::Undefined),
            unit if unit == u16::from(b'1') => Ok(OSD::Boolean(true)),
            unit if unit == u16::from(b'0') => Ok(OSD::Boolean(false)),
            unit if unit == u16::from(b't') => {
                self.consume_boolean_suffix("true", "invalid notation LLSD true value")?;
                Ok(OSD::Boolean(true))
            }
            unit if unit == u16::from(b'T') => {
                self.consume_boolean_suffix("TRUE", "invalid notation LLSD true value")?;
                Ok(OSD::Boolean(true))
            }
            unit if unit == u16::from(b'f') => {
                self.consume_boolean_suffix("false", "invalid notation LLSD false value")?;
                Ok(OSD::Boolean(false))
            }
            unit if unit == u16::from(b'F') => {
                self.consume_boolean_suffix("FALSE", "invalid notation LLSD false value")?;
                Ok(OSD::Boolean(false))
            }
            unit if unit == u16::from(b'i') => self.parse_integer(),
            unit if unit == u16::from(b'r') => self.parse_real(),
            unit if unit == u16::from(b'u') => self.parse_uuid(),
            unit if unit == u16::from(b'b') => self.parse_binary(),
            unit if unit == u16::from(b's') => self.parse_sized_string(),
            unit if unit == u16::from(b'\'') || unit == u16::from(b'"') => {
                Ok(OSD::String(self.parse_delimited(unit)?))
            }
            unit if unit == u16::from(b'l') => self.parse_uri(),
            unit if unit == u16::from(b'd') => self.parse_date(),
            unit if unit == u16::from(b'[') => self.parse_array(depth),
            unit if unit == u16::from(b'{') => self.parse_map(depth),
            _ => Err(self.error_at(marker_position, "unknown notation LLSD type marker")),
        }
    }

    fn consume_boolean_suffix(
        &mut self,
        expected: &str,
        context: &'static str,
    ) -> Result<(), Error> {
        let expected: Vec<_> = expected.encode_utf16().collect();
        let mut matched = 1;
        while matched < expected.len()
            && self
                .units
                .get(self.position)
                .is_some_and(|unit| *unit == expected[matched])
        {
            matched += 1;
            self.position += 1;
        }
        if matched > 1 && matched < expected.len() {
            Err(self.error(context))
        } else {
            Ok(())
        }
    }

    fn parse_integer(&mut self) -> Result<OSD, Error> {
        let start = self.position;
        if self.peek_unit() == Some(u16::from(b'-')) {
            self.position += 1;
        }
        while self.peek_unit().is_some_and(is_ascii_digit) {
            self.position += 1;
        }
        let text = self.decode_range(start, self.position, "invalid notation LLSD integer")?;
        let value = text
            .parse::<i32>()
            .map_err(|_| self.error_at(start, "invalid notation LLSD integer"))?;
        Ok(OSD::Integer(value))
    }

    fn parse_real(&mut self) -> Result<OSD, Error> {
        let start = self.position;
        while self.peek_unit().is_some_and(|unit| {
            is_ascii_digit(unit) || matches!(unit, 0x2e | 0x65 | 0x45 | 0x2b | 0x2d)
        }) {
            self.position += 1;
        }
        let text = self.decode_range(start, self.position, "invalid notation LLSD real")?;
        let value = text
            .parse::<f64>()
            .map_err(|_| self.error_at(start, "invalid notation LLSD real"))?;
        Ok(OSD::Real(value))
    }

    fn parse_uuid(&mut self) -> Result<OSD, Error> {
        let start = self.position;
        let end = start
            .checked_add(36)
            .ok_or_else(|| self.error("notation LLSD UUID length overflow"))?;
        let text = self.decode_range(start, end, "truncated notation LLSD UUID")?;
        self.position = end;
        let uuid = UUID::new_with_string(text)
            .map_err(|_| self.error_at(start, "invalid notation LLSD UUID"))?;
        Ok(OSD::UUID(uuid))
    }

    fn parse_binary(&mut self) -> Result<OSD, Error> {
        let marker_position = self.position.saturating_sub(1);
        let value = if self.peek_unit() == Some(u16::from(b'(')) {
            let length = self.parse_parenthesized_length("invalid notation LLSD binary length")?;
            let delimiter = self.read_quote("missing notation LLSD raw binary delimiter")?;
            let start = self.position;
            let end = start
                .checked_add(length)
                .ok_or_else(|| self.error("notation LLSD binary length overflow"))?;
            let units = self
                .units
                .get(start..end)
                .ok_or_else(|| self.error("truncated notation LLSD raw binary"))?;
            let bytes: Vec<u8> = units
                .iter()
                .copied()
                .map(|unit| {
                    u8::try_from(unit).map_err(|_| {
                        self.error_at(start, "invalid notation LLSD raw binary character")
                    })
                })
                .collect::<Result<_, _>>()?;
            self.position = end;
            self.expect_unit(delimiter, "missing notation LLSD raw binary end delimiter")?;
            bytes
        } else {
            let base_start = self.position;
            let base = self.read_ascii_digits(2, "invalid notation LLSD binary base")?;
            let delimiter = self.read_quote("missing notation LLSD binary delimiter")?;
            let encoded = self.parse_delimited(delimiter)?;
            match base.as_str() {
                "64" => {
                    decode_base64(&encoded).map_err(|context| self.error_at(base_start, context))?
                }
                "16" => {
                    decode_base16(&encoded).map_err(|context| self.error_at(base_start, context))?
                }
                _ => {
                    return Err(
                        self.error_at(marker_position, "unsupported notation LLSD binary base")
                    );
                }
            }
        };
        self.add_allocation(
            value.len(),
            "notation LLSD binary allocation limit exceeded",
        )?;
        Ok(OSD::Binary(value))
    }

    fn parse_sized_string(&mut self) -> Result<OSD, Error> {
        let length = self.parse_parenthesized_length("invalid notation LLSD string length")?;
        let delimiter = self.read_quote("missing notation LLSD string delimiter")?;
        let start = self.position;
        let end = start
            .checked_add(length)
            .ok_or_else(|| self.error("notation LLSD string length overflow"))?;
        let value = self.decode_range(start, end, "truncated notation LLSD string")?;
        self.position = end;
        self.expect_unit(delimiter, "missing notation LLSD string end delimiter")?;
        self.add_allocation(value.len(), "notation LLSD text allocation limit exceeded")?;
        Ok(OSD::String(value))
    }

    fn parse_uri(&mut self) -> Result<OSD, Error> {
        let delimiter = self.read_quote("missing notation LLSD URI delimiter")?;
        let start = self.position;
        let value = self.parse_delimited(delimiter)?;
        if value
            .chars()
            .any(|character| character == '\0' || character.is_control())
        {
            return Err(self.error_at(start, "invalid notation LLSD URI"));
        }
        Ok(OSD::Uri(Uri(value)))
    }

    fn parse_date(&mut self) -> Result<OSD, Error> {
        let delimiter = self.read_quote("missing notation LLSD date delimiter")?;
        let start = self.position;
        let value = self.parse_delimited(delimiter)?;
        let date = crate::model::parse_system_time_for_codec(&value)
            .ok_or_else(|| self.error_at(start, "invalid notation LLSD date"))?;
        Ok(OSD::Date(date))
    }

    fn parse_array(&mut self, depth: usize) -> Result<OSD, Error> {
        let mut values = Vec::new();
        self.skip_whitespace();
        if self.consume_unit(u16::from(b']')) {
            return Ok(OSD::Array(values));
        }
        loop {
            if values.len() >= OSD::DEFAULT_MAX_NODES {
                return Err(self.error("notation LLSD array node limit exceeded"));
            }
            values.push(self.parse_value(depth + 1)?);
            self.skip_whitespace();
            if self.consume_unit(u16::from(b']')) {
                break;
            }
            self.expect_unit(u16::from(b','), "invalid notation LLSD array delimiter")?;
            self.skip_whitespace();
            if self.consume_unit(u16::from(b']')) {
                break;
            }
        }
        self.add_allocation(
            values.len().saturating_mul(size_of::<OSD>()),
            "notation LLSD array allocation limit exceeded",
        )?;
        Ok(OSD::Array(values))
    }

    fn parse_map(&mut self, depth: usize) -> Result<OSD, Error> {
        let mut values = HashMap::new();
        self.skip_whitespace();
        if self.consume_unit(u16::from(b'}')) {
            return Ok(OSD::Map(values));
        }
        loop {
            if values.len() >= OSD::DEFAULT_MAX_NODES {
                return Err(self.error("notation LLSD map node limit exceeded"));
            }
            let key_position = self.position;
            let OSD::String(key) = self.parse_value(depth + 1)? else {
                return Err(self.error_at(key_position, "invalid notation LLSD map key"));
            };
            self.skip_whitespace();
            self.expect_unit(u16::from(b':'), "invalid notation LLSD map key delimiter")?;
            let value = self.parse_value(depth + 1)?;
            values.insert(key, value);
            self.skip_whitespace();
            if self.consume_unit(u16::from(b'}')) {
                break;
            }
            self.expect_unit(u16::from(b','), "invalid notation LLSD map delimiter")?;
            self.skip_whitespace();
            if self.consume_unit(u16::from(b'}')) {
                break;
            }
        }
        self.add_allocation(
            values.len().saturating_mul(size_of::<(String, OSD)>()),
            "notation LLSD map allocation limit exceeded",
        )?;
        Ok(OSD::Map(values))
    }

    fn parse_parenthesized_length(&mut self, context: &'static str) -> Result<usize, Error> {
        self.skip_whitespace();
        let start = self.position;
        self.expect_unit(u16::from(b'('), context)?;
        let digits_start = self.position;
        while self.peek_unit().is_some_and(is_ascii_digit) {
            self.position += 1;
        }
        if self.position == digits_start {
            return Err(self.error_at(start, context));
        }
        let digits = self.decode_range(digits_start, self.position, context)?;
        self.expect_unit(u16::from(b')'), context)?;
        let length = digits
            .parse::<usize>()
            .map_err(|_| self.error_at(start, context))?;
        if length > MAX_INPUT_BYTES {
            return Err(self.error_at(start, context));
        }
        Ok(length)
    }

    fn parse_delimited(&mut self, delimiter: u16) -> Result<String, Error> {
        let start = self.position;
        let mut output = Vec::new();
        let mut escaped = false;
        loop {
            let position = self.position;
            let unit = self.read_unit("unterminated notation LLSD string")?;
            if escaped {
                output.push(match unit {
                    unit if unit == u16::from(b'a') => 0x07,
                    unit if unit == u16::from(b'b') => 0x08,
                    unit if unit == u16::from(b'f') => 0x0c,
                    unit if unit == u16::from(b'n') => 0x0a,
                    unit if unit == u16::from(b'r') => 0x0d,
                    unit if unit == u16::from(b't') => 0x09,
                    unit if unit == u16::from(b'v') => 0x0b,
                    _ => unit,
                });
                escaped = false;
            } else if unit == u16::from(b'\\') {
                escaped = true;
            } else if unit == delimiter {
                break;
            } else {
                output.push(unit);
            }
            if output.len() > MAX_INPUT_BYTES {
                return Err(self.error_at(position, "notation LLSD text allocation limit exceeded"));
            }
        }
        let value = String::from_utf16_lossy(&output);
        self.add_allocation(value.len(), "notation LLSD text allocation limit exceeded")?;
        if self.position < start {
            return Err(self.error("notation LLSD parser position overflow"));
        }
        Ok(value)
    }

    fn read_quote(&mut self, context: &'static str) -> Result<u16, Error> {
        let position = self.position;
        let delimiter = self.read_unit(context)?;
        if matches!(delimiter, 0x22 | 0x27) {
            Ok(delimiter)
        } else {
            Err(self.error_at(position, context))
        }
    }

    fn read_ascii_digits(&mut self, count: usize, context: &'static str) -> Result<String, Error> {
        let start = self.position;
        let end = start
            .checked_add(count)
            .ok_or_else(|| self.error(context))?;
        let units = self
            .units
            .get(start..end)
            .ok_or_else(|| self.error(context))?;
        if !units.iter().copied().all(is_ascii_digit) {
            return Err(self.error_at(start, context));
        }
        let value = String::from_utf16(units).map_err(|_| self.error_at(start, context))?;
        self.position = end;
        Ok(value)
    }

    fn decode_range(
        &self,
        start: usize,
        end: usize,
        context: &'static str,
    ) -> Result<String, Error> {
        let units = self
            .units
            .get(start..end)
            .ok_or_else(|| self.error_at(start, context))?;
        Ok(String::from_utf16_lossy(units))
    }

    fn peek_non_whitespace(&mut self) -> Option<u16> {
        self.skip_whitespace();
        self.peek_unit()
    }

    fn skip_whitespace(&mut self) {
        while self.peek_unit().is_some_and(is_notation_whitespace) {
            self.position += 1;
        }
    }

    fn peek_unit(&self) -> Option<u16> {
        self.units.get(self.position).copied()
    }

    fn read_unit(&mut self, context: &'static str) -> Result<u16, Error> {
        let unit = self.peek_unit().ok_or_else(|| self.error(context))?;
        self.position += 1;
        Ok(unit)
    }

    fn consume_unit(&mut self, expected: u16) -> bool {
        if self.peek_unit() == Some(expected) {
            self.position += 1;
            true
        } else {
            false
        }
    }

    fn expect_unit(&mut self, expected: u16, context: &'static str) -> Result<(), Error> {
        let position = self.position;
        if self.read_unit(context)? == expected {
            Ok(())
        } else {
            Err(self.error_at(position, context))
        }
    }

    fn add_allocation(&mut self, amount: usize, context: &'static str) -> Result<(), Error> {
        self.allocated = self
            .allocated
            .checked_add(amount)
            .ok_or_else(|| self.error(context))?;
        if self.allocated > OSD::DEFAULT_MAX_BINARY_BYTES {
            Err(self.error(context))
        } else {
            Ok(())
        }
    }

    const fn error(&self, context: &'static str) -> Error {
        self.error_at(self.position, context)
    }

    const fn error_at(&self, position: usize, context: &'static str) -> Error {
        parse_error(position, context)
    }
}

struct Encoder {
    output: String,
}

impl Encoder {
    fn new() -> Self {
        Self {
            output: String::with_capacity(128),
        }
    }

    fn finish(self) -> String {
        self.output
    }

    fn write_compact(&mut self, value: &OSD, depth: usize) -> Result<(), Error> {
        self.check_depth(depth)?;
        match value {
            OSD::Undefined => self.push('!'),
            OSD::Boolean(value) => self.push(if *value { 't' } else { 'f' }),
            OSD::Integer(value) => {
                self.push('i')?;
                self.push_str(&value.to_string())
            }
            OSD::Real(value) => {
                self.push('r')?;
                self.push_str(&crate::model::format_real_for_codec(*value))
            }
            OSD::UUID(value) => {
                self.push('u')?;
                self.push_str(&value.to_string())
            }
            OSD::String(value) => self.write_quoted(value, '\''),
            OSD::Binary(value) => {
                self.push_str("b64\"")?;
                self.push_str(&base64::engine::general_purpose::STANDARD.encode(value))?;
                self.push('"')
            }
            OSD::Date(value) => {
                self.push_str("d\"")?;
                self.push_str(&crate::model::format_system_time_for_codec(*value))?;
                self.push('"')
            }
            OSD::Uri(Uri(value)) => {
                self.push('l')?;
                self.write_quoted(value, '"')
            }
            OSD::Array(values) => {
                self.push('[')?;
                for (index, value) in values.iter().enumerate() {
                    if index > 0 {
                        self.push(',')?;
                    }
                    self.write_compact(value, depth + 1)?;
                }
                self.push(']')
            }
            OSD::Map(values) => {
                self.push('{')?;
                for (index, (key, value)) in sorted_entries(values).into_iter().enumerate() {
                    if index > 0 {
                        self.push(',')?;
                    }
                    self.write_quoted(key, '\'')?;
                    self.push(':')?;
                    self.write_compact(value, depth + 1)?;
                }
                self.push('}')
            }
            OSD::LlsdXml(_) => Err(parse_error(
                self.output.len(),
                "LLSD XML fragments have no notation LLSD representation",
            )),
        }
    }

    fn write_formatted(&mut self, value: &OSD, indent: &str, depth: usize) -> Result<(), Error> {
        self.check_depth(depth)?;
        match value {
            OSD::Array(values) => {
                let child_indent = format!("{indent}{INDENT}");
                self.write_array_formatted(values, &child_indent, depth)
            }
            OSD::Map(values) => {
                let child_indent = format!("{indent}{INDENT}");
                self.write_map_formatted(values, &child_indent, depth)
            }
            _ => self.write_compact(value, depth),
        }
    }

    fn write_array_formatted(
        &mut self,
        values: &[OSD],
        indent: &str,
        depth: usize,
    ) -> Result<(), Error> {
        self.push('\n')?;
        self.push_str(indent)?;
        self.push('[')?;
        for (index, value) in values.iter().enumerate() {
            if !matches!(value, OSD::Array(_) | OSD::Map(_)) {
                self.push('\n')?;
            }
            self.push_str(indent)?;
            self.push_str(INDENT)?;
            self.write_formatted(value, indent, depth + 1)?;
            if index + 1 < values.len() {
                self.push(',')?;
            }
        }
        self.push('\n')?;
        self.push_str(indent)?;
        self.push(']')
    }

    fn write_map_formatted(
        &mut self,
        values: &HashMap<String, OSD>,
        indent: &str,
        depth: usize,
    ) -> Result<(), Error> {
        self.push('\n')?;
        self.push_str(indent)?;
        self.push_str("{\n")?;
        let entries = sorted_entries(values);
        for (index, (key, value)) in entries.iter().enumerate() {
            self.push_str(indent)?;
            self.push_str(INDENT)?;
            self.write_quoted(key, '\'')?;
            self.push(':')?;
            self.write_formatted(value, indent, depth + 1)?;
            if index + 1 < entries.len() {
                self.push('\n')?;
                self.push_str(indent)?;
                self.push_str(INDENT)?;
                self.push_str(",\n")?;
            }
        }
        self.push('\n')?;
        self.push_str(indent)?;
        self.push('}')
    }

    fn write_quoted(&mut self, value: &str, delimiter: char) -> Result<(), Error> {
        self.push(delimiter)?;
        for character in value.chars() {
            if character == '\\' || character == delimiter {
                self.push('\\')?;
            }
            self.push(character)?;
        }
        self.push(delimiter)
    }

    fn check_depth(&self, depth: usize) -> Result<(), Error> {
        if depth > OSD::DEFAULT_MAX_DEPTH {
            Err(parse_error(
                self.output.len(),
                "notation LLSD nesting depth exceeded",
            ))
        } else {
            Ok(())
        }
    }

    fn push(&mut self, character: char) -> Result<(), Error> {
        let length = self
            .output
            .len()
            .checked_add(character.len_utf8())
            .ok_or(Error::Argument)?;
        if length > MAX_INPUT_BYTES {
            return Err(Error::Argument);
        }
        self.output.push(character);
        Ok(())
    }

    fn push_str(&mut self, value: &str) -> Result<(), Error> {
        let length = self
            .output
            .len()
            .checked_add(value.len())
            .ok_or(Error::Argument)?;
        if length > MAX_INPUT_BYTES {
            return Err(Error::Argument);
        }
        self.output.push_str(value);
        Ok(())
    }
}

fn sorted_entries(values: &HashMap<String, OSD>) -> Vec<(&String, &OSD)> {
    let mut entries: Vec<_> = values.iter().collect();
    entries.sort_unstable_by_key(|(key, _)| *key);
    entries
}

fn decode_base16(value: &str) -> Result<Vec<u8>, &'static str> {
    if !value.len().is_multiple_of(2) {
        return Err("invalid notation LLSD base16 length");
    }
    value
        .as_bytes()
        .chunks_exact(2)
        .map(|pair| {
            let high = hex_value(pair[0]).ok_or("invalid notation LLSD base16 digit")?;
            let low = hex_value(pair[1]).ok_or("invalid notation LLSD base16 digit")?;
            Ok((high << 4) | low)
        })
        .collect()
}

fn decode_base64(value: &str) -> Result<Vec<u8>, &'static str> {
    let compact: Vec<u8> = value
        .bytes()
        .filter(|byte| !matches!(byte, b' ' | b'\t' | b'\n' | b'\r'))
        .collect();
    base64::engine::general_purpose::STANDARD
        .decode(compact)
        .map_err(|_| "invalid notation LLSD base64 value")
}

const fn hex_value(value: u8) -> Option<u8> {
    match value {
        b'0'..=b'9' => Some(value - b'0'),
        b'a'..=b'f' => Some(value - b'a' + 10),
        b'A'..=b'F' => Some(value - b'A' + 10),
        _ => None,
    }
}

const fn is_ascii_digit(unit: u16) -> bool {
    unit >= b'0' as u16 && unit <= b'9' as u16
}

const fn parse_error(position: usize, context: &'static str) -> Error {
    Error::Parse { position, context }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::time::{Duration, UNIX_EPOCH};

    #[test]
    fn compact_and_formatted_round_trip_every_variant() {
        let value = OSD::Array(vec![
            OSD::Undefined,
            OSD::Boolean(true),
            OSD::Integer(-42),
            OSD::Real(2.5),
            OSD::UUID(
                UUID::new_with_string("97f4aeca-88a1-42a1-b385-b97b18abb255".into()).unwrap(),
            ),
            OSD::String("quote '\\ and 𐄷".into()),
            OSD::Binary(vec![0, 1, 255]),
            OSD::Date(UNIX_EPOCH + Duration::from_millis(1_199_134_150_100)),
            OSD::Uri(Uri("https://example.test/a path".into())),
            OSD::Map(HashMap::from([("nested".into(), OSD::Array(vec![]))])),
        ]);
        let compact = serialize(value.clone()).unwrap();
        assert_eq!(serialize_stream(value.clone()).unwrap().0, compact);
        assert_eq!(deserialize_string(compact).unwrap(), value);
        let formatted = serialize_formatted(value.clone()).unwrap();
        assert_eq!(
            serialize_stream_formatted(value.clone()).unwrap().0,
            formatted
        );
        assert!(formatted.contains('\n'));
        assert_eq!(deserialize_string(formatted).unwrap(), value);
        assert_eq!(
            serialize_formatted(OSD::Array(vec![OSD::Integer(1), OSD::Integer(2)])).unwrap(),
            "\n  [\n    i1,\n    i2\n  ]"
        );
    }

    #[test]
    fn accepted_string_binary_boolean_and_escape_variants_are_exact() {
        assert_eq!(
            deserialize_string("true".into()).unwrap(),
            OSD::Boolean(true)
        );
        assert_eq!(deserialize_string("T".into()).unwrap(), OSD::Boolean(true));
        assert_eq!(
            deserialize_string(r#"s(2)"𐄷""#.into()).unwrap(),
            OSD::String("𐄷".into())
        );
        assert_eq!(
            deserialize_string(r#"b16"0001fF""#.into()).unwrap(),
            OSD::Binary(vec![0, 1, 255])
        );
        assert_eq!(
            deserialize_string("b64\"AA E=\r\n\"".into()).unwrap(),
            OSD::Binary(vec![0, 1])
        );
        assert_eq!(
            deserialize_string("b(3)\"A\\0\"".into()).unwrap(),
            OSD::Binary(vec![b'A', b'\\', b'0'])
        );
        assert_eq!(
            deserialize_string(r"'a\n\t\'\\b'".into()).unwrap(),
            OSD::String("a\n\t'\\b".into())
        );
        assert_eq!(
            escape_character("a\\'b".into(), Utf16CodeUnit(u16::from(b'\''))).unwrap(),
            "a\\\\\\'b"
        );
        assert_eq!(
            unescape_character("a\\\\\\'b".into(), Utf16CodeUnit(u16::from(b'\''))).unwrap(),
            "a\\'b"
        );
        assert_eq!(
            get_length_in_brackets(StringReader(" \t(123)remaining".into())).unwrap(),
            123
        );
        assert_eq!(
            get_string_delimited_by(
                StringReader(r"line\nquote\'tail'ignored".into()),
                Utf16CodeUnit(u16::from(b'\'')),
            )
            .unwrap(),
            "line\nquote'tail"
        );
        assert_eq!(
            peek_and_skip_whitespace(StringReader(" \r\nX".into())).unwrap(),
            i32::from(b'X')
        );
    }

    #[test]
    fn malformed_inputs_report_positions_and_depth_is_bounded() {
        for (input, context) in [
            ("i", "invalid notation LLSD integer"),
            ("r", "invalid notation LLSD real"),
            ("u123", "truncated notation LLSD UUID"),
            ("b64\"%%%\"", "invalid notation LLSD base64 value"),
            ("b16\"0\"", "invalid notation LLSD base16 length"),
            ("s(4)\"abc\"", "missing notation LLSD string end delimiter"),
            ("'unterminated", "unterminated notation LLSD string"),
            ("[i1 i2]", "invalid notation LLSD array delimiter"),
            ("{'key'i1}", "invalid notation LLSD map key delimiter"),
        ] {
            let Error::Parse {
                position,
                context: actual,
            } = deserialize_string(input.into()).unwrap_err()
            else {
                panic!("malformed notation did not return a positional parse error");
            };
            assert!(position <= input.encode_utf16().count());
            assert_eq!(actual, context);
        }

        let mut nested = "[".repeat(OSD::DEFAULT_MAX_DEPTH + 1);
        nested.push('!');
        nested.push_str(&"]".repeat(OSD::DEFAULT_MAX_DEPTH + 1));
        assert!(matches!(
            deserialize_string(nested),
            Err(Error::Parse {
                context: "notation LLSD nesting depth exceeded",
                ..
            })
        ));
    }
}
