use crate::enums::table_state::TableState;
use crate::enums::unify_result::UnifyResult;
use crate::records::property_type::Property;
use crate::records::table_indexer::TableIndexer;
use crate::records::table_type::TableType;
use crate::records::unifier_2::Unifier2;
use crate::type_aliases::type_id::TypeId;

impl Unifier2 {
    pub fn unify_table_type_table_type(
        &mut self,
        sub_table: &mut TableType,
        super_table: &TableType,
    ) -> UnifyResult {
        let mut result = UnifyResult::Ok;

        for (prop_name, sub_prop) in &sub_table.props {
            if let Some(super_prop_opt) = super_table.props.get(prop_name) {
                let super_prop = super_prop_opt;

                if let (Some(sub_read), Some(super_read)) = (sub_prop.read_ty, super_prop.read_ty) {
                    result &= self.unify_type_id_type_id(sub_read, super_read);
                }

                if let (Some(sub_write), Some(super_write)) =
                    (sub_prop.write_ty, super_prop.write_ty)
                {
                    result &= self.unify_type_id_type_id(sub_write, super_write);
                }
            }
        }

        let mut sub_type_params_iter = sub_table.instantiated_type_params.iter();
        let mut super_type_params_iter = super_table.instantiated_type_params.iter();

        while let (Some(sub_tp), Some(super_tp)) =
            (sub_type_params_iter.next(), super_type_params_iter.next())
        {
            result &= self.unify_type_id_type_id(*sub_tp, *super_tp);
        }

        let mut sub_type_pack_params_iter = sub_table.instantiated_type_pack_params.iter();
        let mut super_type_pack_params_iter = super_table.instantiated_type_pack_params.iter();

        while let (Some(sub_tpp), Some(super_tpp)) = (
            sub_type_pack_params_iter.next(),
            super_type_pack_params_iter.next(),
        ) {
            result &= self.unify_type_pack_id_type_pack_id(*sub_tpp, *super_tpp);
        }

        if let (Some(sub_indexer), Some(super_indexer)) = (&sub_table.indexer, &super_table.indexer)
        {
            result &= self.unify_type_id_type_id(sub_indexer.index_type, super_indexer.index_type);
            result &= self.unify_type_id_type_id(
                sub_indexer.index_result_type,
                super_indexer.index_result_type,
            );

            result &= self.unify_type_id_type_id(super_indexer.index_type, sub_indexer.index_type);
            result &= self.unify_type_id_type_id(
                super_indexer.index_result_type,
                sub_indexer.index_result_type,
            );
        }

        if sub_table.indexer.is_none()
            && sub_table.state == TableState::Unsealed
            && super_table.indexer.is_some()
        {
            let super_indexer = super_table.indexer.as_ref().unwrap();

            let mut index_type = super_indexer.index_type;
            if let Some(subst) = self.generic_substitutions.find(&index_type) {
                index_type = *subst;
            }

            let mut index_result_type = super_indexer.index_result_type;
            if let Some(subst) = self.generic_substitutions.find(&index_result_type) {
                index_result_type = *subst;
            }

            sub_table.indexer = Some(TableIndexer {
                index_type,
                index_result_type,
                is_read_only: false,
            });
        }

        result
    }
}
