use internment::ArcIntern;
use rowan::TextRange;
use smol_str::SmolStr;
use std::{hash::Hash, sync::Arc};

use crate::db_index::LuaSignatureId;
use crate::{FileId, InFiled};

use super::super::type_decl::LuaTypeDeclId;
use super::complex::{
    GenericTpl, LuaAliasCallType, LuaArrayType, LuaConditionalType, LuaFunctionType,
    LuaGenericType, LuaInstanceType, LuaIntersectionType, LuaMappedType, LuaMultiLineUnion,
    LuaObjectType, LuaStringTplType, LuaTupleType, LuaUnionType, VariadicType,
};

#[derive(Debug, Clone)]
pub enum LuaType {
    Unknown,
    Any,
    Nil,
    Table,
    Userdata,
    Function,
    Thread,
    Boolean,
    String,
    Integer,
    Number,
    Io,
    SelfInfer,
    Global,
    Never,
    BooleanConst(bool),
    StringConst(ArcIntern<SmolStr>),
    IntegerConst(i64),
    FloatConst(f64),
    TableConst(InFiled<TextRange>),
    Ref(LuaTypeDeclId),
    Def(LuaTypeDeclId),
    Array(Arc<LuaArrayType>),
    Tuple(Arc<LuaTupleType>),
    DocFunction(Arc<LuaFunctionType>),
    Object(Arc<LuaObjectType>),
    Union(Arc<LuaUnionType>),
    Intersection(Arc<LuaIntersectionType>),
    Generic(Arc<LuaGenericType>),
    TableGeneric(Arc<Vec<LuaType>>),
    TplRef(Arc<GenericTpl>),
    StrTplRef(Arc<LuaStringTplType>),
    Variadic(Arc<VariadicType>),
    Signature(LuaSignatureId),
    Instance(Arc<LuaInstanceType>),
    DocStringConst(ArcIntern<SmolStr>),
    DocIntegerConst(i64),
    DocBooleanConst(bool),
    Namespace(ArcIntern<SmolStr>),
    Call(Arc<LuaAliasCallType>),
    MultiLineUnion(Arc<LuaMultiLineUnion>),
    TypeGuard(Arc<LuaType>),
    Language(ArcIntern<SmolStr>),
    ModuleRef(FileId),
    Conditional(Arc<LuaConditionalType>),
    Mapped(Arc<LuaMappedType>),
}

impl PartialEq for LuaType {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (LuaType::Unknown, LuaType::Unknown) => true,
            (LuaType::Any, LuaType::Any) => true,
            (LuaType::Nil, LuaType::Nil) => true,
            (LuaType::Table, LuaType::Table) => true,
            (LuaType::Userdata, LuaType::Userdata) => true,
            (LuaType::Function, LuaType::Function) => true,
            (LuaType::Thread, LuaType::Thread) => true,
            (LuaType::Boolean, LuaType::Boolean) => true,
            (LuaType::String, LuaType::String) => true,
            (LuaType::Integer, LuaType::Integer) => true,
            (LuaType::Number, LuaType::Number) => true,
            (LuaType::Io, LuaType::Io) => true,
            (LuaType::SelfInfer, LuaType::SelfInfer) => true,
            (LuaType::Global, LuaType::Global) => true,
            (LuaType::BooleanConst(a), LuaType::BooleanConst(b)) => a == b,
            (LuaType::StringConst(a), LuaType::StringConst(b)) => a == b,
            (LuaType::IntegerConst(a), LuaType::IntegerConst(b)) => a == b,
            (LuaType::FloatConst(a), LuaType::FloatConst(b)) => a == b,
            (LuaType::TableConst(a), LuaType::TableConst(b)) => a == b,
            (LuaType::Ref(a), LuaType::Ref(b)) => a == b,
            (LuaType::Def(a), LuaType::Def(b)) => a == b,
            (LuaType::Array(a), LuaType::Array(b)) => a == b,
            (LuaType::Call(a), LuaType::Call(b)) => a == b,
            (LuaType::Tuple(a), LuaType::Tuple(b)) => a == b,
            (LuaType::DocFunction(a), LuaType::DocFunction(b)) => a == b,
            (LuaType::Object(a), LuaType::Object(b)) => a == b,
            (LuaType::Union(a), LuaType::Union(b)) => a == b,
            (LuaType::Intersection(a), LuaType::Intersection(b)) => a == b,
            (LuaType::Generic(a), LuaType::Generic(b)) => a == b,
            (LuaType::TableGeneric(a), LuaType::TableGeneric(b)) => a == b,
            (LuaType::TplRef(a), LuaType::TplRef(b)) => a == b,
            (LuaType::StrTplRef(a), LuaType::StrTplRef(b)) => a == b,
            (LuaType::Variadic(a), LuaType::Variadic(b)) => a == b,
            (LuaType::DocBooleanConst(a), LuaType::DocBooleanConst(b)) => a == b,
            (LuaType::Signature(a), LuaType::Signature(b)) => a == b,
            (LuaType::Instance(a), LuaType::Instance(b)) => a == b,
            (LuaType::DocStringConst(a), LuaType::DocStringConst(b)) => a == b,
            (LuaType::DocIntegerConst(a), LuaType::DocIntegerConst(b)) => a == b,
            (LuaType::Namespace(a), LuaType::Namespace(b)) => a == b,
            (LuaType::MultiLineUnion(a), LuaType::MultiLineUnion(b)) => a == b,
            (LuaType::TypeGuard(a), LuaType::TypeGuard(b)) => a == b,
            (LuaType::Never, LuaType::Never) => true,
            (LuaType::Language(a), LuaType::Language(b)) => a == b,
            (LuaType::ModuleRef(a), LuaType::ModuleRef(b)) => a == b,
            (LuaType::Conditional(a), LuaType::Conditional(b)) => a == b,
            (LuaType::Mapped(a), LuaType::Mapped(b)) => a == b,
            _ => false,
        }
    }
}

impl Eq for LuaType {}

impl Hash for LuaType {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        match self {
            LuaType::Unknown => 0.hash(state),
            LuaType::Any => 1.hash(state),
            LuaType::Nil => 2.hash(state),
            LuaType::Table => 3.hash(state),
            LuaType::Userdata => 4.hash(state),
            LuaType::Function => 5.hash(state),
            LuaType::Thread => 6.hash(state),
            LuaType::Boolean => 7.hash(state),
            LuaType::String => 8.hash(state),
            LuaType::Integer => 9.hash(state),
            LuaType::Number => 10.hash(state),
            LuaType::Io => 11.hash(state),
            LuaType::SelfInfer => 12.hash(state),
            LuaType::Global => 13.hash(state),
            LuaType::BooleanConst(a) => (14, a).hash(state),
            LuaType::StringConst(a) => (15, a).hash(state),
            LuaType::IntegerConst(a) => (16, a).hash(state),
            LuaType::FloatConst(a) => (17, a.to_bits()).hash(state),
            LuaType::TableConst(a) => (18, a).hash(state),
            LuaType::Ref(a) => (19, a).hash(state),
            LuaType::Def(a) => (20, a).hash(state),
            LuaType::Array(a) => (22, a).hash(state),
            LuaType::Call(a) => (23, a).hash(state),
            LuaType::Tuple(a) => (25, a).hash(state),
            LuaType::DocFunction(a) => (26, a).hash(state),
            LuaType::Object(a) => (27, Arc::as_ptr(a)).hash(state),
            LuaType::Union(a) => (28, Arc::as_ptr(a)).hash(state),
            LuaType::Intersection(a) => (29, Arc::as_ptr(a)).hash(state),
            LuaType::Generic(a) => (30, Arc::as_ptr(a)).hash(state),
            LuaType::TableGeneric(a) => (31, Arc::as_ptr(a)).hash(state),
            LuaType::TplRef(a) => (32, Arc::as_ptr(a)).hash(state),
            LuaType::StrTplRef(a) => (33, Arc::as_ptr(a)).hash(state),
            LuaType::Variadic(a) => (34, Arc::as_ptr(a)).hash(state),
            LuaType::DocBooleanConst(a) => (35, a).hash(state),
            LuaType::Signature(a) => (36, a).hash(state),
            LuaType::Instance(a) => (37, a).hash(state),
            LuaType::DocStringConst(a) => (38, a).hash(state),
            LuaType::DocIntegerConst(a) => (39, a).hash(state),
            LuaType::Namespace(a) => (40, a).hash(state),
            LuaType::MultiLineUnion(a) => (43, Arc::as_ptr(a)).hash(state),
            LuaType::TypeGuard(a) => (44, Arc::as_ptr(a)).hash(state),
            LuaType::Never => 45.hash(state),
            LuaType::Language(a) => (46, a).hash(state),
            LuaType::ModuleRef(a) => (47, a).hash(state),
            LuaType::Conditional(a) => (48, Arc::as_ptr(a)).hash(state),
            LuaType::Mapped(a) => (49, Arc::as_ptr(a)).hash(state),
        }
    }
}
