//! Faithful port of `TypeFunctionSerializer::shallowSerialize(TypePackId tp)`
//! (Analysis/src/TypeFunctionRuntimeBuilder.cpp:257-292).
use crate::functions::follow_type_pack::follow_type_pack_id;
use crate::functions::get_type_pack::get_type_pack_id;
use crate::functions::to_string_to_string_alt_d::to_string_type_pack_id;
use crate::records::generic_type_pack::GenericTypePack;
use crate::records::type_function_generic_type_pack::TypeFunctionGenericTypePack;
use crate::records::type_function_serializer::TypeFunctionSerializer;
use crate::records::type_function_type_pack::TypeFunctionTypePack;
use crate::records::type_function_type_pack_var::TypeFunctionTypePackVar;
use crate::records::type_function_variadic_type_pack::TypeFunctionVariadicTypePack;
use crate::records::type_pack::TypePack;
use crate::records::unsupported_type_pack::UnsupportedTypePack;
use crate::records::variadic_type_pack::VariadicTypePack;
use crate::type_aliases::type_function_error_data::TypeFunctionErrorData;
use crate::type_aliases::type_function_kind::TypeFunctionKind;
use crate::type_aliases::type_function_type_pack_id::TypeFunctionTypePackId;
use crate::type_aliases::type_function_type_pack_variant::TypeFunctionTypePackVariant;
use crate::type_aliases::type_or_pack::TypeOrPack;
use crate::type_aliases::type_pack_id::TypePackId;
use alloc::format;
use alloc::vec::Vec;
use luaur_ast::records::location::Location;

impl TypeFunctionSerializer {
    pub fn shallow_serialize_type_pack_id(&mut self, tp: TypePackId) -> TypeFunctionTypePackId {
        let tp = unsafe { follow_type_pack_id(tp) };

        // if (auto it = find(tp)) return *it;
        if let Some(it) = self.packs.get(&tp).copied() {
            return it;
        }

        let state = self.state;
        let runtime = self.type_function_runtime;

        // Create a shallow serialization
        let mut target: TypeFunctionTypePackId = core::ptr::null();

        unsafe {
            if !get_type_pack_id::<TypePack>(tp).is_null() {
                target = (*runtime)
                    .type_pack_arena
                    .allocate(TypeFunctionTypePackVar::new(
                        TypeFunctionTypePackVariant::V0(TypeFunctionTypePack {
                            head: Vec::new(),
                            tail: None,
                        }),
                    ));
            } else if !get_type_pack_id::<VariadicTypePack>(tp).is_null() {
                target = (*runtime)
                    .type_pack_arena
                    .allocate(TypeFunctionTypePackVar::new(
                        TypeFunctionTypePackVariant::V1(TypeFunctionVariadicTypePack {
                            type_id: core::ptr::null(),
                        }),
                    ));
            } else if let Some(g_pack) = get_type_pack_id::<GenericTypePack>(tp).as_ref() {
                let mut name = g_pack.name.clone();

                if !g_pack.explicitName {
                    name = format!("g{}", g_pack.index);
                }

                target = (*runtime)
                    .type_pack_arena
                    .allocate(TypeFunctionTypePackVar::new(
                        TypeFunctionTypePackVariant::V2(TypeFunctionGenericTypePack {
                            is_named: g_pack.explicitName,
                            name,
                        }),
                    ));
            } else {
                if luaur_common::FFlag::LuauTypeFunctionStructuredErrors.get() {
                    (*state)
                        .errors
                        .push(crate::records::type_function_error::TypeFunctionError {
                            location: Location::default(),
                            module_name: alloc::string::String::new(),
                            data: TypeFunctionErrorData::V1(UnsupportedTypePack { pack: tp }),
                        });
                } else {
                    (*state).errors_deprecated.push(format!(
                        "Argument of type pack {} is not currently serializable by type functions",
                        to_string_type_pack_id(tp)
                    ));
                }
            }
        }

        // packs[tp] = target;
        *self.packs.get_or_insert(tp) = target;
        // queue.emplace_back(tp, target);
        self.queue
            .push((TypeOrPack::V1(tp), TypeFunctionKind::V1(target)));
        target
    }
}
