#[cfg(test)]
mod test;

use crate::{
    __internal__parser,
    ast::{Command, Expression},
    parse::{ignore_ctx, with_span, DelimiterType, Error},
    Span,
};
use chumsky::{
    extra::{self},
    input::MappedSpan,
    primitive::{choice, empty, just},
    ConfigIterParser, Parser,
};

use super::{command_whitespace_or_comments, ident};

__internal__parser! {pub command_or_comment(expression: impl Parser<'s, MappedSpan<Span<'s>, &'s str, F>, Expression<'s>, extra::Err<Error<'s>>> + Clone), Option<Command<'s>>, extra::Full<Error<'s>, (), (usize, Span<'s>)>, {
        just("{")
            .repeated()
            .configure(|cfg, (brace_count, _)| cfg.exactly(*brace_count))
            .to_span()
            .then(ignore_ctx(
                choice((
                    assignment(expression.clone()).map(|(variable, expression)| Command::Assignment { variable, expression }).map(Some),
                    with_span(expression)
                        .map(|(expression, span)| Command::Expression(expression, span)).map(Some),
                    empty().to(None)
                )).padded_by(command_whitespace_or_comments())
            ))
            .then(
                just("}")
                    .repeated()
                    .configure(|cfg, (brace_count, _)| cfg.exactly(*brace_count))
                    .or_not()
            )
            .try_map_with(|((start_brace_span, content), closing_braces_option), extra| closing_braces_option.map(|()| content).ok_or_else
                (|| {
                    let (curly_count, _): (usize, Span<'s>) = *extra.ctx();
                    Error::UnclosedDelimiter { start: start_brace_span, kind: DelimiterType::Command { curly_count } }
                })
            )
}}

__internal__parser! {assignment(expression: impl Parser<'s, MappedSpan<Span<'s>, &'s str, F>, Expression<'s>, extra::Err<Error<'s>>> + Clone), ((&'s str, Span<'s>), (Expression<'s>, Span<'s>)), {
    with_span(ident())
        .then_ignore(just("=").padded_by(command_whitespace_or_comments().ignored()))
        .then(with_span(expression))
}}
