use enumset::enum_set;

use crate::lst_parser::{
    error::SyntaxError,
    grammar::expression::expression,
    syntax_kind::{SyntaxKind::*, TokenSet},
    CompletedMarker, Parser,
};

const TOP_LEVEL_ITEM_START: TokenSet = enum_set!(DEF_KW | MOD_KW | USE_KW);

pub fn mod_body(p: &mut Parser) {
    loop {
        if top_level_item(p).is_none() {
            break;
        }
    }
}

fn mod_decl(p: &mut Parser) -> Option<CompletedMarker> {
    let mod_start = p.start("module");
    if !p.eat(MOD_KW) {
        mod_start.abandon(p);
        return None;
    }

    let mod_name = p.start("module_name");
    if p.eat(IDENT) {
        mod_name.complete(p, MODULE_NAME);
    } else {
        mod_name.error(p, SyntaxError::Expected(vec![IDENT]));
    }

    let mod_body_marker = p.start("mod_body");
    if p.eat(SEMICOLON) {
        mod_body_marker.abandon(p);
        Some(mod_start.complete(p, MODULE))
    } else if p.eat(L_BRACE) {
        mod_body(p);
        if !p.eat(R_BRACE) {
            mod_body_marker
                .complete(p, MODULE_BODY)
                .precede(p, "unclosed_mod_body_err")
                .error(p, SyntaxError::UnclosedModuleBody);
        } else {
            mod_body_marker.complete(p, MODULE_BODY);
        }
        Some(mod_start.complete(p, MODULE))
    } else {
        Some(mod_start.error(p, SyntaxError::Expected(vec![MODULE_BODY])))
    }
}

pub fn top_level_item(p: &mut Parser) -> Option<CompletedMarker> {
    if !TOP_LEVEL_ITEM_START.contains(p.current()) {
        return None;
    }
    def(p).or_else(|| mod_decl(p)).or_else(|| r#use(p))
}

fn def(p: &mut Parser) -> Option<CompletedMarker> {
    let def_start = p.start("top_level_def");
    if !p.eat(DEF_KW) {
        def_start.abandon(p);
        return None;
    }

    let def_name = p.start("def_name");
    if p.eat(IDENT) {
        def_name.complete(p, DEF_NAME);
    } else {
        def_name.error(p, SyntaxError::Expected(vec![IDENT]));
    }

    let maybe_expected_eq = p.start("maybe_expect_eq");
    if !p.eat(EQ) {
        maybe_expected_eq.error(p, SyntaxError::Expected(vec![EQ]));
    } else {
        maybe_expected_eq.abandon(p);
    }

    let body = p.start("def_body");
    if expression(p, false).is_some() {
        body.complete(p, DEF_BODY);
    } else {
        body.error(p, SyntaxError::Expected(vec![DEF_BODY]));
    }

    Some(if p.eat(SEMICOLON) {
        def_start.complete(p, DEF)
    } else if TOP_LEVEL_ITEM_START.contains(p.current()) || p.at(EOF) {
        def_start
            .complete(p, DEF)
            .precede(p, "unterminated_tl_item")
            .error(p, SyntaxError::UnterminatedTopLevelItem)
    } else {
        def_start
            .complete(p, DEF)
            .precede(p, "err_unexpected")
            .error(p, SyntaxError::Expected(vec![SEMICOLON]))
    })
}

fn r#use(p: &mut Parser) -> Option<CompletedMarker> {
    let use_start = p.start("use_start");
    if !p.eat(USE_KW) {
        use_start.abandon(p);
        return None;
    }

    if use_pat(p).is_none() {
        p.start("expected_use_pat")
            .error(p, SyntaxError::Expected(vec![USE_PAT]));
    }

    let use_item = use_start.complete(p, USE);
    Some(if p.eat(SEMICOLON) {
        use_item
    } else if TOP_LEVEL_ITEM_START.contains(p.current()) || p.at(EOF) {
        use_item
            .precede(p, "unterminated_tl_item")
            .error(p, SyntaxError::UnterminatedTopLevelItem)
    } else {
        use_item
            .precede(p, "err_unexpected")
            .error(p, SyntaxError::Expected(vec![SEMICOLON]))
    })
}

fn use_pat(p: &mut Parser) -> Option<CompletedMarker> {
    let use_pat_marker = p.start("use_pat");
    if !p.eat(IDENT) {
        return None;
    }

    loop {
        if p.eat(PATH_SEP) {
            if pat_item(p).is_none() {
                break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath));
            }
        } else if p.at(SEMICOLON) && p.nth_at(1, COLON) {
            let broken_sep = p.start("broken_path_sep");
            let wrong_semi = p.start("semi_typo");
            p.eat(SEMICOLON);
            wrong_semi.error(p, SyntaxError::PathSepContainsSemicolon);
            p.eat(COLON);
            broken_sep.complete(p, PATH_SEP);
            if pat_item(p).is_none() {
                break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath));
            }
        } else if p.at(COLON) && p.nth_at(1, SEMICOLON) {
            let broken_sep = p.start("broken_path_sep");
            p.eat(COLON);
            let wrong_semi = p.start("semi_typo");
            p.eat(SEMICOLON);
            wrong_semi.error(p, SyntaxError::PathSepContainsSemicolon);
            broken_sep.complete(p, PATH_SEP);
            if pat_item(p).is_none() {
                break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath));
            }
        } else if p.at(SEMICOLON) && p.nth_at(1, SEMICOLON) {
            let broken_sep = p.start("broken_path_sep");
            p.eat(SEMICOLON);
            p.eat(SEMICOLON);
            broken_sep
                .complete(p, PATH_SEP)
                .precede(p, "semi_typo_err")
                .error(p, SyntaxError::PathSepContainsSemicolon);
            if pat_item(p).is_none() {
                break Some(use_pat_marker.error(p, SyntaxError::UnfinishedPath));
            }
        } else if p.eat(SEMICOLON) {
            break Some(use_pat_marker.complete(p, USE_PAT));
        } else {
            break Some(use_pat_marker.error(p, SyntaxError::Expected(vec![PATH_SEP, SEMICOLON])));
        }
    }
}

fn pat_item(p: &mut Parser) -> Option<CompletedMarker> {
    let item_start = p.start("pat_item_start");
    if p.eat(IDENT) {
        Some(item_start.complete(p, PAT_ITEM))
    } else if p.eat(STAR) {
        Some(item_start.complete(p, PAT_GLOB))
    } else if p.eat(L_BRACE) {
        todo!("write PAT_GROUPs")
    } else {
        None
    }
}
