mod generic_tpl_pattern;
mod lambda_tpl_pattern;

use std::{ops::Deref, sync::Arc};

use emmylua_parser::LuaAstNode;
use itertools::Itertools;
use rowan::NodeOrToken;
use smol_str::SmolStr;

use crate::{
    InferFailReason, LuaFunctionType, LuaMemberInfo, LuaMemberKey, LuaMemberOwner, LuaObjectType,
    LuaSemanticDeclId, LuaTupleType, LuaTypeDeclId, LuaTypeNode, LuaUnionType, SemanticDeclLevel,
    VariadicType, check_type_compact,
    db_index::{DbIndex, LuaGenericType, LuaType},
    infer_node_semantic_decl,
    semantic::{
        generic::{
            tpl_context::TplContext,
            tpl_pattern::generic_tpl_pattern::generic_tpl_pattern_match,
            type_substitutor::{GenericCandidate, LiteralPolicy, SubstitutorValue},
            widening::widen_literal_type,
        },
        member::{find_index_operations, get_member_map},
    },
};

use super::type_substitutor::TypeSubstitutor;
use std::collections::HashMap;

type TplPatternMatchResult = Result<(), InferFailReason>;

pub fn tpl_pattern_match_args(
    context: &mut TplContext,
    func_param_types: &[LuaType],
    call_arg_types: &[LuaType],
) -> TplPatternMatchResult {
    tpl_pattern_match_args_inner(context, func_param_types, call_arg_types, false)
}

pub fn tpl_pattern_match_args_skip_unknown(
    context: &mut TplContext,
    func_param_types: &[LuaType],
    call_arg_types: &[LuaType],
) -> TplPatternMatchResult {
    tpl_pattern_match_args_inner(context, func_param_types, call_arg_types, true)
}

fn tpl_pattern_match_args_inner(
    context: &mut TplContext,
    func_param_types: &[LuaType],
    call_arg_types: &[LuaType],
    skip_unknown_tpl: bool,
) -> TplPatternMatchResult {
    for i in 0..func_param_types.len() {
        if i >= call_arg_types.len() {
            break;
        }

        let func_param_type = &func_param_types[i];
        let call_arg_type = &call_arg_types[i];

        match (func_param_type, call_arg_type) {
            (LuaType::Variadic(variadic), _) => {
                variadic_tpl_pattern_match(context, variadic, &call_arg_types[i..])?;
                break;
            }
            (_, LuaType::Variadic(variadic)) => {
                multi_param_tpl_pattern_match_multi_return(
                    context,
                    &func_param_types[i..],
                    variadic,
                )?;
                break;
            }
            _ if skip_unknown_tpl
                && func_param_type.contain_tpl()
                && (call_arg_type.is_any() || call_arg_type.is_unknown()) => {}
            _ => {
                tpl_pattern_match(context, func_param_type, call_arg_type)?;
            }
        }
    }

    Ok(())
}

pub fn multi_param_tpl_pattern_match_multi_return(
    context: &mut TplContext,
    func_param_types: &[LuaType],
    multi_return: &VariadicType,
) -> TplPatternMatchResult {
    match &multi_return {
        VariadicType::Base(base) => {
            let mut call_arg_types = Vec::new();
            for param in func_param_types {
                if param.is_variadic() {
                    call_arg_types.push(LuaType::Variadic(multi_return.clone().into()));
                    break;
                } else {
                    call_arg_types.push(base.clone());
                }
            }

            tpl_pattern_match_args(context, func_param_types, &call_arg_types)?;
        }
        VariadicType::Multi(_) => {
            let mut call_arg_types = Vec::new();
            for (i, param) in func_param_types.iter().enumerate() {
                let Some(return_type) = multi_return.get_type(i) else {
                    break;
                };

                if param.is_variadic() {
                    call_arg_types.push(LuaType::Variadic(
                        multi_return.get_new_variadic_from(i).into(),
                    ));
                    break;
                } else {
                    call_arg_types.push(return_type.clone());
                }
            }

            tpl_pattern_match_args(context, func_param_types, &call_arg_types)?;
        }
    }

    Ok(())
}

pub fn tpl_pattern_match(
    context: &mut TplContext,
    pattern: &LuaType,
    target: &LuaType,
) -> TplPatternMatchResult {
    let target = escape_alias(context.db, target);
    if !pattern.contains_tpl_node() {
        return Ok(());
    }

    match pattern {
        LuaType::TplRef(tpl) => {
            let policy = if tpl.is_const() {
                LiteralPolicy::Preserve
            } else {
                LiteralPolicy::FreshWidening
            };
            context.substitutor.infer_value(
                tpl.get_tpl_id(),
                SubstitutorValue::Type(GenericCandidate::new(target.clone(), policy)),
            );
        }
        LuaType::StrTplRef(str_tpl) => {
            if let LuaType::StringConst(s) = target {
                let prefix = str_tpl.get_prefix();
                let suffix = str_tpl.get_suffix();
                let type_name = SmolStr::new(format!("{}{}{}", prefix, s, suffix));
                let file_id = context.cache.get_file_id();
                let inferred_type = match type_name.as_str() {
                    "unknown" => LuaType::Unknown,
                    "never" => LuaType::Never,
                    "nil" | "void" => LuaType::Nil,
                    "any" => LuaType::Any,
                    "userdata" => LuaType::Userdata,
                    "thread" => LuaType::Thread,
                    "boolean" | "bool" => LuaType::Boolean,
                    "string" => LuaType::String,
                    "integer" | "int" => LuaType::Integer,
                    "number" => LuaType::Number,
                    "io" => LuaType::Io,
                    "self" => LuaType::SelfInfer,
                    "global" => LuaType::Global,
                    "function" => LuaType::Function,
                    _ => context
                        .db
                        .get_type_index()
                        .find_type_decl(
                            file_id,
                            &type_name,
                            context.db.resolve_workspace_id(file_id),
                        )
                        .map(|decl| LuaType::Ref(decl.get_id()))
                        .unwrap_or(LuaType::Ref(LuaTypeDeclId::global(&type_name))),
                };

                context.substitutor.infer_value(
                    str_tpl.get_tpl_id(),
                    SubstitutorValue::Type(GenericCandidate::new(
                        inferred_type,
                        LiteralPolicy::FreshWidening,
                    )),
                );
            }
        }
        LuaType::Array(array_type) => {
            array_tpl_pattern_match(context, array_type.get_base(), &target)?;
        }
        LuaType::TableGeneric(table_generic_params) => {
            table_generic_tpl_pattern_match(context, table_generic_params, &target)?;
        }
        LuaType::Generic(generic) => {
            generic_tpl_pattern_match(context, generic, &target)?;
        }
        LuaType::Union(union) => {
            union_tpl_pattern_match(context, union, &target)?;
        }
        LuaType::DocFunction(doc_func) => {
            func_tpl_pattern_match(context, doc_func, &target)?;
        }
        LuaType::Tuple(tuple) => {
            tuple_tpl_pattern_match(context, tuple, &target)?;
        }
        LuaType::Object(obj) => {
            object_tpl_pattern_match(context, obj, &target)?;
        }
        _ => {}
    }

    Ok(())
}

fn maybe_decay_type(typ: &LuaType, decay: bool) -> LuaType {
    if decay {
        widen_literal_type(typ.clone())
    } else {
        typ.clone()
    }
}

fn object_tpl_pattern_match(
    context: &mut TplContext,
    origin_obj: &LuaObjectType,
    target: &LuaType,
) -> TplPatternMatchResult {
    match target {
        LuaType::Object(target_object) => {
            // 先匹配 fields
            for (k, v) in origin_obj.get_fields().iter().sorted_by_key(|(k, _)| *k) {
                let target_value = target_object.get_fields().get(k);
                if let Some(target_value) = target_value {
                    tpl_pattern_match(context, v, target_value)?;
                }
            }
            // 再匹配索引访问
            let target_index_access = target_object.get_index_access();
            for (origin_key, v) in origin_obj.get_index_access() {
                // 先匹配 key 类型进行转换
                let target_access = target_index_access.iter().find(|(target_key, _)| {
                    check_type_compact(context.db, origin_key, target_key).is_ok()
                });
                if let Some(target_access) = target_access {
                    tpl_pattern_match(context, origin_key, &target_access.0)?;
                    tpl_pattern_match(context, v, &target_access.1)?;
                }
            }
        }
        LuaType::TableConst(inst) => {
            let owner = LuaMemberOwner::Element(inst.clone());
            object_tpl_pattern_match_member_owner_match(context, origin_obj, owner)?;
        }
        _ => {}
    }

    Ok(())
}

fn object_tpl_pattern_match_member_owner_match(
    context: &mut TplContext,
    object: &LuaObjectType,
    owner: LuaMemberOwner,
) -> TplPatternMatchResult {
    let owner_type = match &owner {
        LuaMemberOwner::Element(inst) => LuaType::TableConst(inst.clone()),
        LuaMemberOwner::Type(type_id) => LuaType::Ref(type_id.clone()),
        _ => {
            return Err(InferFailReason::None);
        }
    };

    let members = get_member_map(context.db, &owner_type).ok_or(InferFailReason::None)?;
    for (k, v) in members {
        let resolve_key = match &k {
            LuaMemberKey::Integer(i) => Some(LuaType::IntegerConst(*i)),
            LuaMemberKey::Name(s) => Some(LuaType::StringConst(s.clone().into())),
            _ => None,
        };
        let resolve_type = match v.len() {
            0 => LuaType::Any,
            1 => v[0].typ.clone(),
            _ => {
                let mut types = Vec::new();
                for m in &v {
                    types.push(m.typ.clone());
                }
                LuaType::from_vec(types)
            }
        };

        // this is a workaround, I need refactor infer member map
        if resolve_type.is_unknown()
            && !v.is_empty()
            && let Some(LuaSemanticDeclId::Member(member_id)) = &v[0].property_owner_id
        {
            return Err(InferFailReason::UnResolveMemberType(*member_id));
        }

        if let Some(_) = resolve_key
            && let Some(field_value) = object.get_field(&k)
        {
            tpl_pattern_match(context, field_value, &resolve_type)?;
        }
    }

    Ok(())
}

fn array_tpl_pattern_match(
    context: &mut TplContext,
    base: &LuaType,
    target: &LuaType,
) -> TplPatternMatchResult {
    match target {
        LuaType::Array(target_array_type) => {
            tpl_pattern_match(context, base, target_array_type.get_base())?;
        }
        LuaType::Tuple(target_tuple) => {
            let target_base = target_tuple.collapse_to_union(context.db);
            tpl_pattern_match(context, base, &target_base)?;
        }
        LuaType::Object(target_object) => {
            let target_base = target_object
                .cast_down_array_base(context.db)
                .ok_or(InferFailReason::None)?;
            tpl_pattern_match(context, base, &target_base)?;
        }
        _ => {}
    }

    Ok(())
}

fn table_generic_tpl_pattern_match(
    context: &mut TplContext,
    table_generic_params: &[LuaType],
    target: &LuaType,
) -> TplPatternMatchResult {
    if table_generic_params.len() != 2 {
        return Err(InferFailReason::None);
    }

    match target {
        LuaType::TableGeneric(target_table_generic_params) => {
            let min_len = table_generic_params
                .len()
                .min(target_table_generic_params.len());
            for i in 0..min_len {
                tpl_pattern_match(
                    context,
                    &table_generic_params[i],
                    &target_table_generic_params[i],
                )?;
            }
        }
        LuaType::Array(target_array_base) => {
            tpl_pattern_match(context, &table_generic_params[0], &LuaType::Integer)?;
            tpl_pattern_match(
                context,
                &table_generic_params[1],
                target_array_base.get_base(),
            )?;
        }
        LuaType::Tuple(target_tuple) => {
            let len = target_tuple.get_types().len();
            let mut keys = Vec::new();
            for i in 0..len {
                keys.push(LuaType::IntegerConst((i as i64) + 1));
            }

            let key_type = LuaType::Union(LuaUnionType::from_vec(keys).into());
            let target_base = target_tuple.collapse_to_union(context.db);
            tpl_pattern_match(context, &table_generic_params[0], &key_type)?;
            tpl_pattern_match(context, &table_generic_params[1], &target_base)?;
        }
        LuaType::TableConst(inst) => {
            let owner = LuaMemberOwner::Element(inst.clone());
            table_generic_tpl_pattern_member_owner_match(
                context,
                table_generic_params,
                owner,
                &[],
            )?;
        }
        LuaType::Ref(type_id) => {
            let owner = LuaMemberOwner::Type(type_id.clone());
            table_generic_tpl_pattern_member_owner_match(
                context,
                table_generic_params,
                owner,
                &[],
            )?;
        }
        LuaType::Def(type_id) => {
            let owner = LuaMemberOwner::Type(type_id.clone());
            table_generic_tpl_pattern_member_owner_match(
                context,
                table_generic_params,
                owner,
                &[],
            )?;
        }
        LuaType::Generic(generic) => {
            let owner = LuaMemberOwner::Type(generic.get_base_type_id());
            let target_params = generic.get_params();
            table_generic_tpl_pattern_member_owner_match(
                context,
                table_generic_params,
                owner,
                target_params,
            )?;
        }
        LuaType::Object(obj) => {
            let mut keys = Vec::new();
            let mut values = Vec::new();
            for (k, v) in obj.get_fields() {
                match k {
                    LuaMemberKey::Integer(i) => {
                        keys.push(LuaType::IntegerConst(*i));
                    }
                    LuaMemberKey::Name(s) => {
                        keys.push(LuaType::StringConst(s.clone().into()));
                    }
                    _ => {}
                };
                values.push(v.clone());
            }
            for (k, v) in obj.get_index_access() {
                keys.push(k.clone());
                values.push(v.clone());
            }

            let key_type = LuaType::Union(LuaUnionType::from_vec(keys).into());
            let value_type = LuaType::Union(LuaUnionType::from_vec(values).into());
            tpl_pattern_match(context, &table_generic_params[0], &key_type)?;
            tpl_pattern_match(context, &table_generic_params[1], &value_type)?;
        }

        LuaType::Global | LuaType::Any | LuaType::Table | LuaType::Userdata => {
            // too many
            tpl_pattern_match(context, &table_generic_params[0], &LuaType::Any)?;
            tpl_pattern_match(context, &table_generic_params[1], &LuaType::Any)?;
        }
        _ => {}
    }

    Ok(())
}

// KV 表匹配 ref/def/tableconst
fn table_generic_tpl_pattern_member_owner_match(
    context: &mut TplContext,
    table_generic_params: &[LuaType],
    owner: LuaMemberOwner,
    target_params: &[LuaType],
) -> TplPatternMatchResult {
    if table_generic_params.len() != 2 {
        return Err(InferFailReason::None);
    }

    let owner_type = match &owner {
        LuaMemberOwner::Element(inst) => LuaType::TableConst(inst.clone()),
        LuaMemberOwner::Type(type_id) => match target_params.len() {
            0 => LuaType::Ref(type_id.clone()),
            _ => LuaType::Generic(Arc::new(LuaGenericType::new(
                type_id.clone(),
                target_params.to_vec(),
            ))),
        },
        _ => {
            return Err(InferFailReason::None);
        }
    };

    let members = get_member_map(context.db, &owner_type).ok_or(InferFailReason::None)?;
    // 如果是 pairs 调用, 我们需要尝试寻找元方法, 但目前`__pairs` 被放进成员表中
    if is_pairs_call(context).unwrap_or(false)
        && try_handle_pairs_metamethod(context, table_generic_params, &members).is_ok()
    {
        return Ok(());
    }

    let target_key_type = table_generic_params[0].clone();
    let mut keys = Vec::new();
    let mut values = Vec::new();
    for (k, v) in members {
        let key_type = match k {
            LuaMemberKey::Integer(i) => LuaType::IntegerConst(i),
            LuaMemberKey::Name(s) => LuaType::StringConst(s.clone().into()),
            LuaMemberKey::TypeKey(typ) => typ,
            _ => continue,
        };

        if !target_key_type.is_generic()
            && check_type_compact(context.db, &target_key_type, &key_type).is_err()
        {
            continue;
        }

        keys.push(key_type);

        let resolve_type = match v.len() {
            0 => LuaType::Any,
            1 => v[0].typ.clone(),
            _ => {
                let mut types = Vec::new();
                for m in v {
                    types.push(m.typ.clone());
                }
                LuaType::from_vec(types)
            }
        };

        values.push(resolve_type);
    }

    if keys.is_empty() {
        find_index_operations(context.db, &owner_type)
            .ok_or(InferFailReason::None)?
            .iter()
            .for_each(|m| {
                if target_key_type.is_generic() {
                    return;
                }
                let key_type = match &m.key {
                    LuaMemberKey::TypeKey(typ) => typ.clone(),
                    _ => return,
                };
                if check_type_compact(context.db, &target_key_type, &key_type).is_ok() {
                    keys.push(key_type);
                    values.push(m.typ.clone());
                }
            });
    }

    let key_type = match &keys[..] {
        [] => return Err(InferFailReason::None),
        [first] => first.clone(),
        _ => LuaType::Union(LuaUnionType::from_vec(keys).into()),
    };
    let value_type = match &values[..] {
        [first] => first.clone(),
        _ => LuaType::Union(LuaUnionType::from_vec(values).into()),
    };

    tpl_pattern_match(context, &table_generic_params[0], &key_type)?;
    tpl_pattern_match(context, &table_generic_params[1], &value_type)?;

    Ok(())
}

fn union_tpl_pattern_match(
    context: &mut TplContext,
    union: &LuaUnionType,
    target: &LuaType,
) -> TplPatternMatchResult {
    let mut error_count = 0;
    let mut last_error = InferFailReason::None;
    for u in union.into_vec() {
        match tpl_pattern_match(context, &u, target) {
            // 返回 ok 时并不一定匹配成功, 仅表示没有发生错误
            Ok(_) => {}
            Err(e) => {
                error_count += 1;
                last_error = e;
            }
        }
    }

    if error_count == union.into_vec().len() {
        Err(last_error)
    } else {
        Ok(())
    }
}

fn func_tpl_pattern_match(
    context: &mut TplContext,
    tpl_func: &LuaFunctionType,
    target: &LuaType,
) -> TplPatternMatchResult {
    match target {
        LuaType::DocFunction(target_doc_func) => {
            func_tpl_pattern_match_doc_func(context, tpl_func, target_doc_func)?;
        }
        LuaType::Signature(signature_id) => {
            let signature = context
                .db
                .get_signature_index()
                .get(signature_id)
                .ok_or(InferFailReason::None)?;
            if !signature.is_resolve_return() {
                return lambda_tpl_pattern::check_lambda_tpl_pattern(
                    context,
                    tpl_func,
                    *signature_id,
                );
            }
            let fake_doc_func = signature.to_doc_func_type();
            func_tpl_pattern_match_doc_func(context, tpl_func, &fake_doc_func)?;
        }
        _ => {}
    }

    Ok(())
}

fn func_tpl_pattern_match_doc_func(
    context: &mut TplContext,
    tpl_func: &LuaFunctionType,
    target_func: &LuaFunctionType,
) -> TplPatternMatchResult {
    let mut tpl_func_params = tpl_func.get_params().to_vec();
    if tpl_func.is_colon_define() {
        tpl_func_params.insert(0, ("self".to_string(), Some(LuaType::Any)));
    }

    let mut target_func_params = target_func.get_params().to_vec();

    if target_func.is_colon_define() {
        target_func_params.insert(0, ("self".to_string(), Some(LuaType::Any)));
    }

    param_type_list_pattern_match_type_list(context, &tpl_func_params, &target_func_params)?;

    let tpl_return = tpl_func.get_ret();
    let target_return = target_func.get_ret();
    return_type_pattern_match_target_type(context, tpl_return, target_return)?;

    Ok(())
}

fn param_type_list_pattern_match_type_list(
    context: &mut TplContext,
    sources: &[(String, Option<LuaType>)],
    targets: &[(String, Option<LuaType>)],
) -> TplPatternMatchResult {
    let type_len = sources.len();
    let mut target_offset = 0;
    for i in 0..type_len {
        let source = match sources.get(i) {
            Some(t) => t.1.clone().unwrap_or(LuaType::Any),
            None => break,
        };

        match &source {
            LuaType::Variadic(inner) => {
                let i = i + target_offset;
                if i >= targets.len() {
                    variadic_tpl_pattern_match(context, inner, &[])?;
                    break;
                }

                if let VariadicType::Base(LuaType::TplRef(generic_tpl)) = inner.deref() {
                    let tpl_id = generic_tpl.get_tpl_id();
                    if let Some(inferred_type_value) = context.substitutor.get(tpl_id) {
                        match inferred_type_value {
                            SubstitutorValue::Type(_) => {
                                continue;
                            }
                            SubstitutorValue::MultiTypes(types) => {
                                if types.len() > 1 {
                                    target_offset += types.len() - 1;
                                }
                                continue;
                            }
                            SubstitutorValue::Params(params) => {
                                if params.len() > 1 {
                                    target_offset += params.len() - 1;
                                }
                                continue;
                            }
                            _ => {}
                        }
                    }
                }

                let mut target_rest_params = &targets[i..];
                // If the variadic parameter is not the last one, then target_rest_params should exclude the parameters that come after it.
                if i + 1 < type_len {
                    let source_rest_len = type_len - i - 1;
                    if source_rest_len >= target_rest_params.len() {
                        continue;
                    }
                    let target_rest_len = target_rest_params.len() - source_rest_len;
                    target_rest_params = &target_rest_params[..target_rest_len];
                    if target_rest_len > 1 {
                        target_offset += target_rest_len - 1;
                    }
                }

                func_varargs_tpl_pattern_match(inner, target_rest_params, context.substitutor)?;
            }
            _ => {
                let target = match targets.get(i + target_offset) {
                    Some(t) => t.1.clone().unwrap_or(LuaType::Any),
                    None => break,
                };
                tpl_pattern_match(context, &source, &target)?;
            }
        }
    }

    Ok(())
}

pub(crate) fn return_type_pattern_match_target_type(
    context: &mut TplContext,
    source: &LuaType,
    target: &LuaType,
) -> TplPatternMatchResult {
    match (source, target) {
        // toooooo complex
        (LuaType::Variadic(variadic_source), LuaType::Variadic(variadic_target)) => {
            match variadic_target.deref() {
                VariadicType::Base(target_base) => match variadic_source.deref() {
                    VariadicType::Base(source_base) => {
                        if let LuaType::TplRef(type_ref) = source_base {
                            let tpl_id = type_ref.get_tpl_id();
                            let policy = if type_ref.is_const() {
                                LiteralPolicy::Preserve
                            } else {
                                LiteralPolicy::FreshWidening
                            };
                            context.substitutor.infer_value(
                                tpl_id,
                                SubstitutorValue::Type(GenericCandidate::new(
                                    target_base.clone(),
                                    policy,
                                )),
                            );
                        }
                    }
                    VariadicType::Multi(source_multi) => {
                        for ret_type in source_multi {
                            match ret_type {
                                LuaType::Variadic(inner) => {
                                    if let VariadicType::Base(base) = inner.deref()
                                        && let LuaType::TplRef(type_ref) = base
                                    {
                                        let tpl_id = type_ref.get_tpl_id();
                                        let policy = if type_ref.is_const() {
                                            LiteralPolicy::Preserve
                                        } else {
                                            LiteralPolicy::FreshWidening
                                        };
                                        context.substitutor.infer_value(
                                            tpl_id,
                                            SubstitutorValue::Type(GenericCandidate::new(
                                                target_base.clone(),
                                                policy,
                                            )),
                                        );
                                    }

                                    break;
                                }
                                LuaType::TplRef(tpl_ref) => {
                                    let tpl_id = tpl_ref.get_tpl_id();
                                    let policy = if tpl_ref.is_const() {
                                        LiteralPolicy::Preserve
                                    } else {
                                        LiteralPolicy::FreshWidening
                                    };
                                    context.substitutor.infer_value(
                                        tpl_id,
                                        SubstitutorValue::Type(GenericCandidate::new(
                                            target_base.clone(),
                                            policy,
                                        )),
                                    );
                                }
                                _ => {}
                            }
                        }
                    }
                },
                VariadicType::Multi(target_types) => {
                    variadic_tpl_pattern_match(context, variadic_source, target_types)?;
                }
            }
        }
        (LuaType::Variadic(variadic), _) => {
            variadic_tpl_pattern_match(context, variadic, std::slice::from_ref(target))?;
        }
        (_, LuaType::Variadic(variadic)) => {
            multi_param_tpl_pattern_match_multi_return(
                context,
                std::slice::from_ref(source),
                variadic,
            )?;
        }
        _ => {
            tpl_pattern_match(context, source, target)?;
        }
    }

    Ok(())
}

fn func_varargs_tpl_pattern_match(
    variadic: &VariadicType,
    target_rest_params: &[(String, Option<LuaType>)],
    substitutor: &mut TypeSubstitutor,
) -> TplPatternMatchResult {
    match variadic {
        VariadicType::Base(base) => {
            if let LuaType::TplRef(tpl_ref) = base {
                let tpl_id = tpl_ref.get_tpl_id();
                substitutor.infer_value(
                    tpl_id,
                    SubstitutorValue::Params(
                        target_rest_params
                            .iter()
                            .map(|(n, t)| (n.clone(), t.clone()))
                            .collect(),
                    ),
                );
            }
        }
        VariadicType::Multi(_) => {}
    }

    Ok(())
}

pub fn variadic_tpl_pattern_match(
    context: &mut TplContext,
    tpl: &VariadicType,
    target_rest_types: &[LuaType],
) -> TplPatternMatchResult {
    match tpl {
        VariadicType::Base(base) => {
            if let LuaType::TplRef(tpl_ref) = base {
                let tpl_id = tpl_ref.get_tpl_id();
                let decay = !tpl_ref.is_const();
                match target_rest_types.len() {
                    0 => {
                        // Zero varargs are an empty sequence, not one nil return slot.
                        context
                            .substitutor
                            .infer_value(tpl_id, SubstitutorValue::MultiTypes(Vec::new()));
                    }
                    1 => {
                        // If the single argument is itself a multi-return (e.g. a function call
                        // returning multiple values), expand it so that `T...` receives all the
                        // return values rather than a single Variadic wrapper.
                        match &target_rest_types[0] {
                            LuaType::Variadic(variadic) => match variadic.deref() {
                                VariadicType::Multi(types) => match types.len() {
                                    0 => {
                                        context.substitutor.infer_value(
                                            tpl_id,
                                            SubstitutorValue::MultiTypes(Vec::new()),
                                        );
                                    }
                                    1 => {
                                        let policy = if tpl_ref.is_const() {
                                            LiteralPolicy::Preserve
                                        } else {
                                            LiteralPolicy::FreshWidening
                                        };
                                        context.substitutor.infer_value(
                                            tpl_id,
                                            SubstitutorValue::Type(GenericCandidate::new(
                                                types[0].clone(),
                                                policy,
                                            )),
                                        );
                                    }
                                    _ => {
                                        context.substitutor.infer_value(
                                            tpl_id,
                                            SubstitutorValue::MultiTypes(
                                                types
                                                    .iter()
                                                    .map(|t| maybe_decay_type(t, decay))
                                                    .collect(),
                                            ),
                                        );
                                    }
                                },
                                VariadicType::Base(base) => {
                                    context.substitutor.infer_value(
                                        tpl_id,
                                        SubstitutorValue::MultiBase(base.clone()),
                                    );
                                }
                            },
                            arg => {
                                let policy = if tpl_ref.is_const() {
                                    LiteralPolicy::Preserve
                                } else {
                                    LiteralPolicy::FreshWidening
                                };
                                context.substitutor.infer_value(
                                    tpl_id,
                                    SubstitutorValue::Type(GenericCandidate::new(
                                        arg.clone(),
                                        policy,
                                    )),
                                );
                            }
                        }
                    }
                    _ => {
                        context.substitutor.infer_value(
                            tpl_id,
                            SubstitutorValue::MultiTypes(
                                target_rest_types
                                    .iter()
                                    .map(|t| maybe_decay_type(t, decay))
                                    .collect(),
                            ),
                        );
                    }
                }
            }
        }
        VariadicType::Multi(multi) => {
            for (i, ret_type) in multi.iter().enumerate() {
                match ret_type {
                    LuaType::Variadic(inner) => {
                        if i < target_rest_types.len() {
                            variadic_tpl_pattern_match(context, inner, &target_rest_types[i..])?;
                        }

                        break;
                    }
                    LuaType::TplRef(tpl_ref) => {
                        let tpl_id = tpl_ref.get_tpl_id();
                        match target_rest_types.get(i) {
                            Some(t) => {
                                let policy = if tpl_ref.is_const() {
                                    LiteralPolicy::Preserve
                                } else {
                                    LiteralPolicy::FreshWidening
                                };
                                context.substitutor.infer_value(
                                    tpl_id,
                                    SubstitutorValue::Type(GenericCandidate::new(
                                        t.clone(),
                                        policy,
                                    )),
                                );
                            }
                            None => {
                                break;
                            }
                        };
                    }
                    _ => {}
                }
            }
        }
    }

    Ok(())
}

fn tuple_tpl_pattern_match(
    context: &mut TplContext,
    tpl_tuple: &LuaTupleType,
    target: &LuaType,
) -> TplPatternMatchResult {
    match target {
        LuaType::Tuple(target_tuple) => {
            let tpl_tuple_types = tpl_tuple.get_types();
            let target_tuple_types = target_tuple.get_types();
            let tpl_tuple_len = tpl_tuple_types.len();
            for i in 0..tpl_tuple_len {
                let tpl_type = &tpl_tuple_types[i];

                if let LuaType::Variadic(inner) = tpl_type {
                    let target_rest_types = &target_tuple_types[i..];
                    variadic_tpl_pattern_match(context, inner, target_rest_types)?;
                    break;
                }

                let target_type = match target_tuple_types.get(i) {
                    Some(t) => t,
                    None => break,
                };

                tpl_pattern_match(context, tpl_type, target_type)?;
            }
        }
        LuaType::Array(target_array_base) => {
            let tupl_tuple_types = tpl_tuple.get_types();
            let last_type = tupl_tuple_types.last().ok_or(InferFailReason::None)?;
            if let LuaType::Variadic(inner) = last_type {
                match inner.deref() {
                    VariadicType::Base(base) => {
                        if let LuaType::TplRef(tpl_ref) = base {
                            let tpl_id = tpl_ref.get_tpl_id();
                            context.substitutor.infer_value(
                                tpl_id,
                                SubstitutorValue::MultiBase(target_array_base.get_base().clone()),
                            );
                        }
                    }
                    VariadicType::Multi(_) => {}
                }
            }
        }
        _ => {}
    }

    Ok(())
}

fn escape_alias(db: &DbIndex, may_alias: &LuaType) -> LuaType {
    if let LuaType::Ref(type_id) = may_alias
        && let Some(type_decl) = db.get_type_index().get_type_decl(type_id)
        && type_decl.is_alias()
        && let Some(origin_type) = type_decl.get_alias_origin(db, None)
    {
        return origin_type.clone();
    }

    may_alias.clone()
}

fn is_pairs_call(context: &mut TplContext) -> Option<bool> {
    let call_expr = context.call_expr.as_ref()?;
    let prefix_expr = call_expr.get_prefix_expr()?;
    let semantic_decl = match prefix_expr.syntax().clone().into() {
        NodeOrToken::Node(node) => infer_node_semantic_decl(
            context.db,
            context.cache,
            node,
            SemanticDeclLevel::default(),
        ),
        _ => None,
    }?;

    let LuaSemanticDeclId::LuaDecl(decl_id) = semantic_decl else {
        return None;
    };
    let decl = context.db.get_decl_index().get_decl(&decl_id)?;
    if !context.db.get_module_index().is_std(&decl.get_file_id()) {
        return None;
    }
    let name = decl.get_name();
    if name != "pairs" {
        return None;
    }
    Some(true)
}

fn try_handle_pairs_metamethod(
    context: &mut TplContext,
    table_generic_params: &[LuaType],
    members: &HashMap<LuaMemberKey, Vec<LuaMemberInfo>>,
) -> TplPatternMatchResult {
    let pairs_member = members
        .get(&LuaMemberKey::Name("__pairs".into()))
        .ok_or(InferFailReason::None)?
        .first()
        .ok_or(InferFailReason::None)?;
    // 获取迭代函数返回类型
    let meta_return = match &pairs_member.typ {
        LuaType::Signature(signature_id) => context
            .db
            .get_signature_index()
            .get(signature_id)
            .map(|s| s.get_return_type()),
        LuaType::DocFunction(doc_func) => Some(doc_func.get_ret().clone()),
        _ => None,
    }
    .ok_or(InferFailReason::None)?;

    // 解析出迭代函数返回类型
    let final_return_type = match meta_return {
        LuaType::DocFunction(doc_func) => Some(doc_func.get_ret().clone()),
        LuaType::Signature(signature_id) => context
            .db
            .get_signature_index()
            .get(&signature_id)
            .map(|s| s.get_return_type()),
        _ => None,
    };

    if let Some(LuaType::Variadic(variadic)) = &final_return_type {
        let key_type = variadic.get_type(0).ok_or(InferFailReason::None)?;
        let value_type = variadic.get_type(1).ok_or(InferFailReason::None)?;
        tpl_pattern_match(context, &table_generic_params[0], key_type)?;
        tpl_pattern_match(context, &table_generic_params[1], value_type)?;
        return Ok(());
    }
    Err(InferFailReason::None)
}
