use es_entity::*;
use sqlx::PgPool;
use tracing::instrument;

use crate::{primitives::VelocityLimitId, velocity::error::VelocityError};

use super::entity::*;

#[derive(EsRepo, Debug, Clone)]
#[es_repo(
    entity = "VelocityLimit",
    columns(name(ty = "String", update(persist = false)),),
    tbl_prefix = "cala",
    persist_event_context = false
)]
pub struct VelocityLimitRepo {
    pool: PgPool,
}

impl VelocityLimitRepo {
    pub fn new(pool: &PgPool) -> Self {
        Self { pool: pool.clone() }
    }

    #[instrument(name = "velocity_limit.add_limit_to_control", skip_all)]
    pub async fn add_limit_to_control(
        &self,
        op: &mut impl es_entity::AtomicOperation,
        control: VelocityControlId,
        limit: VelocityLimitId,
    ) -> Result<(), VelocityError> {
        sqlx::query!(
            r#"INSERT INTO cala_velocity_control_limits (velocity_control_id, velocity_limit_id)
            VALUES ($1, $2)"#,
            control as VelocityControlId,
            limit as VelocityLimitId,
        )
        .execute(op.as_executor())
        .await?;
        Ok(())
    }

    #[instrument(
        name = "velocity_limit.list_for_control",
        skip_all,
        err(level = "warn")
    )]
    pub async fn list_for_control(
        &self,
        op: impl es_entity::IntoOneTimeExecutor<'_>,
        control: VelocityControlId,
    ) -> Result<Vec<VelocityLimit>, VelocityError> {
        let rows = op
            .into_executor()
            .fetch_all(sqlx::query_as!(
                GenericEvent,
                r#"WITH limits AS (
              SELECT id, l.created_at AS entity_created_at
              FROM cala_velocity_limits l
              JOIN cala_velocity_control_limits ON id = velocity_limit_id
              WHERE velocity_control_id = $1
            )
            SELECT l.id as entity_id, e.sequence, e.event, NULL as "context: es_entity::ContextData", e.recorded_at
            FROM limits l
            JOIN cala_velocity_limit_events e ON l.id = e.id
            ORDER BY l.id, e.sequence"#,
                control as VelocityControlId,
            ))
            .await?;
        let n = rows.len();
        let ret = EntityEvents::load_n::<VelocityLimit>(rows, n)?.0;
        Ok(ret)
    }
}
