use std::{
    fmt,
    num::{
        NonZeroUsize,
        ParseIntError,
    },
    str::FromStr,
    string::ToString,
};
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Index(pub(crate) usize);
impl Index {
    #[must_use]
    pub fn new(index: usize) -> Index {
        Index(index)
    }
}
impl From<Index> for usize {
    fn from(index: Index) -> usize {
        index.0
    }
}
impl fmt::Display for Index {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Index ({})", self.0)
    }
}
impl FromStr for Index {
    type Err = ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Index(s.parse::<usize>()?))
    }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Range(pub(crate) Option<Index>, pub(crate) Option<Index>);
impl Range {
    #[must_use]
    pub fn new(start: Option<Index>, end: Option<Index>) -> Range {
        Range(start, end)
    }
    #[must_use]
    pub fn to_boundaries(&self, len: NonZeroUsize) -> (usize, usize) {
        let start = self.0.unwrap_or(Index(0));
        let end = self.1.unwrap_or(Index(len.get() - 1));
        (start.0, end.0)
    }
}
impl fmt::Display for Range {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let format_bound = |bound: &Option<Index>| match bound {
            Some(index) => index.to_string(),
            None => String::new(),
        };
        write!(
            f,
            "Range [{}:{}]",
            format_bound(&self.0),
            format_bound(&self.1)
        )
    }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Lens<'a>(pub(crate) Vec<Token<'a>>, pub(crate) Option<LensValue<'a>>);
impl<'a> Lens<'a> {
    #[must_use]
    pub fn new(tokens: &[Token<'a>], value: Option<LensValue<'a>>) -> Lens<'a> {
        Lens(tokens.to_vec(), value)
    }
    #[must_use]
    pub fn get(&self) -> (Vec<Token<'a>>, Option<LensValue<'a>>) {
        (self.0.clone(), self.1.clone())
    }
}
impl<'a> fmt::Display for Lens<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}{}",
            self.0.stringify(),
            match &self.1 {
                Some(lens_value) => {
                    lens_value.to_string()
                }
                None => String::new(),
            }
        )
    }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LensValue<'a> {
    Bool(bool),
    Null,
    Number(usize),
    String(&'a str),
}
impl<'a> fmt::Display for LensValue<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            LensValue::Bool(boolean) => {
                write!(f, "{boolean}")
            }
            LensValue::Null => {
                write!(f, "null")
            }
            LensValue::Number(number) => {
                write!(f, "{number}")
            }
            LensValue::String(string) => write!(f, "{string}"),
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Token<'a> {
    ArrayIndexSelector(Vec<Index>),
    ArrayRangeSelector(Range),
    FlattenOperator,
    GroupSeparator,
    KeySelector(&'a str),
    LensSelector(Vec<Lens<'a>>),
    MultiKeySelector(Vec<&'a str>),
    ObjectIndexSelector(Vec<Index>),
    ObjectRangeSelector(Range),
    PipeInOperator,
    PipeOutOperator,
    TruncateOperator,
}
impl<'a> Token<'a> {
    fn get_name(&self) -> &'a str {
        match self {
            Token::ArrayIndexSelector(_) => "Array Index Selector",
            Token::ArrayRangeSelector(_) => "Array Range Selector",
            Token::FlattenOperator => "Flatten Operator",
            Token::GroupSeparator => "Group Separator",
            Token::KeySelector(_) => "Key Selector",
            Token::LensSelector(_) => "Lens Selector",
            Token::MultiKeySelector(_) => "Multi Key Selector",
            Token::ObjectIndexSelector(_) => "Object Index Selector",
            Token::ObjectRangeSelector(_) => "Object Range Selector",
            Token::PipeInOperator => "Pipe In Operator",
            Token::PipeOutOperator => "Pipe Out Operator",
            Token::TruncateOperator => "Truncate Operator",
        }
    }
}
impl<'a> fmt::Display for Token<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Token::ArrayIndexSelector(indexes) | Token::ObjectIndexSelector(indexes) => {
                let formatted_indexes = indexes
                    .iter()
                    .map(ToString::to_string)
                    .collect::<Vec<String>>()
                    .join(", ");
                write!(f, "{} [{formatted_indexes}]", self.get_name())
            }
            Token::ArrayRangeSelector(range) | Token::ObjectRangeSelector(range) => {
                write!(f, "{} {}", self.get_name(), range)
            }
            Token::KeySelector(key) => {
                write!(f, r#"{} "{key}""#, self.get_name())
            }
            Token::LensSelector(lenses) => {
                let formatted_indexes = lenses
                    .iter()
                    .map(ToString::to_string)
                    .collect::<Vec<String>>()
                    .join(", ");
                write!(f, "{} [{formatted_indexes}]", self.get_name())
            }
            Token::MultiKeySelector(multi_key) => {
                let formatted_keys = multi_key.join(", ");
                write!(f, "{} {formatted_keys}", self.get_name())
            }
            Token::FlattenOperator
            | Token::GroupSeparator
            | Token::PipeInOperator
            | Token::PipeOutOperator
            | Token::TruncateOperator => {
                write!(f, "{}", self.get_name())
            }
        }
    }
}
pub trait View {
    fn stringify(&self) -> String;
}
impl<'a, T: AsRef<[Token<'a>]>> View for T {
    fn stringify(&self) -> String {
        self.as_ref()
            .iter()
            .map(ToString::to_string)
            .collect::<Vec<String>>()
            .join(", ")
    }
}