use core::fmt;
use std::iter::Peekable;

use owo_colors::{
    colors::{Blue, BrightWhite, Green, White},
    OwoColorize, Style,
};
use smallvec::SmallVec;

/// A single step of a field path, used to identify which fields have already
/// been consumed so they can be skipped when rendering the "rest" (`{..}`).
#[derive(Clone, Copy)]
pub enum PathToken<'a> {
    Name(&'a str),
    Index(usize),
}

pub fn parse_json(input: &str) -> Result<Json<'_>, ParseError> {
    let mut json = Json::default();
    json.parse_replace(input)?;
    Ok(json)
}

#[derive(Clone, Default)]
pub enum Json<'a> {
    // first arg is the key value pairs, second is a list of keys used as
    // cache for parse_replace
    Object(JsonObject<'a>),
    Array(Vec<Json<'a>>),
    String(&'a str),
    Value(&'a str),
    #[default]
    Null,
    NullPrevObject(JsonObject<'a>),
    NullPrevArray(Vec<Json<'a>>),
}

impl<'a> Json<'a> {
    pub fn parse_replace(&mut self, input: &'a str) -> Result<(), ParseError> {
        let mut chars = input.trim().char_indices().peekable();
        if let Some((_, c)) = chars.peek() {
            if *c != '{' && *c != '[' {
                return Err(ParseError {
                    message: "JSON must be an object or array",
                    value: input.to_owned(),
                    index: 0,
                });
            }
        }

        self.parse_value_in_place(&mut chars, input)?;
        Ok(())
    }

    pub fn get(&self, key: &str) -> &Json<'_> {
        match self {
            Json::Object(obj) => obj.get(key),
            _ => &Json::Null,
        }
    }

    pub fn get_mut<'b>(&'b mut self, key: &str) -> Option<&'b mut Json<'a>> {
        match self {
            Json::Object(obj) => obj.get_mut(key),
            _ => None,
        }
    }

    pub fn get_i(&self, index: usize) -> &Json<'_> {
        match self {
            Json::Array(arr) => arr.get(index).unwrap_or(&Json::Null),
            _ => &Json::Null,
        }
    }

    pub fn get_i_mut<'b>(&'b mut self, index: usize) -> Option<&'b mut Json<'a>> {
        match self {
            Json::Array(arr) => arr.get_mut(index),
            _ => None,
        }
    }

    pub fn remove(&mut self, key: &str) -> Option<Json<'a>> {
        match self {
            Json::Object(obj) => obj.remove(key),
            _ => None,
        }
    }

    pub fn remove_i(&mut self, index: usize) -> Option<Json<'a>> {
        match self {
            Json::Array(arr) => arr.get_mut(index).map(|e| e.replace(Json::Null)),
            _ => None,
        }
    }

    /// Looks up a value by a JSON Pointer.
    ///
    /// JSON Pointer defines a string syntax for identifying a specific value
    /// within a JavaScript Object Notation (JSON) document.
    ///
    /// A Pointer is a Unicode string with the reference tokens separated by
    /// `/`. Inside tokens `/` is replaced by `~1` and `~` is replaced by
    /// `~0`. The addressed value is returned and if there is no such value
    /// `None` is returned.
    ///
    /// For more information read [RFC6901](https://tools.ietf.org/html/rfc6901).
    pub fn pointer(&self, pointer: &str) -> Option<&Json<'_>> {
        if pointer.is_empty() {
            return Some(self);
        }
        if !pointer.starts_with('/') {
            return None;
        }
        pointer
            .split('/')
            .skip(1)
            .map(|x| x.replace("~1", "/").replace("~0", "~"))
            .try_fold(self, |target, token| match target {
                Json::Object(map) => map.try_get(&token),
                Json::Array(list) => parse_index(&token).and_then(|x| list.get(x)),
                _ => None,
            })
    }

    /// Looks up a value by a JSON Pointer and returns a mutable reference to
    /// that value.
    ///
    /// JSON Pointer defines a string syntax for identifying a specific value
    /// within a JavaScript Object Notation (JSON) document.
    ///
    /// A Pointer is a Unicode string with the reference tokens separated by
    /// `/`. Inside tokens `/` is replaced by `~1` and `~` is replaced by
    /// `~0`. The addressed value is returned and if there is no such value
    /// `None` is returned.
    ///
    /// For more information read [RFC6901](https://tools.ietf.org/html/rfc6901).
    pub fn pointer_mut<'b>(&'b mut self, pointer: &str) -> Option<&'b mut Json<'a>> {
        if pointer.is_empty() {
            return Some(self);
        }
        if !pointer.starts_with('/') {
            return None;
        }

        pointer
            .split('/')
            .skip(1)
            .map(|x| x.replace("~1", "/").replace("~0", "~"))
            .try_fold(self, |target, token| match target {
                Json::Object(map) => map.get_mut(&token),
                Json::Array(list) => parse_index(&token).and_then(move |x| list.get_mut(x)),
                _ => None,
            })
    }

    pub fn is_null(&self) -> bool {
        matches!(
            self,
            Json::Null | Json::NullPrevObject(_) | Json::NullPrevArray(_)
        )
    }

    pub fn is_empty(&self) -> bool {
        match self {
            Json::Object(obj) => obj.is_empty(),
            Json::Array(arr) => arr.is_empty() || arr.iter().all(Json::is_null),
            Json::String(s) => s.is_empty(),
            Json::Value(_) => false,
            Json::Null | Json::NullPrevObject(_) | Json::NullPrevArray(_) => true,
        }
    }

    pub fn is_object(&self) -> bool { matches!(self, Json::Object(_)) }

    pub fn is_array(&self) -> bool { matches!(self, Json::Array(_)) }

    pub fn is_str(&self) -> bool { matches!(self, Json::String(_)) }

    pub fn is_value(&self) -> bool { matches!(self, Json::Value(_)) }

    pub fn as_object(&self) -> Option<&JsonObject<'a>> {
        match self {
            Json::Object(obj) => Some(obj),
            _ => None,
        }
    }

    pub fn as_object_mut(&mut self) -> Option<&mut JsonObject<'a>> {
        match self {
            Json::Object(obj) => Some(obj),
            _ => None,
        }
    }

    pub fn as_array(&self) -> Option<&Vec<Json<'a>>> {
        match self {
            Json::Array(arr) => Some(arr),
            _ => None,
        }
    }

    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Json<'a>>> {
        match self {
            Json::Array(arr) => Some(arr),
            _ => None,
        }
    }

    pub fn as_str(&self) -> Option<&str> {
        match self {
            Json::String(s) => Some(s),
            _ => None,
        }
    }

    pub fn as_value(&self) -> Option<&str> {
        match self {
            Json::Value(v) => Some(v),
            _ => None,
        }
    }

    // Replace self with a new value and return the previous value
    pub fn replace(&mut self, value: Json<'a>) -> Json<'a> { std::mem::replace(self, value) }

    fn parse_value_in_place<I>(
        &mut self,
        chars: &mut Peekable<I>,
        input: &'a str,
    ) -> Result<(), ParseError>
    where
        I: Iterator<Item = (usize, char)>,
    {
        match chars.peek().map(|&(_, c)| c) {
            Some('{') => {
                if let Json::Object(obj) = self {
                    obj.parse_object_in_place(chars, input)?;
                } else {
                    let this = self.replace(Json::Null);
                    if let Json::NullPrevObject(mut obj) = this {
                        obj.parse_object_in_place(chars, input)?;
                        *self = Json::Object(obj);
                    } else {
                        let mut obj = JsonObject(Vec::new());
                        obj.parse_object_in_place(chars, input)?;
                        *self = Json::Object(obj);
                    }
                }
            }
            Some('[') => {
                if let Json::Array(arr) = self {
                    parse_array_in_place(arr, chars, input)?;
                } else {
                    let this = self.replace(Json::Null);
                    if let Json::NullPrevArray(mut arr) = this {
                        parse_array_in_place(&mut arr, chars, input)?;
                        *self = Json::Array(arr);
                    } else {
                        let mut arr = Vec::new();
                        parse_array_in_place(&mut arr, chars, input)?;
                        *self = Json::Array(arr);
                    }
                }
            }
            Some('"') => {
                *self = parse_string(chars, input)?;
            }
            Some('n') => {
                parse_null(chars, input)?;
                self.replace_with_null();
            }
            Some(']') => {
                return Err(ParseError {
                    message: "Unexpected closing bracket",
                    value: input.to_owned(),
                    index: chars
                        .peek()
                        .map(|&(i, _)| i)
                        .unwrap_or_else(|| input.len() - 1),
                })
            }
            Some('}') => {
                return Err(ParseError {
                    message: "Unexpected closing brace",
                    value: input.to_owned(),
                    index: chars
                        .peek()
                        .map(|&(i, _)| i)
                        .unwrap_or_else(|| input.len() - 1),
                })
            }
            Some(_) => {
                *self = parse_raw_value(chars, input)?;
            }
            None => {
                return Err(ParseError {
                    message: "Unexpected end of input",
                    value: input.to_owned(),
                    index: input.len(),
                })
            }
        }

        Ok(())
    }

    fn replace_with_null(&mut self) {
        let prev = self.replace(Json::Null);

        if let Json::Object(obj) = prev {
            *self = Json::NullPrevObject(obj);
        } else if let Json::Array(arr) = prev {
            *self = Json::NullPrevArray(arr);
        } else if matches!(prev, Json::NullPrevObject(_)) || matches!(prev, Json::NullPrevArray(_))
        {
            *self = prev;
        }
    }
}

#[derive(Clone, Default)]
pub struct JsonObject<'a>(pub Vec<(&'a str, Json<'a>)>);

impl<'a> JsonObject<'a> {
    pub fn get(&self, key: &str) -> &Json<'_> { self.try_get(key).unwrap_or(&Json::Null) }

    pub fn get_mut<'b>(&'b mut self, key: &str) -> Option<&'b mut Json<'a>> {
        self.0.iter_mut().find(|(k, _)| k == &key).map(|(_, v)| v)
    }

    pub fn try_get(&self, key: &str) -> Option<&Json<'_>> {
        self.0.iter().find(|(k, _)| k == &key).map(|(_, v)| v)
    }

    pub fn insert(&mut self, key: &'a str, value: Json<'a>) {
        if let Some((_, v)) = self.0.iter_mut().find(|(k, _)| k == &key) {
            *v = value;
        } else {
            self.0.push((key, value));
        }
    }

    pub fn remove(&mut self, key: &str) -> Option<Json<'a>> {
        if let Some((_, val)) = self.0.iter_mut().find(|(k, _)| k == &key) {
            Some(val.replace(Json::Null))
        } else {
            None
        }
    }

    pub fn is_empty(&self) -> bool {
        if self.0.is_empty() {
            return true;
        }

        self.0.iter().all(|(_, v)| v.is_null())
    }

    pub fn iter(&self) -> std::slice::Iter<'_, (&'a str, Json<'a>)> { self.0.iter() }

    pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, (&'a str, Json<'a>)> { self.0.iter_mut() }

    pub fn parse_insert(&mut self, key: &'a str, input: &'a str) -> Result<(), ParseError> {
        if let Some((old_key, value)) = self.0.iter_mut().find(|(k, _)| k == &key) {
            *old_key = key;
            value.parse_replace(input)?;
        } else {
            let mut new_value = Json::Null;
            new_value.parse_replace(input)?;

            self.0.push((key, new_value));
        }

        Ok(())
    }

    fn parse_object_in_place<I>(
        &mut self,
        chars: &mut Peekable<I>,
        input: &'a str,
    ) -> Result<(), ParseError>
    where
        I: Iterator<Item = (usize, char)>,
    {
        // Consume the opening '{'
        let Some((_, '{')) = chars.next() else {
            return Err(ParseError {
                message: "Object doesn't have a starting brace",
                value: input.to_owned(),
                index: 0,
            });
        };

        skip_whitespace(chars);
        if let Some((_, '}')) = chars.peek() {
            chars.next(); // Consume the closing '}'

            // Set values to Json::Null for keys not found in the input
            for (_, value) in self.iter_mut() {
                value.replace_with_null();
            }

            return Ok(());
        }

        let mut count = 0;

        loop {
            let Ok(Json::String(key)) = parse_string(chars, input) else {
                return Err(ParseError {
                    message: "Unexpected char in object",
                    value: input.to_owned(),
                    index: chars
                        .peek()
                        .map(|&(i, _)| i - 1)
                        .unwrap_or_else(|| input.len() - 1),
                });
            };

            skip_whitespace(chars);
            if chars.next().map(|(_, c)| c) != Some(':') {
                return Err(ParseError {
                    message: "Expected colon ':' after key in object",
                    value: input.to_owned(),
                    // Use the index right after the key, which should be the
                    // current position
                    index: chars
                        .peek()
                        .map(|&(i, _)| i - 1)
                        .unwrap_or_else(|| input.len() - 1),
                });
            }

            skip_whitespace(chars);
            if let Some((old_key, value)) = self.0.get_mut(count) {
                *old_key = key;
                value.parse_value_in_place(chars, input)?;
            } else {
                let mut new_value = Json::Null;
                new_value.parse_value_in_place(chars, input)?;
                self.0.push((key, new_value));
            }

            count += 1;

            skip_whitespace(chars);
            match chars.peek().map(|&(_, c)| c) {
                Some(',') => {
                    chars.next();
                    skip_whitespace(chars);
                } // Consume and continue
                Some('}') => {
                    chars.next(); // Consume the closing '}'

                    for (_, value) in self.iter_mut().skip(count) {
                        value.replace_with_null();
                    }

                    return Ok(());
                }
                _ => {
                    return Err(ParseError {
                        message: "Expected comma or closing brace '}' in \
                                  object",
                        value: input.to_owned(),
                        index: chars.peek().map(|&(i, _)| i).unwrap_or_else(|| input.len()),
                    })
                }
            }
        }
    }
}

fn parse_array_in_place<'a, I>(
    arr: &mut Vec<Json<'a>>,
    chars: &mut Peekable<I>,
    input: &'a str,
) -> Result<(), ParseError>
where
    I: Iterator<Item = (usize, char)>,
{
    chars.next(); // Consume the opening '['

    skip_whitespace(chars);
    if let Some((_, ']')) = chars.peek() {
        chars.next(); // Consume the closing ']'

        for value in arr.iter_mut() {
            value.replace_with_null();
        }

        return Ok(());
    }

    let mut count = 0;

    loop {
        if count < arr.len() {
            arr[count].parse_value_in_place(chars, input)?;
        } else {
            let mut new_element = Json::Null;
            new_element.parse_value_in_place(chars, input)?;
            arr.push(new_element);
        }
        count += 1;

        skip_whitespace(chars);
        match chars.peek().map(|&(_, c)| c) {
            Some(',') => {
                chars.next();
                skip_whitespace(chars);
            } // Consume and continue
            Some(']') => {
                chars.next(); // Consume the closing ']'

                for value in arr.iter_mut().skip(count) {
                    value.replace_with_null();
                }

                return Ok(());
            } // Handle in the next loop iteration
            _ => {
                return Err(ParseError {
                    message: "Expected comma or closing bracket ']' in array",
                    value: input.to_owned(),
                    // Use the current position as the error index
                    index: chars
                        .peek()
                        .map(|&(i, _)| i)
                        .unwrap_or_else(|| input.len() - 1),
                });
            }
        }
    }
}

fn parse_string<'a, I>(chars: &mut Peekable<I>, input: &'a str) -> Result<Json<'a>, ParseError>
where
    I: Iterator<Item = (usize, char)>,
{
    // Consume the opening quote
    let Some((start_index, '"')) = chars.next() else {
        return Err(ParseError {
            message: "Expected opening quote for string",
            value: input.to_owned(),
            index: input.len(),
        });
    };

    while let Some((i, c)) = chars.next() {
        match c {
            '"' => return Ok(Json::String(&input[start_index + 1..i])),
            '\\' => {
                chars.next(); // Skip the character following the escape
            }
            _ => {}
        }
    }

    Err(ParseError {
        message: "Closing quote not found for string started",
        value: input.to_owned(),
        index: start_index,
    })
}

fn parse_null<'a, I>(chars: &mut Peekable<I>, input: &'a str) -> Result<Json<'a>, ParseError>
where
    I: Iterator<Item = (usize, char)>,
{
    let start_index = chars.peek().map(|&(i, _)| i).unwrap_or_else(|| input.len());
    if chars.next().map(|(_, c)| c) == Some('n')
        && chars.next().map(|(_, c)| c) == Some('u')
        && chars.next().map(|(_, c)| c) == Some('l')
        && chars.next().map(|(_, c)| c) == Some('l')
    {
        Ok(Json::Null)
    } else {
        Err(ParseError {
            message: "Invalid null value",
            value: input.to_owned(),
            // Point to the start of 'n' that led to expecting "null"
            index: start_index,
        })
    }
}

fn parse_raw_value<'a, I>(chars: &mut Peekable<I>, input: &'a str) -> Result<Json<'a>, ParseError>
where
    I: Iterator<Item = (usize, char)>,
{
    let start_index = chars.peek().map(|&(i, _)| i).unwrap_or_else(|| input.len());
    while let Some(&(i, c)) = chars.peek() {
        if c == ',' || c == ']' || c == '}' {
            return Ok(Json::Value(&input[start_index..i]));
        }
        chars.next();
    }

    Ok(Json::Value(&input[start_index..]))
}

// skip whitespaces and return the number of characters skipped
fn skip_whitespace<I>(chars: &mut Peekable<I>)
where
    I: Iterator<Item = (usize, char)>,
{
    while let Some(&(_, c)) = chars.peek() {
        if c.is_whitespace() {
            chars.next();
        } else {
            break;
        }
    }
}

impl Json<'_> {
    pub fn indented(&self, indent: usize) -> StyledJson<'_> {
        StyledJson {
            json: self,
            indent,
            styles: None,
        }
    }

    pub fn styled(&self, styles: MarkupStyles) -> StyledJson<'_> {
        StyledJson {
            json: self,
            indent: 0,
            styles: Some(styles),
        }
    }
}

pub struct StyledJson<'a> {
    json: &'a Json<'a>,
    indent: usize,
    styles: Option<MarkupStyles>,
}

impl StyledJson<'_> {
    pub fn indented(self, indent: usize) -> Self { Self { indent, ..self } }

    pub fn styled(self, styles: MarkupStyles) -> Self {
        Self {
            styles: Some(styles),
            ..self
        }
    }
}

impl fmt::Display for StyledJson<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        self.json.fmt_compact(f, &self.styles)
    }
}

impl fmt::Debug for StyledJson<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        self.json.fmt_pretty(f, self.indent, &self.styles)
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MarkupStyles {
    pub key: Style,
    pub value: Style,
    pub str: Style,
    pub syntax: Style,
}

impl Default for MarkupStyles {
    fn default() -> Self {
        Self {
            key: Style::new().fg::<Blue>(),
            value: Style::new().fg::<BrightWhite>(),
            str: Style::new().fg::<Green>(),
            syntax: Style::new().fg::<White>(),
        }
    }
}

impl fmt::Display for Json<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        self.fmt_compact(f, &None)
    }
}

impl fmt::Debug for Json<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        self.fmt_pretty(f, 0, &None)
    }
}

impl Json<'_> {
    fn fmt_compact(
        &self,
        f: &mut fmt::Formatter<'_>,
        styles: &Option<MarkupStyles>,
    ) -> Result<(), fmt::Error> {
        match self {
            Json::Object(obj) => {
                if obj.is_empty() {
                    return write_syntax(f, "{}", styles);
                }

                let mut non_nulls = obj.iter().filter(|(_, v)| !v.is_null());
                let Some((key, value)) = non_nulls.next() else {
                    return write_syntax(f, "{}", styles);
                };

                write_syntax(f, "{", styles)?;
                write_key(f, key, styles)?;
                write_syntax(f, ":", styles)?;
                value.fmt_compact(f, styles)?;

                for (key, value) in non_nulls {
                    write_syntax(f, ",", styles)?;
                    write_key(f, key, styles)?;
                    write_syntax(f, ":", styles)?;
                    value.fmt_compact(f, styles)?;
                }

                write_syntax(f, "}", styles)
            }
            Json::Array(arr) => {
                if arr.is_empty() {
                    return write_syntax(f, "[]", styles);
                }

                let mut non_nulls = arr.iter().filter(|v| !v.is_null());
                let Some(value) = non_nulls.next() else {
                    return write_syntax(f, "[]", styles);
                };

                write_syntax(f, "[", styles)?;
                value.fmt_compact(f, styles)?;

                for value in non_nulls {
                    write_syntax(f, ",", styles)?;
                    value.fmt_compact(f, styles)?;
                }
                write_syntax(f, "]", styles)
            }
            Json::String(v) => write_str(f, v, styles),
            Json::Value(v) => write_value(f, v, styles),
            Json::Null | Json::NullPrevObject(_) | Json::NullPrevArray(_) => {
                write_value(f, "null", styles)
            }
        }
    }

    fn fmt_pretty(
        &self,
        f: &mut fmt::Formatter<'_>,
        indent: usize,
        styles: &Option<MarkupStyles>,
    ) -> Result<(), fmt::Error> {
        match self {
            Json::Object(obj) => {
                if obj.is_empty() {
                    return write_syntax(f, "{}", styles);
                }

                let mut non_nulls = obj.iter().filter(|(_, v)| !v.is_null());
                let Some((key, value)) = non_nulls.next() else {
                    return write_syntax(f, "{}", styles);
                };

                write_syntax(f, "{", styles)?;
                write!(f, "\n{:indent$}", "", indent = (indent + 2))?;
                write_key(f, key, styles)?;
                write_syntax(f, ":", styles)?;
                write!(f, " ")?;
                value.fmt_pretty(f, indent + 2, styles)?;

                for (key, value) in non_nulls {
                    write_syntax(f, ",", styles)?;
                    write!(f, "\n{:indent$}", "", indent = (indent + 2))?;
                    write_key(f, key, styles)?;
                    write_syntax(f, ":", styles)?;
                    write!(f, " ")?;
                    value.fmt_pretty(f, indent + 2, styles)?;
                }

                write!(f, "\n{:indent$}", "", indent = indent)?;
                write_syntax(f, "}", styles)
            }
            Json::Array(arr) => {
                if arr.is_empty() {
                    return write_syntax(f, "[]", styles);
                }

                let mut non_nulls = arr.iter().filter(|v| !v.is_null());
                let Some(value) = non_nulls.next() else {
                    return write_syntax(f, "[]", styles);
                };

                write_syntax(f, "[", styles)?;
                write!(f, "\n{:indent$}", "", indent = (indent + 2))?;
                value.fmt_pretty(f, indent + 2, styles)?;

                for value in non_nulls {
                    write_syntax(f, ",", styles)?;
                    write!(f, "\n{:indent$}", "", indent = (indent + 2))?;
                    value.fmt_pretty(f, indent + 2, styles)?;
                }
                write!(f, "\n{:indent$}", "", indent = indent)?;
                write_syntax(f, "]", styles)
            }
            Json::String(v) => write_str(f, v, styles),
            Json::Value(v) => write_value(f, v, styles),
            Json::Null | Json::NullPrevObject(_) | Json::NullPrevArray(_) => {
                write_value(f, "null", styles)
            }
        }
    }

    /// Renders this value the same way as [`Self::fmt_compact`] /
    /// [`Self::fmt_pretty`], but skips any field paths listed in `excluded`.
    ///
    /// This is used for the "rest" field (`{..}`): the fields already consumed
    /// by earlier args (timestamp, level, message, ...) are skipped while the
    /// rest of the object is printed. Unlike the previous implementation it
    /// renders a *filtered view* of the original object instead of deep-cloning
    /// it and deleting the used fields, so no per-line allocation/clone happens.
    ///
    /// `indent` selects the layout: `None` => compact, `Some(n)` => pretty with
    /// base indentation `n`.
    pub fn fmt_rest(
        &self,
        f: &mut fmt::Formatter<'_>,
        excluded: &[&[PathToken<'_>]],
        indent: Option<usize>,
        styles: &Option<MarkupStyles>,
    ) -> fmt::Result {
        match self {
            Json::Object(obj) => {
                if !obj
                    .iter()
                    .any(|(key, value)| !value.is_null() && !is_excluded_key(excluded, key))
                {
                    return write_syntax(f, "{}", styles);
                }
                write_syntax(f, "{", styles)?;
                let mut first = true;
                for (key, value) in obj.iter() {
                    if value.is_null() || is_excluded_key(excluded, key) {
                        continue;
                    }
                    if !first {
                        write_syntax(f, ",", styles)?;
                    }
                    first = false;
                    if let Some(ind) = indent {
                        write!(f, "\n{:width$}", "", width = ind + 2)?;
                    }
                    write_key(f, key, styles)?;
                    write_syntax(f, ":", styles)?;
                    if indent.is_some() {
                        write!(f, " ")?;
                    }
                    let child = child_excluded_key(excluded, key);
                    write_rest_value(f, value, &child, indent, styles)?;
                }
                if let Some(ind) = indent {
                    if !first {
                        write!(f, "\n{:width$}", "", width = ind)?;
                    }
                }
                write_syntax(f, "}", styles)
            }
            Json::Array(arr) => {
                if !arr
                    .iter()
                    .enumerate()
                    .any(|(i, value)| !value.is_null() && !is_excluded(excluded, |t| token_matches_index(t, i)))
                {
                    return write_syntax(f, "[]", styles);
                }
                write_syntax(f, "[", styles)?;
                let mut first = true;
                for (i, value) in arr.iter().enumerate() {
                    if value.is_null() || is_excluded(excluded, |t| token_matches_index(t, i)) {
                        continue;
                    }
                    if !first {
                        write_syntax(f, ",", styles)?;
                    }
                    first = false;
                    if let Some(ind) = indent {
                        write!(f, "\n{:width$}", "", width = ind + 2)?;
                    }
                    let child = child_excluded(excluded, |t| token_matches_index(t, i));
                    write_rest_value(f, value, &child, indent, styles)?;
                }
                if let Some(ind) = indent {
                    if !first {
                        write!(f, "\n{:width$}", "", width = ind)?;
                    }
                }
                write_syntax(f, "]", styles)
            }
            _ => match indent {
                Some(ind) => self.fmt_pretty(f, ind, styles),
                None => self.fmt_compact(f, styles),
            },
        }
    }

    /// Returns `true` if rendering [`Self::fmt_rest`] with the same `excluded`
    /// paths would emit at least one field, i.e. the filtered "rest" is not
    /// empty. Used by the `if` conditional on the rest field.
    pub fn has_rest_content(&self, excluded: &[&[PathToken<'_>]]) -> bool {
        match self {
            Json::Object(obj) => obj.iter().any(|(key, value)| {
                !value.is_null()
                    && !is_excluded_key(excluded, key)
                    && {
                        let child = child_excluded_key(excluded, key);
                        child.is_empty() || value.has_rest_content(&child)
                    }
            }),
            Json::Array(arr) => arr.iter().enumerate().any(|(i, value)| {
                !value.is_null()
                    && !is_excluded(excluded, |t| token_matches_index(t, i))
                    && {
                        let child = child_excluded(excluded, |t| token_matches_index(t, i));
                        child.is_empty() || value.has_rest_content(&child)
                    }
            }),
            _ => !self.is_null(),
        }
    }
}

/// Renders a single child value while rendering the "rest" view. If the child
/// has no remaining excluded sub-paths it is printed with the normal
/// (allocation-free) formatters; otherwise filtering continues recursively.
fn write_rest_value(
    f: &mut fmt::Formatter<'_>,
    value: &Json,
    child_excluded: &[&[PathToken<'_>]],
    indent: Option<usize>,
    styles: &Option<MarkupStyles>,
) -> fmt::Result {
    let value_indent = indent.map(|i| i + 2);
    if child_excluded.is_empty() {
        match value_indent {
            Some(ind) => value.fmt_pretty(f, ind, styles),
            None => value.fmt_compact(f, styles),
        }
    } else {
        value.fmt_rest(f, child_excluded, value_indent, styles)
    }
}

/// `true` if any excluded path is exactly this field (length 1 and its first
/// token matches), meaning the field should be omitted entirely.
fn is_excluded(excluded: &[&[PathToken<'_>]], matches: impl Fn(&PathToken<'_>) -> bool) -> bool {
    excluded.iter().any(|p| p.len() == 1 && matches(&p[0]))
}

/// Collects the sub-paths of any excluded path that descends through this field
/// (length > 1 with a matching first token), with that first token stripped.
fn child_excluded<'p>(
    excluded: &[&'p [PathToken<'p>]],
    matches: impl Fn(&PathToken<'_>) -> bool,
) -> SmallVec<[&'p [PathToken<'p>]; 4]> {
    excluded
        .iter()
        .filter(|p| p.len() > 1 && matches(&p[0]))
        .map(|p| &p[1..])
        .collect()
}

/// Length of the leading `Name`-token prefix of `path` whose dotted join equals
/// `key`, or `None`. This lets an excluded path like `[fields, log, file]` match
/// the actual flattened key `"log.file"` under `fields` (join of its last two
/// tokens), consuming two tokens — mirroring how a dotted path resolves.
fn key_match_len(path: &[PathToken<'_>], key: &str) -> Option<usize> {
    let mut joined = String::new();
    for (i, tok) in path.iter().enumerate() {
        match tok {
            PathToken::Name(n) => {
                if i > 0 {
                    joined.push('.');
                }
                joined.push_str(n);
            }
            PathToken::Index(_) => return None,
        }
        if joined == key {
            return Some(i + 1);
        }
        if joined.len() >= key.len() {
            return None;
        }
    }
    None
}

/// A key is fully excluded when some excluded path is entirely consumed by it.
fn is_excluded_key(excluded: &[&[PathToken<'_>]], key: &str) -> bool {
    excluded.iter().any(|p| key_match_len(p, key) == Some(p.len()))
}

/// Sub-paths of excluded paths that descend *through* `key` (consuming its
/// leading tokens), for filtering the value under `key`.
fn child_excluded_key<'p>(
    excluded: &[&'p [PathToken<'p>]],
    key: &str,
) -> SmallVec<[&'p [PathToken<'p>]; 4]> {
    excluded
        .iter()
        .filter_map(|p| {
            let l = key_match_len(p, key)?;
            (l < p.len()).then_some(&p[l..])
        })
        .collect()
}

fn token_matches_index(tok: &PathToken<'_>, index: usize) -> bool {
    matches!(tok, PathToken::Index(i) if *i == index)
}

fn write_key(
    f: &mut fmt::Formatter<'_>,
    key: &str,
    styles: &Option<MarkupStyles>,
) -> Result<(), fmt::Error> {
    if let Some(style) = styles {
        write!(f, "{}", format_args!("\"{key}\"").style(style.key))
    } else {
        write!(f, "\"{}\"", key)
    }
}

fn write_value(
    f: &mut fmt::Formatter<'_>,
    value: &str,
    styles: &Option<MarkupStyles>,
) -> Result<(), fmt::Error> {
    if let Some(style) = styles {
        write!(f, "{}", value.style(style.value))
    } else {
        write!(f, "{}", value)
    }
}

fn write_str(
    f: &mut fmt::Formatter<'_>,
    str: &str,
    styles: &Option<MarkupStyles>,
) -> Result<(), fmt::Error> {
    if let Some(style) = styles {
        write!(f, "{}", format_args!("\"{str}\"").style(style.str))
    } else {
        write!(f, "\"{}\"", str)
    }
}

fn write_syntax(
    f: &mut fmt::Formatter<'_>,
    syntax: &str,
    styles: &Option<MarkupStyles>,
) -> Result<(), fmt::Error> {
    if let Some(style) = styles {
        write!(f, "{}", syntax.style(style.syntax))
    } else {
        write!(f, "{}", syntax)
    }
}

fn parse_index(s: &str) -> Option<usize> {
    if s.starts_with('+') || (s.starts_with('0') && s.len() != 1) {
        return None;
    }
    s.parse().ok()
}

pub struct ParseError {
    pub message: &'static str,
    pub value: String,
    pub index: usize,
}

impl std::fmt::Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        // Create a snippet from the input, showing up to 10 characters before
        // and after the error index
        let start = self.index.saturating_sub(15);
        let end = (self.index + 10).min(self.value.len());
        let snippet = &self.value[start..end];

        write!(f, "{} at index {}: '{}'", self.message, self.index, snippet)
    }
}

impl std::fmt::Debug for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        let snippet_length = 20;
        let start = self.index.saturating_sub(snippet_length);
        let end = (self.index + snippet_length).min(self.value.len());
        let snippet = &self.value[start..end];

        let caret_position = self.index.saturating_sub(start) + 1;

        write!(
            f,
            "{} at index {}:\n`{}`\n{:>width$}",
            self.message,
            self.index,
            snippet,
            "^",                        // Caret pointing to the error location
            width = caret_position + 1, // Correct alignment for the caret
        )
    }
}
impl std::error::Error for ParseError {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn basic() {
        let test_cases = vec![
            r#"{"key": "value"}"#,
            r#"{"escaped": "This is a \"test\""}"#,
            r#"{"nested": {"array": [1, "two", null], "emptyObj": {}, "bool": true}}"#,
            r#"["mixed", 123, {"obj": "inside array"}]"#,
            r#"{}"#,
            r#"[]"#,
        ];

        for case in test_cases {
            match parse_json(case) {
                Ok(parsed) => println!("Parsed JSON: {:#?}", parsed),
                Err(e) => println!("Failed to parse JSON: {}", e),
            }
        }

        let arr = parse_json(r#"["mixed", 123, {"obj": "inside array"}]"#).unwrap();
        println!("Array: {:#?}", arr);
        assert_eq!(arr.get_i(2).get("obj").as_str(), Some("inside array"));
    }

    #[test]
    fn invalid() {
        let test_cases = vec![
            (
                r#"{"key": "value"         "#,
                "Missing Closing Brace for an Object",
            ),
            (
                r#"{"key": "value         }"#,
                "Missing Closing Quote for a String",
            ),
            (r#"{"key"     ,     "value"}"#, "Missing Colon in an Object"),
            (
                r#"{"key1": "value1", "key2": "value2"       ,          }"#,
                "Extra Comma in an Object",
            ),
            (r#"{key: "value"}"#, "Unquoted Key"),
            (
                r#"{"array": [1, 2, "missing bracket"        ,    }        "#,
                "Unclosed Array",
            ),
        ];

        for (json_str, description) in test_cases {
            println!("Testing case: {}", description);
            match parse_json(json_str) {
                Ok(_) => println!("No error detected, but expected an error."),
                Err(e) => {
                    println!("Error (Display): {}", e);
                    println!("Error (Debug):\n{:?}", e);
                }
            }
            println!("---------------------------------------\n");
        }
    }
}
