use diagnostics::LocalSink;
use syntax::ast::Expression;

use crate::store::Store;

pub(crate) fn run(typed_ast: &[Expression], store: &Store, sink: &LocalSink) {
    let Some(prelude_module) = store.get_module("prelude") else {
        return;
    };
    for item in typed_ast {
        check_top_level_function(item, prelude_module, sink);
        visit_expression(item, prelude_module, sink);
    }
}

fn check_top_level_function(
    item: &Expression,
    prelude_module: &syntax::program::Module,
    sink: &LocalSink,
) {
    if let Expression::Function {
        name, name_span, ..
    } = item
    {
        let qualified = format!("prelude.{}", name);
        if prelude_module.definitions.contains_key(qualified.as_str()) {
            sink.push(diagnostics::infer::prelude_function_shadowed(
                name, *name_span,
            ));
        }
    }
}

fn visit_expression(
    expression: &Expression,
    prelude_module: &syntax::program::Module,
    sink: &LocalSink,
) {
    match expression {
        Expression::Enum {
            name, name_span, ..
        }
        | Expression::ValueEnum {
            name, name_span, ..
        }
        | Expression::Struct {
            name, name_span, ..
        }
        | Expression::TypeAlias {
            name, name_span, ..
        }
        | Expression::Interface {
            name, name_span, ..
        } => {
            let qualified = format!("prelude.{}", name);
            if prelude_module.definitions.contains_key(qualified.as_str()) {
                sink.push(diagnostics::infer::prelude_type_shadowed(name, *name_span));
            }
        }
        _ => {}
    }

    for child in expression.children() {
        visit_expression(child, prelude_module, sink);
    }
}
