use crate::ast::Assignment;
use crate::catalog::{Catalog, TableDef};
use crate::error::DbError;
use crate::storage::{Storage, StoredRow};

use super::super::super::clock::Clock;
use super::super::super::context::ExecutionContext;
use super::super::super::evaluator::eval_expr_to_type_in_context;
use super::super::super::model::single_row_context;
use super::padding::{apply_ansi_padding, enforce_string_length};

#[allow(clippy::too_many_arguments)]
pub(crate) fn apply_assignments(
    table: &TableDef,
    row: &mut StoredRow,
    assignments: &[Assignment],
    joined: &super::super::super::model::JoinedRow,
    ctx: &mut ExecutionContext<'_>,
    catalog: &mut dyn Catalog,
    storage: &mut dyn Storage,
    clock: &dyn Clock,
) -> Result<(), DbError> {
    for assignment in assignments {
        let idx = table
            .columns
            .iter()
            .position(|c| c.name.eq_ignore_ascii_case(&assignment.column))
            .ok_or_else(|| DbError::column_not_found(&assignment.column))?;
        let target = &table.columns[idx].data_type;
        if table.columns[idx].computed_expr.is_some() {
            return Err(DbError::Execution(format!(
                "cannot update computed column '{}'",
                table.columns[idx].name
            )));
        }
        let value = eval_expr_to_type_in_context(
            &assignment.expr,
            target,
            joined,
            ctx,
            catalog,
            storage,
            clock,
        )?;
        let mut value = value;
        apply_ansi_padding(&mut value, target, table.columns[idx].ansi_padding_on);
        enforce_string_length(target, &value, &table.columns[idx].name)?;
        row.values[idx] = value;
    }

    for (idx, col) in table.columns.iter().enumerate() {
        if let Some(computed) = &col.computed_expr {
            let snapshot = row.clone();
            let joined = single_row_context(table, snapshot);
            let value = super::super::super::evaluator::eval_expr(
                computed, &joined, ctx, catalog, storage, clock,
            )?;
            let mut value = value;
            apply_ansi_padding(&mut value, &col.data_type, col.ansi_padding_on);
            enforce_string_length(&col.data_type, &value, &col.name)?;
            row.values[idx] = value;
        }
    }

    for (col, value) in table.columns.iter().zip(row.values.iter_mut()) {
        apply_ansi_padding(value, &col.data_type, col.ansi_padding_on);
        enforce_string_length(&col.data_type, value, &col.name)?;
    }
    Ok(())
}
