use super::*;
use crate::{
    context::Context,
    filter::alias::{Alias, AliasMode},
    model_extensions::*,
};
use quaint::ast::*;
use query_structure::*;
#[derive(Debug, Default)]
pub(crate) struct SubqueriesSelectBuilder {
    alias: Alias,
}
impl JoinSelectBuilder for SubqueriesSelectBuilder {
    fn build(&mut self, args: QueryArguments, selected_fields: &FieldSelection, ctx: &Context<'_>) -> Select<'static> {
        let (select, alias) = self.build_default_select(&args, ctx);
        self.with_selection(select, selected_fields, alias, ctx)
    }
    fn build_selection<'a>(
        &mut self,
        select: Select<'a>,
        field: &SelectedField,
        parent_alias: Alias,
        ctx: &Context<'_>,
    ) -> Select<'a> {
        match field {
            SelectedField::Scalar(sf) => select.column(
                sf.as_column(ctx)
                    .table(parent_alias.to_table_string())
                    .set_is_selected(true),
            ),
            SelectedField::Relation(rs) => self.with_relation(select, rs, parent_alias, ctx),
            _ => select,
        }
    }
    fn add_to_one_relation<'a>(
        &mut self,
        select: Select<'a>,
        rs: &RelationSelection,
        parent_alias: Alias,
        ctx: &Context<'_>,
    ) -> Select<'a> {
        let (subselect, _) = self.build_to_one_select(rs, parent_alias, |x| x, ctx);
        select.value(Expression::from(subselect).alias(rs.field.name().to_owned()))
    }
    fn add_to_many_relation<'a>(
        &mut self,
        select: Select<'a>,
        rs: &RelationSelection,
        parent_alias: Alias,
        ctx: &Context<'_>,
    ) -> Select<'a> {
        let subselect = self.build_to_many_select(rs, parent_alias, ctx);
        select.value(Expression::from(subselect).alias(rs.field.name().to_owned()))
    }
    fn add_many_to_many_relation<'a>(
        &mut self,
        select: Select<'a>,
        rs: &RelationSelection,
        parent_alias: Alias,
        ctx: &Context<'_>,
    ) -> Select<'a> {
        let subselect = self.build_m2m_select(rs, parent_alias, ctx);
        select.value(Expression::from(subselect).alias(rs.field.name().to_owned()))
    }
    fn add_virtual_selection<'a>(
        &mut self,
        select: Select<'a>,
        vs: &VirtualSelection,
        parent_alias: Alias,
        ctx: &Context<'_>,
    ) -> Select<'a> {
        let virtual_select = self.build_virtual_select(vs, parent_alias, ctx);
        let alias = relation_count_alias_name(vs.relation_field());
        select.value(Expression::from(virtual_select).alias(alias))
    }
    fn build_json_obj_fn(
        &mut self,
        rs: &RelationSelection,
        parent_alias: Alias,
        ctx: &Context<'_>,
    ) -> Expression<'static> {
        let virtuals = self.build_json_obj_virtual_selection(rs.virtuals(), parent_alias, ctx);
        let build_obj_params = rs
            .selections
            .iter()
            .filter_map(|field| match field {
                SelectedField::Scalar(sf) => Some((
                    Cow::from(sf.db_name().to_owned()),
                    Expression::from(sf.as_column(ctx).table(parent_alias.to_table_string())),
                )),
                SelectedField::Relation(rs) => Some((
                    Cow::from(rs.field.name().to_owned()),
                    Expression::from(self.with_relation(Select::default(), rs, parent_alias, ctx)),
                )),
                _ => None,
            })
            .chain(virtuals)
            .collect();
        json_build_object(build_obj_params).into()
    }
    fn build_virtual_expr(
        &mut self,
        vs: &VirtualSelection,
        parent_alias: Alias,
        ctx: &Context<'_>,
    ) -> Expression<'static> {
        coalesce([
            Expression::from(self.build_virtual_select(vs, parent_alias, ctx)),
            Expression::from(0.raw()),
        ])
        .into()
    }
    fn next_alias(&mut self) -> Alias {
        self.alias = self.alias.inc(AliasMode::Table);
        self.alias
    }
}
impl SubqueriesSelectBuilder {
    fn build_m2m_select<'a>(&mut self, rs: &RelationSelection, parent_alias: Alias, ctx: &Context<'_>) -> Select<'a> {
        let rf = rs.field.clone();
        let m2m_table_alias = self.next_alias();
        let root_alias = self.next_alias();
        let outer_alias = self.next_alias();
        let m2m_join_data =
            rf.related_model()
                .as_table(ctx)
                .on(rf.m2m_join_conditions(Some(m2m_table_alias), None, ctx));
        let m2m_table = rf.as_table(ctx).alias(m2m_table_alias.to_table_string());
        let root = Select::from_table(m2m_table)
            .inner_join(m2m_join_data)
            .value(rf.related_model().as_table(ctx).asterisk())
            .with_ordering(&rs.args, None, ctx) .and_where(
                rf.related_field()
                    .m2m_join_conditions(Some(m2m_table_alias), Some(parent_alias), ctx),
            ) .with_filters(rs.args.filter.clone(), None, ctx) .comment("root");
        let take = match rs.args.order_by.is_empty() {
            true => rs.args.take_abs(),
            false => rs.args.take_abs().or(Some(i64::MAX)),
        };
        let inner = Select::from_table(Table::from(root).alias(root_alias.to_table_string()))
            .value(self.build_json_obj_fn(rs, root_alias, ctx).alias(JSON_AGG_IDENT))
            .with_pagination(take, rs.args.skip)
            .comment("inner"); Select::from_table(Table::from(inner).alias(outer_alias.to_table_string()))
            .value(json_agg())
            .comment("outer")
    }
}