mod implicit_many_to_many;
mod inline;
mod two_way_embedded_many_to_many;
pub use implicit_many_to_many::{ImplicitManyToManyRelationTableName, ImplicitManyToManyRelationWalker};
pub use inline::{CompleteInlineRelationWalker, InlineRelationWalker};
pub use two_way_embedded_many_to_many::TwoWayEmbeddedManyToManyRelationWalker;
use crate::{ast, relations::*, walkers::*};
pub type RelationWalker<'db> = Walker<'db, RelationId>;
impl<'db> RelationWalker<'db> {
    pub fn models(self) -> [ast::ModelId; 2] {
        let rel = self.get();
        [rel.model_a, rel.model_b]
    }
    pub fn relation_fields(self) -> impl Iterator<Item = RelationFieldWalker<'db>> {
        let (a, b) = self.get().attributes.fields();
        [a, b].into_iter().flatten().map(move |field| self.walk(field))
    }
    pub fn is_ignored(self) -> bool {
        self.relation_fields().any(|f| {
            f.is_ignored()
                || f.referencing_fields()
                    .into_iter()
                    .flatten()
                    .any(|scalar_field| scalar_field.is_ignored() || scalar_field.is_unsupported())
        })
    }
    pub fn is_self_relation(self) -> bool {
        let r = self.get();
        r.model_a == r.model_b
    }
    pub fn refine(self) -> RefinedRelationWalker<'db> {
        if self.get().is_implicit_many_to_many() {
            RefinedRelationWalker::ImplicitManyToMany(self.walk(ManyToManyRelationId(self.id)))
        } else if self.get().is_two_way_embedded_many_to_many() {
            RefinedRelationWalker::TwoWayEmbeddedManyToMany(TwoWayEmbeddedManyToManyRelationWalker(self))
        } else {
            RefinedRelationWalker::Inline(InlineRelationWalker(self))
        }
    }
    pub fn explicit_relation_name(self) -> Option<&'db str> {
        self.get().relation_name.map(|string_id| &self.db[string_id])
    }
    pub fn relation_name(self) -> RelationName<'db> {
        let relation = self.get();
        relation
            .relation_name
            .map(|s| RelationName::Explicit(&self.db[s]))
            .unwrap_or_else(|| {
                RelationName::generated(self.walk(relation.model_a).name(), self.walk(relation.model_b).name())
            })
    }
    fn get(self) -> &'db Relation {
        &self.db.relations[self.id]
    }
}
pub enum RefinedRelationWalker<'db> {
    Inline(InlineRelationWalker<'db>),
    ImplicitManyToMany(ImplicitManyToManyRelationWalker<'db>),
    TwoWayEmbeddedManyToMany(TwoWayEmbeddedManyToManyRelationWalker<'db>),
}
impl<'db> RefinedRelationWalker<'db> {
    pub fn as_inline(&self) -> Option<InlineRelationWalker<'db>> {
        match self {
            RefinedRelationWalker::Inline(inline) => Some(*inline),
            _ => None,
        }
    }
    pub fn as_many_to_many(&self) -> Option<ImplicitManyToManyRelationWalker<'db>> {
        match self {
            RefinedRelationWalker::ImplicitManyToMany(m2m) => Some(*m2m),
            _ => None,
        }
    }
}