use std::sync::Arc;

use emmylua_parser::{LuaAstNode, LuaCallExpr, LuaNameExpr};

use crate::{
    DbIndex, LuaDeclId, LuaInferCache, LuaType, semantic::overload_resolve::resolve_signature,
};

pub fn resolve_global_decl_id(
    db: &DbIndex,
    cache: &mut LuaInferCache,
    name: &str,
    name_expr: Option<&LuaNameExpr>,
) -> Option<LuaDeclId> {
    let decl_ids = db.get_global_index().get_global_decl_ids(name)?;
    if decl_ids.len() == 1 {
        return Some(decl_ids[0]);
    }

    if let Some(name_expr) = name_expr
        && let Some(call_expr) = name_expr.get_parent::<LuaCallExpr>()
    {
        return resolve_global_func_decl_id(db, cache, name, call_expr);
    }

    let mut last_valid_decl_id = None;
    for decl_id in decl_ids {
        let decl_type_cache = db.get_type_index().get_type_cache(&(*decl_id).into());
        if let Some(type_cache) = decl_type_cache {
            let typ = type_cache.as_type();
            if typ.is_def() || typ.is_ref() || typ.is_function() {
                return Some(*decl_id);
            }

            if type_cache.is_table() {
                last_valid_decl_id = Some(decl_id)
            }
        }
    }
    if last_valid_decl_id.is_none() && !decl_ids.is_empty() {
        return Some(decl_ids[0]);
    }

    last_valid_decl_id.cloned()
}

fn resolve_global_func_decl_id(
    db: &DbIndex,
    cache: &mut LuaInferCache,
    name: &str,
    call_expr: LuaCallExpr,
) -> Option<LuaDeclId> {
    let decl_ids = db.get_global_index().get_global_decl_ids(name)?;
    let mut overload_signature = vec![];
    for decl_id in decl_ids {
        let decl_type_cache = db.get_type_index().get_type_cache(&(*decl_id).into());
        if let Some(type_cache) = decl_type_cache {
            let typ = type_cache.as_type();
            if typ.is_def() || typ.is_ref() || typ.is_table() {
                return Some(*decl_id);
            }

            if let LuaType::Signature(signature) = typ {
                let signature = db.get_signature_index().get(signature)?;
                overload_signature.push((decl_id.clone(), signature.to_doc_func_type()));
            }
        }
    }

    let signature = resolve_signature(
        db,
        cache,
        overload_signature
            .iter()
            .map(|(_, doc_func)| doc_func.clone())
            .collect(),
        call_expr,
        false,
        None,
    );

    if let Ok(signature) = signature {
        for (decl_id, doc_func) in &overload_signature {
            if Arc::ptr_eq(&signature, doc_func) {
                return Some(decl_id.clone());
            }
        }
    }

    overload_signature.first().map(|(id, _)| id.clone())
}
