use crate::{
    ast,
    expr::Span,
    parser::{error::ParseError, token::Token},
};
use chumsky::prelude::*;

pub fn parser() -> impl Parser<Token, ast::UntypedUse, Error = ParseError> {
    let unqualified_import = choice((
        select! {Token::Name { name } => name}.then(
            just(Token::As)
                .ignore_then(select! {Token::Name { name } => name})
                .or_not(),
        ),
        select! {Token::UpName { name } => name}.then(
            just(Token::As)
                .ignore_then(select! {Token::UpName { name } => name})
                .or_not(),
        ),
    ))
    .map_with_span(|(name, as_name), span| ast::UnqualifiedImport {
        name,
        location: span,
        as_name,
    });

    let unqualified_imports = just(Token::Dot)
        .ignore_then(
            unqualified_import
                .separated_by(just(Token::Comma))
                .allow_trailing()
                .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
        )
        .or_not();

    let as_name = just(Token::As)
        .ignore_then(select! {Token::Name { name } => name})
        .or_not();

    let module_path = select! {Token::Name { name } => name}
        .separated_by(just(Token::Slash))
        .then(unqualified_imports)
        .map_with_span(|(module, unqualified), span: Span| (module, (span, unqualified)))
        .then(as_name);

    just(Token::Use).ignore_then(module_path).map_with_span(
        |((module, (unqualified_start, unqualified)), as_name), span| ast::Use {
            module,
            as_name,
            unqualified: (unqualified_start.end, unqualified.unwrap_or_default()),
            package: (),
            location: span,
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::assert_import;

    #[test]
    fn import_basic() {
        assert_import!("use aiken/list");
    }

    #[test]
    fn import_unqualified() {
        assert_import!(
            r#"
            use std/address.{Address as A, thing as w}
            "#
        );
    }

    #[test]
    fn import_alias() {
        assert_import!("use aiken/list as foo");
    }
}
