use sea_orm::entity::prelude::*;
use sea_orm::QueryOrder;
pub use super::_entities::product_variants::{ActiveModel, Column, Entity, Model};
pub type ProductVariants = 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 = sea_orm::ActiveValue::Set(chrono::Utc::now().into());
            Ok(this)
        } else {
            Ok(self)
        }
    }
}

// implement your read-oriented logic here
impl Model {
    /// Whether a discount is currently active: a sale price is set and is
    /// strictly below the regular price.
    #[must_use]
    pub fn on_sale(&self) -> bool {
        matches!(self.sale_price_cents, Some(sale) if sale < self.price_cents)
    }

    /// The price actually charged: the sale price when [`Model::on_sale`],
    /// otherwise the regular price.
    #[must_use]
    pub fn effective_price_cents(&self) -> i64 {
        if self.on_sale() {
            self.sale_price_cents.unwrap_or(self.price_cents)
        } else {
            self.price_cents
        }
    }

    /// Whether a baseline business discount (for all company accounts) is set and
    /// actually below the regular price.
    #[must_use]
    pub fn business_on_sale(&self) -> bool {
        matches!(self.business_sale_price_cents, Some(sale) if sale < self.price_cents)
    }

    /// Whether the variant's inventory is tracked. A `None` stock means
    /// "available, not tracked" (always purchasable, unlimited).
    #[must_use]
    pub fn tracked(&self) -> bool {
        self.stock.is_some()
    }

    /// Whether the variant can currently be bought: untracked variants are always
    /// available; tracked ones need a positive quantity on hand.
    #[must_use]
    pub fn in_stock(&self) -> bool {
        self.stock.map_or(true, |s| s > 0)
    }

    /// Clamp a desired quantity to what's available: capped at the tracked stock,
    /// or left as-is (only floored at 0) when untracked.
    #[must_use]
    pub fn cap(&self, qty: i32) -> i32 {
        match self.stock {
            Some(s) => qty.clamp(0, s),
            None => qty.max(0),
        }
    }
}

// implement your write-oriented logic here
impl ActiveModel {}

// implement your custom finders, selectors oriented logic here
impl Entity {
    /// All variants for one product, in display order.
    pub async fn for_product<C: ConnectionTrait>(
        db: &C,
        product_id: i32,
    ) -> Result<Vec<Model>, DbErr> {
        Entity::find()
            .filter(Column::ProductId.eq(product_id))
            .order_by_asc(Column::Position)
            .order_by_asc(Column::Id)
            .all(db)
            .await
    }

    /// All variants for many products in one query, grouped by `product_id` and
    /// ordered within each group. Products with no variants are absent.
    pub async fn grouped_for_products<C: ConnectionTrait>(
        db: &C,
        product_ids: &[i32],
    ) -> Result<std::collections::HashMap<i32, Vec<Model>>, DbErr> {
        let mut map: std::collections::HashMap<i32, Vec<Model>> = std::collections::HashMap::new();
        if product_ids.is_empty() {
            return Ok(map);
        }
        let rows = Entity::find()
            .filter(Column::ProductId.is_in(product_ids.to_vec()))
            .order_by_asc(Column::Position)
            .order_by_asc(Column::Id)
            .all(db)
            .await?;
        for row in rows {
            map.entry(row.product_id).or_default().push(row);
        }
        Ok(map)
    }
}
