use chumsky::{
    extra,
    input::MappedSpan,
    primitive::{any, choice, just},
    text::{digits, newline},
    IterParser, Parser,
};

use crate::{
    __internal__parser,
    ast::{Expression, Literal, StringLiteralComponent},
    parse::{
        escape_sequence, map_err_missing_delimiter,
        source_file::{command_whitespace_or_comments, ident, template::body as template_body},
        with_span, DelimiterType, Error,
    },
    Span,
};

#[cfg(test)]
mod test;

__internal__parser! {sign_or_not, (), {
    just("+").or(just("-")).or_not().ignored()
}}

__internal__parser! {pub literal(expression: impl Parser<'s, MappedSpan<Span<'s>, &'s str, F>, Expression<'s>, extra::Err<Error<'s>>> + Clone), Literal<'s>, {
    choice((
        sign_or_not().then(digits(10).then(just(".")).then(digits(10))).to_slice().try_map(|str, span| match str.parse() {
            Ok(f) => Ok(Literal::Float(f)),
            Err(err) => Err(Error::InvalidFloatLiteral(err, span))
        }),
        sign_or_not().then(digits(10)).to_slice().try_map(|str, span| match str.parse() {
            Ok(i) => Ok(Literal::Int(i)),
            Err(err) => Err(Error::InvalidIntLiteral(err, span))
        }),
        just("false").to(false).or(just("true").to(true)).map(Literal::Bool),
        map_err_missing_delimiter(just("\""), string_literal().map(Literal::String), just("\""), DelimiterType::StringLiteral),
        with_span(ident().map(Some).or(just(":").to(None))).then(template_body(expression.clone())).map(|(ty, body)| Literal::Template(ty, Box::new(body))),
        map_err_missing_delimiter(
            just("("),
            with_span(expression.clone())
                .padded_by(command_whitespace_or_comments())
                .separated_by(just(","))
                .allow_trailing()
                .collect::<Vec<(Expression<'s>, Span<'s>)>>()
                .then_ignore(command_whitespace_or_comments())
                .map(Literal::Tuple),
            just(")"),
            DelimiterType::TupleLiteral
        ),
        map_err_missing_delimiter(
            just("["),
            with_span(expression.clone())
                .padded_by(command_whitespace_or_comments())
                .separated_by(just(","))
                .allow_trailing()
                .collect::<Vec<(Expression<'s>, Span<'s>)>>()
                .then_ignore(command_whitespace_or_comments())
                .map(Literal::Array),
            just("]"),
            DelimiterType::ArrayLiteral
        ),
        map_err_missing_delimiter(
            just("{"),
            with_span(ident())
                .then_ignore(just(":").padded_by(command_whitespace_or_comments()))
                .then(with_span(expression))
                .separated_by(just(",").padded_by(command_whitespace_or_comments()))
                .allow_trailing()
                .collect()
                .map(Literal::Struct)
                .padded_by(command_whitespace_or_comments()),
            just("}"),
            DelimiterType::StructLiteral
        )
    ))
}}

__internal__parser! {string_literal, Vec<(StringLiteralComponent<'s>, Span<'s>)>, {
    with_span(choice((
        just("\\\"").to(StringLiteralComponent::EscapedQuote),
        escape_sequence().map(StringLiteralComponent::EscapeSequence),
        any()
            .and_is(
                choice((
                    just("\\").ignored(),
                    just("\"").ignored(),
                    newline())
                ).not()
            )
            .repeated()
            .at_least(1)
            .to_slice()
            .map(StringLiteralComponent::Literal)
    ))).repeated().collect()
}}
