use nom::{
    branch::alt,
    combinator::{
        iterator,
        map,
        value,
    },
    IResult,
};
use crate::{
    combinators::{
        parse_array_index,
        parse_array_range,
        parse_flatten_operator,
        parse_group_separator,
        parse_key,
        parse_lenses,
        parse_multi_key,
        parse_object_index,
        parse_object_range,
        parse_pipe_in_operator,
        parse_pipe_out_operator,
        parse_truncate_operator,
    },
    errors::JqlParserError,
    tokens::{
        Lens,
        Range,
        Token,
        View,
    },
};
fn parse_fragment(input: &str) -> IResult<&str, Token> {
    alt((
        map(parse_array_index(), Token::ArrayIndexSelector),
        map(parse_array_range(), |(start, end)| {
            Token::ArrayRangeSelector(Range(start, end))
        }),
        map(parse_key(), Token::KeySelector),
        map(parse_multi_key(), Token::MultiKeySelector),
        map(parse_object_index(), Token::ObjectIndexSelector),
        map(parse_object_range(), |(start, end)| {
            Token::ObjectRangeSelector(Range(start, end))
        }),
        map(parse_lenses(), |lenses| {
            Token::LensSelector(
                lenses
                    .into_iter()
                    .map(|(tokens, value)| Lens(tokens, value))
                    .collect(),
            )
        }),
        value(Token::FlattenOperator, parse_flatten_operator()),
        value(Token::GroupSeparator, parse_group_separator()),
        value(Token::PipeInOperator, parse_pipe_in_operator()),
        value(Token::PipeOutOperator, parse_pipe_out_operator()),
        value(Token::TruncateOperator, parse_truncate_operator()),
    ))(input)
}
pub fn parse(input: &str) -> Result<Vec<Token>, JqlParserError> {
    let mut parser_iterator = iterator(input, parse_fragment);
    let tokens = parser_iterator.collect::<Vec<Token>>();
    let result: IResult<_, _> = parser_iterator.finish();
    match result {
        Ok((unparsed, _)) => {
            if !unparsed.is_empty() {
                return Err(JqlParserError::ParsingError {
                    tokens: tokens.stringify(),
                    unparsed: unparsed.to_string(),
                });
            }
            let truncate_count = tokens
                .iter()
                .filter(|&token| *token == Token::TruncateOperator)
                .count();
            if truncate_count > 1
                || (truncate_count == 1 && tokens.last() != Some(&Token::TruncateOperator))
            {
                return Err(JqlParserError::TruncateError(tokens.stringify()));
            }
            Ok(tokens)
        }
        Err(_) => Err(JqlParserError::UnknownError),
    }
}
#[cfg(test)]
mod tests {
    use super::{
        parse,
        parse_fragment,
    };
    use crate::{
        errors::JqlParserError,
        tokens::{
            Index,
            Lens,
            LensValue,
            Range,
            Token,
            View,
        },
    };
    #[test]
    fn check_array_index_selector() {
        assert_eq!(
            parse_fragment("[0,1,2]"),
            Ok((
                "",
                Token::ArrayIndexSelector(vec![Index(0), Index(1), Index(2)])
            ))
        );
        assert_eq!(
            parse_fragment(" [ 0 , 1 , 2 ] "),
            Ok((
                "",
                Token::ArrayIndexSelector(vec![Index(0), Index(1), Index(2)])
            ))
        );
    }
    #[test]
    fn check_array_range_selector() {
        assert_eq!(
            parse_fragment("[0:2]"),
            Ok((
                "",
                Token::ArrayRangeSelector(Range(Some(Index(0)), Some(Index(2))))
            ))
        );
        assert_eq!(
            parse_fragment("[:2]"),
            Ok(("", Token::ArrayRangeSelector(Range(None, Some(Index(2))))))
        );
        assert_eq!(
            parse_fragment("[0:]"),
            Ok(("", Token::ArrayRangeSelector(Range(Some(Index(0)), None))))
        );
        assert_eq!(
            parse_fragment("[:]"),
            Ok(("", Token::ArrayRangeSelector(Range(None, None))))
        );
    }
    #[test]
    fn check_key_selector() {
        assert_eq!(
            parse_fragment(r#""one""#),
            Ok(("", Token::KeySelector("one")))
        );
        assert_eq!(
            parse_fragment(r#" "one" "#),
            Ok(("", Token::KeySelector("one")))
        );
    }
    #[test]
    fn check_multi_key_selector() {
        assert_eq!(
            parse_fragment(r#"{"one","two","three"}"#),
            Ok(("", Token::MultiKeySelector(vec!["one", "two", "three"])))
        );
        assert_eq!(
            parse_fragment(r#" { "one", "two" , "three" } "#),
            Ok(("", Token::MultiKeySelector(vec!["one", "two", "three"])))
        );
    }
    #[test]
    fn check_object_index_selector() {
        assert_eq!(
            parse_fragment("{0,1,2}"),
            Ok((
                "",
                Token::ObjectIndexSelector(vec![Index(0), Index(1), Index(2)])
            ))
        );
        assert_eq!(
            parse_fragment(" { 0 , 1 , 2 } "),
            Ok((
                "",
                Token::ObjectIndexSelector(vec![Index(0), Index(1), Index(2)])
            ))
        );
    }
    #[test]
    fn check_object_range_selector() {
        assert_eq!(
            parse_fragment("{0:2}"),
            Ok((
                "",
                Token::ObjectRangeSelector(Range(Some(Index(0)), Some(Index(2))))
            ))
        );
        assert_eq!(
            parse_fragment("{:2}"),
            Ok(("", Token::ObjectRangeSelector(Range(None, Some(Index(2))))))
        );
        assert_eq!(
            parse_fragment("{0:}"),
            Ok(("", Token::ObjectRangeSelector(Range(Some(Index(0)), None))))
        );
        assert_eq!(
            parse_fragment("{:}"),
            Ok(("", Token::ObjectRangeSelector(Range(None, None))))
        );
    }
    #[test]
    fn check_lens_selector() {
        assert_eq!(
            parse_fragment(r#"|={"abc""c","bcd""d"=123,"efg"=null,"hij"="test"}"#),
            Ok((
                "",
                Token::LensSelector(vec![
                    Lens(
                        vec![Token::KeySelector("abc"), Token::KeySelector("c")],
                        None
                    ),
                    Lens(
                        vec![Token::KeySelector("bcd"), Token::KeySelector("d")],
                        Some(LensValue::Number(123))
                    ),
                    Lens(vec![Token::KeySelector("efg")], Some(LensValue::Null)),
                    Lens(
                        vec![Token::KeySelector("hij")],
                        Some(LensValue::String("test"))
                    ),
                ])
            ))
        );
    }
    #[test]
    fn check_flatten_operator() {
        assert_eq!(parse_fragment(".."), Ok(("", Token::FlattenOperator)));
        assert_eq!(parse_fragment(" .. "), Ok(("", Token::FlattenOperator)));
    }
    #[test]
    fn check_pipe_in_operator() {
        assert_eq!(parse_fragment("|>"), Ok(("", Token::PipeInOperator)));
        assert_eq!(parse_fragment(" |> "), Ok(("", Token::PipeInOperator)));
    }
    #[test]
    fn check_pipe_out_operator() {
        assert_eq!(parse_fragment("<|"), Ok(("", Token::PipeOutOperator)));
        assert_eq!(parse_fragment(" <| "), Ok(("", Token::PipeOutOperator)));
    }
    #[test]
    fn check_truncate_operator() {
        assert_eq!(parse_fragment("!"), Ok(("", Token::TruncateOperator)));
        assert_eq!(parse_fragment(" ! "), Ok(("", Token::TruncateOperator)));
    }
    #[test]
    fn check_group_separator() {
        assert_eq!(parse_fragment(","), Ok(("", Token::GroupSeparator)));
        assert_eq!(parse_fragment(" , "), Ok(("", Token::GroupSeparator)));
    }
    #[test]
    fn check_full_parser() {
        assert_eq!(
            parse(r#""this"[9,0]"#),
            Ok(vec![
                Token::KeySelector("this"),
                Token::ArrayIndexSelector(vec![Index(9), Index(0)])
            ]),
        );
        assert_eq!(
            parse("[9,0]nope"),
            Err(JqlParserError::ParsingError {
                tokens: [Token::ArrayIndexSelector(vec![Index(9), Index(0)])].stringify(),
                unparsed: "nope".to_string(),
            })
        );
        assert_eq!(
            parse(r#""this"[9,0]|>"some"<|"ok"..!"#),
            Ok(vec![
                Token::KeySelector("this"),
                Token::ArrayIndexSelector(vec![Index(9), Index(0)]),
                Token::PipeInOperator,
                Token::KeySelector("some"),
                Token::PipeOutOperator,
                Token::KeySelector("ok"),
                Token::FlattenOperator,
                Token::TruncateOperator
            ]),
        );
        assert_eq!(
            parse(r#""a"!"b""#),
            Err(JqlParserError::TruncateError(
                [
                    Token::KeySelector("a"),
                    Token::TruncateOperator,
                    Token::KeySelector("b")
                ]
                .stringify()
            ))
        );
    }
}