use crate::error::DbError;

use crate::executor::context::ExecutionContext;
use crate::executor::grouping::GroupExecutor;
use crate::executor::model::JoinedRow;
use crate::executor::query::plan::RelationalQuery;
use crate::executor::result;
use crate::executor::window::WindowExecutor;

use super::super::QueryExecutor;
use super::analysis::PipelineState;

pub(crate) fn execute_projection_stage(
    executor: &QueryExecutor<'_>,
    query: &RelationalQuery,
    source_rows: Vec<JoinedRow>,
    ctx: &mut ExecutionContext,
    state: &PipelineState,
) -> Result<result::QueryResult, DbError> {
    if !query.filter.group_by.is_empty() || state.has_aggregate {
        let group_executor = GroupExecutor {
            catalog: executor.catalog,
            storage: executor.storage,
            clock: executor.clock,
        };
        group_executor.execute_grouped_select(
            query.projection.items.clone(),
            source_rows,
            query.filter.group_by.clone(),
            query.filter.having.clone(),
            ctx,
        )
    } else if state.has_window {
        let window_executor =
            WindowExecutor::new(executor.catalog, executor.storage, executor.clock);
        window_executor.execute(&query.projection.items, source_rows, ctx)
    } else {
        crate::executor::query::projection::execute_flat_select(
            executor.catalog,
            executor.storage,
            executor.clock,
            query.projection.items.clone(),
            source_rows,
            ctx,
        )
    }
}
