use crate::enums::subtyping_variance::SubtypingVariance;
use crate::records::path_hash::PathHash;
use crate::records::subtyping_reasoning::SubtypingReasoning;
use crate::records::subtyping_reasoning_hash::SubtypingReasoningHash;
use crate::records::subtyping_result::SubtypingResult;
use crate::type_aliases::path::Path;
use crate::type_aliases::subtyping_reasonings::SubtypingReasonings;
use luaur_common::records::dense_hash_table::{DenseDefault, DenseHasher};

impl PartialEq for SubtypingReasoning {
    fn eq(&self, other: &Self) -> bool {
        self.operator_eq(other)
    }
}

impl Eq for SubtypingReasoning {}

impl DenseHasher<SubtypingReasoning> for SubtypingReasoningHash {
    fn hash(&self, r: &SubtypingReasoning) -> usize {
        PathHash.operator_call_6(&r.sub_path)
            ^ (PathHash.operator_call_6(&r.super_path) << 1)
            ^ ((r.variance as usize) << 1)
            ^ ((r.is_property_modifier_violation as usize) << 2)
    }
}

pub(crate) fn k_empty_reasoning() -> SubtypingReasoning {
    SubtypingReasoning {
        sub_path: Path::default(),
        super_path: Path::default(),
        variance: SubtypingVariance::Invalid,
        is_property_modifier_violation: false,
    }
}

impl Default for SubtypingResult {
    fn default() -> Self {
        SubtypingResult {
            is_subtype: false,
            normalization_too_complex: false,
            is_cacheable: true,
            is_error_suppressing: false,
            errors: Default::default(),
            reasoning: SubtypingReasonings::new(k_empty_reasoning()),
            assumed_constraints: Default::default(),
            generic_bounds_mismatches: Default::default(),
        }
    }
}

impl DenseDefault for SubtypingResult {
    fn dense_default() -> Self {
        SubtypingResult::default()
    }
}

pub fn merge_reasonings(a: &SubtypingReasonings, b: &SubtypingReasonings) -> SubtypingReasonings {
    let mut result = SubtypingReasonings::new(k_empty_reasoning());

    for r in a.iter() {
        if r.variance == SubtypingVariance::Invariant {
            result.insert(r.clone());
        } else if r.variance == SubtypingVariance::Covariant
            || r.variance == SubtypingVariance::Contravariant
        {
            let inverse_reasoning = SubtypingReasoning {
                sub_path: r.sub_path.clone(),
                super_path: r.super_path.clone(),
                variance: if r.variance == SubtypingVariance::Covariant {
                    SubtypingVariance::Contravariant
                } else {
                    SubtypingVariance::Covariant
                },
                is_property_modifier_violation: false,
            };

            if b.contains(&inverse_reasoning) {
                result.insert(SubtypingReasoning {
                    sub_path: r.sub_path.clone(),
                    super_path: r.super_path.clone(),
                    variance: SubtypingVariance::Invariant,
                    is_property_modifier_violation: false,
                });
            } else {
                result.insert(r.clone());
            }
        }

        if result.size() >= luaur_common::FInt::LuauSubtypingReasoningLimit.get() as usize {
            return result;
        }
    }

    for r in b.iter() {
        if r.variance == SubtypingVariance::Invariant {
            result.insert(r.clone());
        } else if r.variance == SubtypingVariance::Covariant
            || r.variance == SubtypingVariance::Contravariant
        {
            let inverse_reasoning = SubtypingReasoning {
                sub_path: r.sub_path.clone(),
                super_path: r.super_path.clone(),
                variance: if r.variance == SubtypingVariance::Covariant {
                    SubtypingVariance::Contravariant
                } else {
                    SubtypingVariance::Covariant
                },
                is_property_modifier_violation: false,
            };

            if a.contains(&inverse_reasoning) {
                result.insert(SubtypingReasoning {
                    sub_path: r.sub_path.clone(),
                    super_path: r.super_path.clone(),
                    variance: SubtypingVariance::Invariant,
                    is_property_modifier_violation: false,
                });
            } else {
                result.insert(r.clone());
            }
        }

        if result.size() >= luaur_common::FInt::LuauSubtypingReasoningLimit.get() as usize {
            return result;
        }
    }

    result
}
