use std::{borrow::Cow, collections::BTreeMap};

use crate::{
    Error, Float, Format, IoResult, Result, SequenceDecoder, SequenceReader, SimpleValue, Strictness, Value,
    codec::{Argument, Head, HeadOrStop, Major},
    io::{HexReader, HexSliceReader, MyReader, SliceReader},
    limits,
    parse::Parser,
    tag::{NEG_BIG_INT, POS_BIG_INT},
    util::{trim_leading_zeros, u64_from_slice},
};

/// Configuration for CBOR decoding.
///
/// `DecodeOptions` controls the input format ([`Binary`](Format::Binary),
/// [`Hex`](Format::Hex), or [`Diagnostic`](Format::Diagnostic)) and the
/// limits the decoder enforces against hostile or malformed input.
/// Construct it with [`DecodeOptions::new`] (or `Default`), adjust
/// settings with the builder methods, and call [`decode`](Self::decode)
/// or [`read_from`](Self::read_from) for a single item, or
/// [`sequence_decoder`](Self::sequence_decoder) / [`sequence_reader`](Self::sequence_reader)
/// for a CBOR sequence.
///
/// The convenience methods on [`Value`] ([`decode`](Value::decode),
/// [`decode_hex`](Value::decode_hex), [`read_from`](Value::read_from),
/// [`read_hex_from`](Value::read_hex_from)) all forward to a default
/// `DecodeOptions`. Use this type directly when you need to decode
/// diagnostic notation, iterate a sequence, relax a limit for a known
/// input, or tighten one for untrusted input.
///
/// # Options
///
/// | Option | Default | Purpose |
/// |---|---|---|
/// | [`format`](Self::format) | [`Binary`](Format::Binary) | Input syntax: binary, hex text, or diagnostic notation. |
/// | [`recursion_limit`](Self::recursion_limit) | 200 | Maximum nesting depth of arrays, maps, and tags. |
/// | [`length_limit`](Self::length_limit) | 1,000,000,000 | Maximum declared element count of a single array, map, byte string, or text string. |
/// | [`oom_mitigation`](Self::oom_mitigation) | 100,000,000 | Byte budget for speculative pre-allocation. |
/// | [`strictness`](Self::strictness) | [`Strictness::STRICT`] | Which non-deterministic encodings the decoder accepts and normalizes. |
///
/// ## `recursion_limit`
///
/// Each array, map, or tag consumes one unit of recursion budget for
/// its contents. Exceeding the limit returns [`Error::NestingTooDeep`].
/// The limit protects against stack overflow on adversarial input and
/// should be well below the stack a thread has available.
///
/// ## `length_limit`
///
/// Applies to the length field in the CBOR head of arrays, maps, byte
/// strings, and text strings. It caps the declared size before any
/// bytes are read, so a malicious header claiming a petabyte-long
/// string is rejected immediately with [`Error::LengthTooLarge`]. The
/// limit does not restrict total input size; a valid document may
/// contain many items each up to the limit.
///
/// ## `oom_mitigation`
///
/// CBOR encodes lengths in the head, so a decoder is tempted to
/// pre-allocate a `Vec` of the declared capacity. On hostile input
/// that is a trivial amplification attack: a few bytes on the wire
/// reserve gigabytes of memory. `oom_mitigation` is a byte budget,
/// shared across the current decode, that caps the total amount of
/// speculative capacity the decoder may reserve for array backing
/// storage. Once the budget is exhausted, further arrays start empty
/// and grow on demand. Decoding still succeeds if the input is
/// well-formed; only the up-front reservation is bounded.
///
/// The budget is consumed, not refilled: a deeply nested structure
/// with many small arrays can drain it early and decode the tail with
/// zero pre-allocation. That is the intended behavior.
///
/// # Examples
///
/// Decode binary CBOR with default limits:
///
/// ```
/// use cbor_core::DecodeOptions;
///
/// let v = DecodeOptions::new().decode(&[0x18, 42]).unwrap();
/// assert_eq!(v.to_u32().unwrap(), 42);
/// ```
///
/// Switch the input format to hex text or diagnostic notation:
///
/// ```
/// use cbor_core::{DecodeOptions, Format};
///
/// let v = DecodeOptions::new().format(Format::Hex).decode("182a").unwrap();
/// assert_eq!(v.to_u32().unwrap(), 42);
///
/// let v = DecodeOptions::new().format(Format::Diagnostic).decode("42").unwrap();
/// assert_eq!(v.to_u32().unwrap(), 42);
/// ```
///
/// Tighten limits for input from an untrusted source:
///
/// ```
/// use cbor_core::DecodeOptions;
///
/// let strict = DecodeOptions::new()
///     .recursion_limit(16)
///     .length_limit(4096)
///     .oom_mitigation(64 * 1024);
///
/// assert!(strict.decode(&[0x18, 42]).is_ok());
/// ```
#[derive(Debug, Clone)]
pub struct DecodeOptions {
    pub(crate) format: Format,
    pub(crate) recursion_limit: u16,
    pub(crate) length_limit: u64,
    pub(crate) oom_mitigation: usize,
    pub(crate) strictness: Strictness,
}

impl Default for DecodeOptions {
    fn default() -> Self {
        Self::new()
    }
}

impl DecodeOptions {
    /// Create a new set of options with the crate defaults.
    ///
    /// ```
    /// use cbor_core::DecodeOptions;
    ///
    /// let opts = DecodeOptions::new();
    /// let v = opts.decode(&[0x18, 42]).unwrap();
    /// assert_eq!(v.to_u32().unwrap(), 42);
    /// ```
    #[must_use]
    pub const fn new() -> Self {
        Self {
            format: Format::Binary,
            recursion_limit: limits::RECURSION_LIMIT,
            length_limit: limits::LENGTH_LIMIT,
            oom_mitigation: limits::OOM_MITIGATION,
            strictness: Strictness::STRICT,
        }
    }

    /// Select the input format: [`Binary`](Format::Binary),
    /// [`Hex`](Format::Hex), or [`Diagnostic`](Format::Diagnostic).
    ///
    /// Default: [`Format::Binary`].
    ///
    /// ```
    /// use cbor_core::{DecodeOptions, Format};
    ///
    /// let hex = DecodeOptions::new().format(Format::Hex).decode("182a").unwrap();
    /// let bin = DecodeOptions::new().decode(&[0x18, 0x2a]).unwrap();
    /// assert_eq!(hex, bin);
    ///
    /// let v = DecodeOptions::new().format(Format::Diagnostic).decode("42").unwrap();
    /// assert_eq!(v.to_u32().unwrap(), 42);
    /// ```
    #[must_use]
    pub const fn format(mut self, format: Format) -> Self {
        self.format = format;
        self
    }

    /// Set the maximum nesting depth of arrays, maps, and tags.
    ///
    /// Default: 200. Input that exceeds the limit returns
    /// [`Error::NestingTooDeep`].
    ///
    /// ```
    /// use cbor_core::{DecodeOptions, Error};
    ///
    /// // Two nested one-element arrays: 0x81 0x81 0x00
    /// let err = DecodeOptions::new()
    ///     .recursion_limit(1)
    ///     .decode(&[0x81, 0x81, 0x00])
    ///     .unwrap_err();
    /// assert_eq!(err, Error::NestingTooDeep);
    /// ```
    #[must_use]
    pub const fn recursion_limit(mut self, limit: u16) -> Self {
        self.recursion_limit = limit;
        self
    }

    /// Set the maximum declared length for byte strings, text strings,
    /// arrays, and maps.
    ///
    /// Default: 1,000,000,000. Checked against the length field in the
    /// CBOR head before any bytes are consumed; an oversized declaration
    /// returns [`Error::LengthTooLarge`].
    ///
    /// ```
    /// use cbor_core::{DecodeOptions, Error};
    ///
    /// // A five-byte text string: 0x65 'h' 'e' 'l' 'l' 'o'
    /// let err = DecodeOptions::new()
    ///     .length_limit(4)
    ///     .decode(b"\x65hello")
    ///     .unwrap_err();
    /// assert_eq!(err, Error::LengthTooLarge);
    /// ```
    #[must_use]
    pub const fn length_limit(mut self, limit: u64) -> Self {
        self.length_limit = limit;
        self
    }

    /// Set the byte budget for speculative pre-allocation of array
    /// backing storage.
    ///
    /// Default: 100,000,000. Lower values trade a small amount of
    /// decoding throughput for stronger resistance to memory-amplification
    /// attacks. Valid input decodes regardless; only the up-front
    /// reservation is bounded.
    ///
    /// ```
    /// use cbor_core::DecodeOptions;
    ///
    /// // A two-element array: 0x82 0x01 0x02
    /// let v = DecodeOptions::new()
    ///     .oom_mitigation(0)
    ///     .decode(&[0x82, 0x01, 0x02])
    ///     .unwrap();
    /// assert_eq!(v.len(), Some(2));
    /// ```
    #[must_use]
    pub const fn oom_mitigation(mut self, bytes: usize) -> Self {
        self.oom_mitigation = bytes;
        self
    }

    /// Configure which non-deterministic encodings the decoder will
    /// accept. Default: [`Strictness::STRICT`], which rejects every
    /// deviation with [`Error::NonDeterministic`].
    ///
    /// Pass [`Strictness::LENIENT`] to accept all known deviations, or
    /// build a custom mix of `allow_*` fields. Tolerated input is
    /// normalized while decoding, so the resulting [`Value`] is
    /// canonical and re-encoding it produces CBOR::Core compliant
    /// bytes.
    ///
    /// ```
    /// use cbor_core::{DecodeOptions, Strictness, Value};
    ///
    /// // 255 wrongly encoded with a two byte argument; normalized on read.
    /// let v = DecodeOptions::new()
    ///     .strictness(Strictness::LENIENT)
    ///     .decode(&[0x19, 0x00, 0xff])
    ///     .unwrap();
    /// assert_eq!(v, Value::from(255));
    /// assert_eq!(v.encode(), vec![0x18, 0xff]);
    /// ```
    #[must_use]
    pub const fn strictness(mut self, strictness: Strictness) -> Self {
        self.strictness = strictness;
        self
    }

    /// Decode exactly one CBOR data item from an in-memory buffer.
    ///
    /// Takes the input by reference: `&[u8]`, `&[u8; N]`, `&Vec<u8>`,
    /// `&str`, `&String`, etc. all work via `T: AsRef<[u8]> + ?Sized`.
    /// In [`Format::Binary`], decoded text and byte strings borrow
    /// directly from the input slice and the returned [`Value`]
    /// inherits that lifetime; in [`Format::Hex`] and
    /// [`Format::Diagnostic`] the result is owned.
    ///
    /// The input must contain **exactly one** value. Use
    /// [`sequence_decoder`](Self::sequence_decoder) when the input is a
    /// CBOR sequence.
    ///
    /// # Errors
    ///
    /// * [`Error::InvalidFormat`] if any bytes remain after the value
    ///   has been read. In [`Format::Diagnostic`] mode trailing
    ///   whitespace and comments are accepted, but nothing else.
    /// * [`Error::UnexpectedEof`] for an empty buffer (in diagnostic
    ///   notation, one containing only whitespace and comments) or a
    ///   truncated value.
    /// * Any other [`Error`](crate::Error) variant that the decoder
    ///   raises for malformed, non-canonical, or oversized input.
    ///
    /// ```
    /// use cbor_core::{DecodeOptions, Format};
    ///
    /// let v = DecodeOptions::new().decode(&[0x18, 42]).unwrap();
    /// assert_eq!(v.to_u32().unwrap(), 42);
    ///
    /// let v = DecodeOptions::new().format(Format::Hex).decode("182a").unwrap();
    /// assert_eq!(v.to_u32().unwrap(), 42);
    ///
    /// let v = DecodeOptions::new()
    ///     .format(Format::Diagnostic)
    ///     .decode("42  / trailing comment is fine /")
    ///     .unwrap();
    /// assert_eq!(v.to_u32().unwrap(), 42);
    /// ```
    pub fn decode<'a, T>(&self, bytes: &'a T) -> Result<Value<'a>>
    where
        T: AsRef<[u8]> + ?Sized,
    {
        let bytes = bytes.as_ref();
        match self.format {
            Format::Binary => {
                let mut reader = SliceReader(bytes);
                let value = self.do_read(&mut reader, self.recursion_limit, self.oom_mitigation)?;
                if !reader.0.is_empty() {
                    return Err(Error::InvalidFormat);
                }
                Ok(value)
            }
            Format::Hex => {
                let mut reader = HexSliceReader(bytes);
                let value = self.do_read(&mut reader, self.recursion_limit, self.oom_mitigation)?;
                if !reader.0.is_empty() {
                    return Err(Error::InvalidFormat);
                }
                Ok(value)
            }
            Format::Diagnostic => {
                let mut parser = Parser::new(SliceReader(bytes), self.recursion_limit, self.strictness);
                parser.parse_complete()
            }
        }
    }

    /// Decode exactly one CBOR data item into an owned [`Value`].
    ///
    /// Takes the input by value: `Vec<u8>`, `&[u8]`, `&str`, and
    /// anything else that implements `AsRef<[u8]>` all work. Unlike
    /// [`decode`](Self::decode), the result never borrows from the
    /// input regardless of format: text and byte strings are always
    /// copied into owned allocations. The returned value can be held
    /// as `Value<'static>` and stored or sent across threads without
    /// any lifetime constraint.
    ///
    /// Use this when the input is short-lived (a temporary buffer, a
    /// `Vec` returned from a function, etc.) and the decoded value
    /// needs to outlive it. When the input already lives long enough,
    /// [`decode`](Self::decode) avoids the copies.
    ///
    /// The input must contain **exactly one** value. Use
    /// [`sequence_decoder`](Self::sequence_decoder) when the input is a
    /// CBOR sequence.
    ///
    /// # Errors
    ///
    /// Same as [`decode`](Self::decode).
    ///
    /// ```
    /// use cbor_core::{DecodeOptions, Format, Value};
    ///
    /// // Decode from a short-lived Vec without worrying about lifetimes.
    /// let bytes: Vec<u8> = vec![0x18, 42];
    /// let v: Value<'static> = DecodeOptions::new().decode_owned(bytes).unwrap();
    /// assert_eq!(v.to_u32().unwrap(), 42);
    ///
    /// // Hex and diagnostic formats work the same way.
    /// let v: Value<'static> = DecodeOptions::new()
    ///     .format(Format::Hex)
    ///     .decode_owned("182a")
    ///     .unwrap();
    /// assert_eq!(v.to_u32().unwrap(), 42);
    /// ```
    pub fn decode_owned<'a>(&self, bytes: impl AsRef<[u8]>) -> Result<Value<'a>> {
        let mut bytes = bytes.as_ref();

        match self.format {
            Format::Binary | Format::Hex => {
                let value = self.read_from(&mut bytes).map_err(|err| match err {
                    crate::IoError::Io(_io_error) => unreachable!(),
                    crate::IoError::Data(error) => error,
                })?;

                if bytes.is_empty() {
                    Ok(value)
                } else {
                    Err(Error::InvalidFormat)
                }
            }

            Format::Diagnostic => {
                let mut parser = Parser::new(SliceReader(bytes), self.recursion_limit, self.strictness);
                parser.parse_complete()
            }
        }
    }

    /// Read a single CBOR data item from a stream.
    ///
    /// Designed to be called repeatedly to pull successive elements of
    /// a CBOR sequence:
    ///
    /// * In [`Format::Binary`] and [`Format::Hex`] the reader is
    ///   consumed only up to the end of the item; any bytes after
    ///   remain in the stream.
    /// * In [`Format::Diagnostic`] trailing whitespace and comments
    ///   are consumed up to either end of stream or a top-level
    ///   separator comma (the comma is also consumed). Anything else
    ///   after the value fails with [`Error::InvalidFormat`].
    ///
    /// Bytes are read into an internal buffer, so the result is
    /// always owned and can be held as `Value<'static>`. For
    /// zero-copy decoding from a byte slice, use
    /// [`decode`](Self::decode) instead.
    ///
    /// # Errors
    ///
    /// * [`IoError::Io`](crate::IoError::Io) wrapping any I/O failure
    ///   reported by the reader.
    /// * [`IoError::Data`](crate::IoError::Data) wrapping any
    ///   [`Error`](crate::Error) variant raised by the decoder for
    ///   malformed, non-canonical, or oversized input.
    ///
    /// ```
    /// use cbor_core::{DecodeOptions, Format};
    ///
    /// let mut bytes: &[u8] = &[0x18, 42];
    /// let v = DecodeOptions::new().read_from(&mut bytes).unwrap();
    /// assert_eq!(v.to_u32().unwrap(), 42);
    ///
    /// let mut hex: &[u8] = b"182a";
    /// let v = DecodeOptions::new().format(Format::Hex).read_from(&mut hex).unwrap();
    /// assert_eq!(v.to_u32().unwrap(), 42);
    ///
    /// // Diagnostic: repeated read_from pulls successive sequence items.
    /// let mut diag: &[u8] = b"1, 2, 3";
    /// let opts = DecodeOptions::new().format(Format::Diagnostic);
    /// let a = opts.read_from(&mut diag).unwrap();
    /// let b = opts.read_from(&mut diag).unwrap();
    /// let c = opts.read_from(&mut diag).unwrap();
    /// assert_eq!(a.to_u32().unwrap(), 1);
    /// assert_eq!(b.to_u32().unwrap(), 2);
    /// assert_eq!(c.to_u32().unwrap(), 3);
    /// ```
    pub fn read_from<'a>(&self, reader: impl std::io::Read) -> IoResult<Value<'a>> {
        match self.format {
            Format::Binary => {
                let mut reader = reader;
                self.do_read(&mut reader, self.recursion_limit, self.oom_mitigation)
            }
            Format::Hex => {
                let mut reader = HexReader(reader);
                self.do_read(&mut reader, self.recursion_limit, self.oom_mitigation)
            }
            Format::Diagnostic => {
                let mut parser = Parser::new(reader, self.recursion_limit, self.strictness);
                parser.parse_stream_item()
            }
        }
    }

    /// Create an iterator over a CBOR sequence stored in memory.
    ///
    /// The returned [`SequenceDecoder`] yields each successive item of the
    /// sequence as `Result<Value<'a>>`, where `'a` is the lifetime of
    /// the input slice. In binary format, items borrow text and byte
    /// strings from the input; in hex and diagnostic format the items
    /// are owned. The iterator captures a snapshot of these options;
    /// subsequent changes to `self` do not affect it.
    ///
    /// ```
    /// use cbor_core::{DecodeOptions, Format};
    ///
    /// let opts = DecodeOptions::new().format(Format::Diagnostic);
    ///
    /// let items: Vec<_> = opts
    ///     .sequence_decoder(b"1, 2, 3,")
    ///     .collect::<Result<_, _>>()
    ///     .unwrap();
    /// assert_eq!(items.len(), 3);
    /// ```
    pub fn sequence_decoder<'a, T>(&self, input: &'a T) -> SequenceDecoder<'a>
    where
        T: AsRef<[u8]> + ?Sized,
    {
        SequenceDecoder::with_options(self.clone(), input.as_ref())
    }

    /// Create an iterator over a CBOR sequence read from a stream.
    ///
    /// The returned [`SequenceReader`] yields each successive item as
    /// `IoResult<Value<'static>>`. `None` indicates a clean end
    /// between items; a truncated item produces `Some(Err(_))`. Items
    /// are always owned (the bytes are read into an internal
    /// buffer); for zero-copy iteration use
    /// [`sequence_decoder`](Self::sequence_decoder) on a byte slice
    /// instead.
    ///
    /// ```
    /// use cbor_core::DecodeOptions;
    ///
    /// // Binary CBOR sequence: three one-byte items 0x01 0x02 0x03.
    /// let bytes: &[u8] = &[0x01, 0x02, 0x03];
    /// let items: Vec<_> = DecodeOptions::new()
    ///     .sequence_reader(bytes)
    ///     .collect::<Result<_, _>>()
    ///     .unwrap();
    /// assert_eq!(items.len(), 3);
    /// ```
    pub fn sequence_reader<R: std::io::Read>(&self, reader: R) -> SequenceReader<R> {
        SequenceReader::with_options(self.clone(), reader)
    }

    /// Decode exactly one CBOR data item from an arbitrary reader.
    /// Used by the sequence iterators to share the core decoding logic.
    pub(crate) fn decode_one<'a, R>(&self, reader: &mut R) -> std::result::Result<Value<'a>, R::Error>
    where
        R: MyReader<'a>,
        R::Error: From<Error>,
    {
        self.do_read(reader, self.recursion_limit, self.oom_mitigation)
    }

    fn do_read<'a, R>(
        &self,
        reader: &mut R,
        recursion_limit: u16,
        oom_mitigation: usize,
    ) -> std::result::Result<Value<'a>, R::Error>
    where
        R: MyReader<'a>,
        R::Error: From<Error>,
    {
        match self.read_value_or_break(reader, recursion_limit, oom_mitigation)? {
            Some(value) => Ok(value),
            // A break code where a value was expected (top level, array
            // item position, map key position, tag content) is malformed.
            None => Err(Error::Malformed.into()),
        }
    }

    /// Read the next item, returning `Ok(None)` when a break code stops
    /// the input. Used by indefinite-length container loops, which need
    /// to terminate on the break.
    fn read_value_or_break<'a, R>(
        &self,
        reader: &mut R,
        recursion_limit: u16,
        oom_mitigation: usize,
    ) -> std::result::Result<Option<Value<'a>>, R::Error>
    where
        R: MyReader<'a>,
        R::Error: From<Error>,
    {
        match HeadOrStop::read_from(reader)? {
            HeadOrStop::Definite(head) => self
                .process_head(head, reader, recursion_limit, oom_mitigation)
                .map(Some),

            HeadOrStop::Indefinite(major) => {
                if self.strictness.allow_indefinite_length {
                    self.process_indefinite(major, reader, recursion_limit, oom_mitigation)
                        .map(Some)
                } else {
                    Err(Error::NonDeterministic.into())
                }
            }

            HeadOrStop::Break => Ok(None),
        }
    }

    fn process_head<'a, R>(
        &self,
        head: Head,
        reader: &mut R,
        recursion_limit: u16,
        oom_mitigation: usize,
    ) -> std::result::Result<Value<'a>, R::Error>
    where
        R: MyReader<'a>,
        R::Error: From<Error>,
    {
        let is_float = head.initial_byte.major() == Major::SimpleOrFloat
            && matches!(head.argument, Argument::U16(_) | Argument::U32(_) | Argument::U64(_));

        if !is_float && !head.argument.is_deterministic() && !self.strictness.allow_non_shortest_integers {
            return Err(Error::NonDeterministic.into());
        }

        let this = match head.initial_byte.major() {
            Major::Unsigned => Value::Unsigned(head.value()),
            Major::Negative => Value::Negative(head.value()),

            Major::ByteString => {
                let len = head.value();
                if len > self.length_limit {
                    return Err(Error::LengthTooLarge.into());
                }
                Value::ByteString(reader.read_cow(len, oom_mitigation)?)
            }

            Major::TextString => {
                let len = head.value();
                if len > self.length_limit {
                    return Err(Error::LengthTooLarge.into());
                }
                let text = match reader.read_cow(len, oom_mitigation)? {
                    Cow::Borrowed(bytes) => Cow::Borrowed(std::str::from_utf8(bytes).map_err(Error::from)?),
                    Cow::Owned(bytes) => Cow::Owned(String::from_utf8(bytes).map_err(Error::from)?),
                };
                Value::TextString(text)
            }

            Major::Array => {
                let value = head.value();

                if value > self.length_limit {
                    return Err(Error::LengthTooLarge.into());
                }

                let Some(recursion_limit) = recursion_limit.checked_sub(1) else {
                    return Err(Error::NestingTooDeep.into());
                };

                let request: usize = value.try_into().or(Err(Error::LengthTooLarge))?;
                let granted = request.min(oom_mitigation / size_of::<Value>());
                let oom_mitigation = oom_mitigation - granted * size_of::<Value>();

                let mut vec = Vec::with_capacity(granted);

                for _ in 0..value {
                    vec.push(self.do_read(reader, recursion_limit, oom_mitigation)?);
                }

                Value::Array(vec)
            }

            Major::Map => {
                let value = head.value();

                if value > self.length_limit {
                    return Err(Error::LengthTooLarge.into());
                }

                let Some(recursion_limit) = recursion_limit.checked_sub(1) else {
                    return Err(Error::NestingTooDeep.into());
                };

                let mut map = BTreeMap::new();
                for _ in 0..value {
                    let key = self.do_read(reader, recursion_limit, oom_mitigation)?;
                    let val = self.do_read(reader, recursion_limit, oom_mitigation)?;
                    self.map_insert(&mut map, key, val)?;
                }

                Value::Map(map)
            }

            Major::Tag => {
                let Some(recursion_limit) = recursion_limit.checked_sub(1) else {
                    return Err(Error::NestingTooDeep.into());
                };

                let tag_number = head.value();
                let tag_content = self.do_read(reader, recursion_limit, oom_mitigation)?;

                // Big integer canonicalization (tag 2 / tag 3): the
                // payload must be a byte string longer than 8 bytes
                // (otherwise the value fits in u64) with no leading
                // zero byte.
                match tag_content {
                    Value::ByteString(bytes) if matches!(tag_number, POS_BIG_INT | NEG_BIG_INT) => {
                        let canonical = bytes.len() > 8 && bytes[0] != 0;
                        if canonical {
                            Value::Tag(tag_number, Box::new(Value::ByteString(bytes)))
                        } else if self.strictness.allow_oversized_bigints {
                            normalize_bigint(tag_number, bytes)
                        } else {
                            return Err(Error::NonDeterministic.into());
                        }
                    }
                    other => Value::Tag(tag_number, Box::new(other)),
                }
            }

            Major::SimpleOrFloat => match head.argument {
                Argument::None => Value::SimpleValue(SimpleValue(head.initial_byte.info())),
                Argument::U8(n) if n >= 32 => Value::SimpleValue(SimpleValue(n)),

                Argument::U16(bits) => Value::Float(Float::from_bits_u16(bits)),
                Argument::U32(bits) => self.checked_float(Float::from_bits_u32(bits))?,
                Argument::U64(bits) => self.checked_float(Float::from_bits_u64(bits))?,

                _ => return Err(Error::Malformed.into()),
            },
        };

        Ok(this)
    }

    fn checked_float<'a>(&self, float: Float) -> Result<Value<'a>> {
        if float.is_deterministic() {
            Ok(Value::Float(float))
        } else if self.strictness.allow_non_shortest_floats {
            Ok(Value::Float(float.shortest()))
        } else {
            Err(Error::NonDeterministic)
        }
    }

    /// Insert a key/value pair into a map under the active determinism
    /// policy. Used by both definite and indefinite-length map decoders.
    fn map_insert<'a>(&self, map: &mut BTreeMap<Value<'a>, Value<'a>>, key: Value<'a>, val: Value<'a>) -> Result<()> {
        if !self.strictness.allow_unsorted_map_keys
            && let Some(last) = map.last_entry()
            && *last.key() >= key
        {
            Err(Error::NonDeterministic)
        } else if map.insert(key, val).is_some() && !self.strictness.allow_duplicate_map_keys {
            Err(Error::NonDeterministic)
        } else {
            Ok(())
        }
    }

    /// Decode an indefinite-length container of the given major type.
    /// The break code that terminates the container is consumed.
    fn process_indefinite<'a, R>(
        &self,
        major: Major,
        reader: &mut R,
        recursion_limit: u16,
        oom_mitigation: usize,
    ) -> std::result::Result<Value<'a>, R::Error>
    where
        R: MyReader<'a>,
        R::Error: From<Error>,
    {
        match major {
            Major::ByteString => self.read_indefinite_bytes(reader, oom_mitigation),
            Major::TextString => self.read_indefinite_text(reader, oom_mitigation),
            Major::Array => self.read_indefinite_array(reader, recursion_limit, oom_mitigation),
            Major::Map => self.read_indefinite_map(reader, recursion_limit, oom_mitigation),
            _ => unreachable!("process_indefinite: invalid major"),
        }
    }

    /// Read a `(_ chunk*)` byte string. Each chunk is itself a
    /// definite-length byte string; an indefinite-length chunk or a
    /// chunk of a different major type is malformed even in lenient
    /// mode.
    fn read_indefinite_bytes<'a, R>(
        &self,
        reader: &mut R,
        oom_mitigation: usize,
    ) -> std::result::Result<Value<'a>, R::Error>
    where
        R: MyReader<'a>,
        R::Error: From<Error>,
    {
        let mut buf = Vec::new();
        let mut total: u64 = 0;

        loop {
            match HeadOrStop::read_from(reader)? {
                HeadOrStop::Break => break,

                HeadOrStop::Definite(head) if head.initial_byte.major() == Major::ByteString => {
                    if !head.argument.is_deterministic() && !self.strictness.allow_non_shortest_integers {
                        return Err(Error::NonDeterministic.into());
                    }

                    let chunk_len = head.value();

                    total = total.checked_add(chunk_len).ok_or(Error::LengthTooLarge)?;
                    if total > self.length_limit {
                        return Err(Error::LengthTooLarge.into());
                    }

                    let chunk = reader.read_cow(chunk_len, oom_mitigation)?;
                    buf.extend_from_slice(&chunk);
                }

                _ => return Err(Error::Malformed.into()),
            }
        }

        Ok(Value::ByteString(Cow::Owned(buf)))
    }

    /// Read a `(_ chunk*)` text string. Each chunk is independently
    /// validated as UTF-8 (per RFC 8949 §3.2.2).
    fn read_indefinite_text<'a, R>(
        &self,
        reader: &mut R,
        oom_mitigation: usize,
    ) -> std::result::Result<Value<'a>, R::Error>
    where
        R: MyReader<'a>,
        R::Error: From<Error>,
    {
        let mut buf = String::new();
        let mut total: u64 = 0;

        loop {
            match HeadOrStop::read_from(reader)? {
                HeadOrStop::Break => break,

                HeadOrStop::Definite(head) if head.initial_byte.major() == Major::TextString => {
                    if !head.argument.is_deterministic() && !self.strictness.allow_non_shortest_integers {
                        return Err(Error::NonDeterministic.into());
                    }

                    let chunk_len = head.value();

                    total = total.checked_add(chunk_len).ok_or(Error::LengthTooLarge)?;
                    if total > self.length_limit {
                        return Err(Error::LengthTooLarge.into());
                    }

                    let chunk = reader.read_cow(chunk_len, oom_mitigation)?;
                    buf.push_str(std::str::from_utf8(&chunk).map_err(Error::from)?);
                }

                _ => return Err(Error::Malformed.into()),
            }
        }

        Ok(Value::TextString(Cow::Owned(buf)))
    }

    fn read_indefinite_array<'a, R>(
        &self,
        reader: &mut R,
        recursion_limit: u16,
        oom_mitigation: usize,
    ) -> std::result::Result<Value<'a>, R::Error>
    where
        R: MyReader<'a>,
        R::Error: From<Error>,
    {
        let Some(recursion_limit) = recursion_limit.checked_sub(1) else {
            return Err(Error::NestingTooDeep.into());
        };

        let mut vec = Vec::new();

        for _ in 0..self.length_limit {
            match self.read_value_or_break(reader, recursion_limit, oom_mitigation)? {
                Some(item) => vec.push(item),
                None => return Ok(Value::Array(vec)),
            }
        }

        match HeadOrStop::read_from(reader)? {
            HeadOrStop::Definite(_) => Err(Error::LengthTooLarge.into()),
            HeadOrStop::Indefinite(_) => Err(Error::Malformed.into()),
            HeadOrStop::Break => Ok(Value::Array(vec)),
        }
    }

    fn read_indefinite_map<'a, R>(
        &self,
        reader: &mut R,
        recursion_limit: u16,
        oom_mitigation: usize,
    ) -> std::result::Result<Value<'a>, R::Error>
    where
        R: MyReader<'a>,
        R::Error: From<Error>,
    {
        let Some(recursion_limit) = recursion_limit.checked_sub(1) else {
            return Err(Error::NestingTooDeep.into());
        };

        let mut map = BTreeMap::new();

        for _ in 0..self.length_limit {
            match self.read_value_or_break(reader, recursion_limit, oom_mitigation)? {
                Some(key) => {
                    let value = self.do_read(reader, recursion_limit, oom_mitigation)?;
                    self.map_insert(&mut map, key, value)?;
                }
                None => return Ok(Value::Map(map)),
            }
        }

        match HeadOrStop::read_from(reader)? {
            HeadOrStop::Definite(_) => Err(Error::LengthTooLarge.into()),
            HeadOrStop::Indefinite(_) => Err(Error::Malformed.into()),
            HeadOrStop::Break => Ok(Value::Map(map)),
        }
    }
}

/// Normalize a non-canonical big integer payload.
///
/// Strips leading zero bytes and downcasts to
/// [`Value::Unsigned`] / [`Value::Negative`] when the magnitude fits
/// in a `u64`. Otherwise returns a tag 2 / tag 3 with a stripped
/// payload, preserving the [`Cow`] borrow when the input was borrowed.
fn normalize_bigint(tag_number: u64, bytes: Cow<'_, [u8]>) -> Value<'_> {
    fn integer<'b>(tag_number: u64, n: u64) -> Value<'b> {
        match tag_number {
            POS_BIG_INT => Value::Unsigned(n),
            NEG_BIG_INT => Value::Negative(n),
            _other => unreachable!("normalize_bigint: invalid tag"),
        }
    }

    match bytes {
        Cow::Borrowed(bytes) => {
            let trimmed = trim_leading_zeros(bytes);

            if let Ok(n) = u64_from_slice(trimmed) {
                integer(tag_number, n)
            } else {
                let bytes = trimmed.into();
                Value::Tag(tag_number, Box::new(Value::ByteString(bytes)))
            }
        }
        Cow::Owned(bytes) => {
            let trimmed = trim_leading_zeros(&bytes);

            if let Ok(n) = u64_from_slice(trimmed) {
                integer(tag_number, n)
            } else {
                let bytes = if trimmed.len() == bytes.len() {
                    bytes.into()
                } else {
                    trimmed.to_vec().into()
                };
                Value::Tag(tag_number, Box::new(Value::ByteString(bytes)))
            }
        }
    }
}
