//! Per-account negotiated product prices: an admin-set price for one product,
//! for one business account ("personal agreement"). One row per (user, product),
//! kept unique by the index in the migration.

pub use crate::models::_entities::account_product_prices::{ActiveModel, Column, Entity, Model};
use sea_orm::entity::prelude::*;
use sea_orm::{ActiveValue, IntoActiveModel, QueryFilter, TryIntoModel};

pub type AccountProductPrices = Entity;

#[async_trait::async_trait]
impl ActiveModelBehavior for ActiveModel {
    async fn before_save<C>(self, _db: &C, insert: bool) -> std::result::Result<Self, DbErr>
    where
        C: ConnectionTrait,
    {
        if !insert && self.updated_at.is_unchanged() {
            let mut this = self;
            this.updated_at = ActiveValue::set(chrono::Utc::now().into());
            Ok(this)
        } else {
            Ok(self)
        }
    }
}

impl Model {
    /// All negotiated prices for one account, as a `(variant_id -> cents)` map.
    pub async fn map_for_user(
        db: &DatabaseConnection,
        user_id: i32,
    ) -> Result<std::collections::HashMap<i32, i64>, DbErr> {
        let rows = Entity::find()
            .filter(Column::UserId.eq(user_id))
            .all(db)
            .await?;
        Ok(rows.into_iter().map(|r| (r.variant_id, r.price_cents)).collect())
    }

    /// Insert or update the negotiated price for `(user_id, variant_id)`.
    pub async fn upsert(
        db: &DatabaseConnection,
        user_id: i32,
        variant_id: i32,
        price_cents: i64,
    ) -> Result<Self, DbErr> {
        let existing = Entity::find()
            .filter(Column::UserId.eq(user_id))
            .filter(Column::VariantId.eq(variant_id))
            .one(db)
            .await?;
        let mut active = match existing {
            Some(row) => row.into_active_model(),
            None => ActiveModel {
                user_id: ActiveValue::set(user_id),
                variant_id: ActiveValue::set(variant_id),
                ..Default::default()
            },
        };
        active.price_cents = ActiveValue::set(price_cents);
        active.save(db).await?.try_into_model()
    }

    /// Remove the negotiated price for `(user_id, variant_id)`, if any.
    pub async fn clear(
        db: &DatabaseConnection,
        user_id: i32,
        variant_id: i32,
    ) -> Result<(), DbErr> {
        Entity::delete_many()
            .filter(Column::UserId.eq(user_id))
            .filter(Column::VariantId.eq(variant_id))
            .exec(db)
            .await?;
        Ok(())
    }
}
