//! Model pricing management — CRUD.
//!
//! All functions take `&DatabaseConnection` instead of `&AppService`.

use chrono::Utc;
use db::database::AppDatabase;
use models::agents::PricingCurrency;
use models::agents::model_pricing;
use sea_orm::*;
use uuid::Uuid;

use crate::error::AgentError;

#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
pub struct CreateModelPricingRequest {
    pub model_version_id: Uuid,
    pub input_price_per_1k_tokens: String,
    pub output_price_per_1k_tokens: String,
    pub currency: String,
    pub effective_from: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
pub struct UpdateModelPricingRequest {
    pub input_price_per_1k_tokens: Option<String>,
    pub output_price_per_1k_tokens: Option<String>,
    pub currency: Option<String>,
    pub effective_from: Option<chrono::DateTime<Utc>>,
}

#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct ModelPricingResponse {
    pub id: i64,
    pub model_version_id: Uuid,
    pub input_price_per_1k_tokens: String,
    pub output_price_per_1k_tokens: String,
    pub currency: String,
    pub effective_from: chrono::DateTime<Utc>,
}

impl From<model_pricing::Model> for ModelPricingResponse {
    fn from(p: model_pricing::Model) -> Self {
        Self {
            id: p.id,
            model_version_id: p.model_version_id,
            input_price_per_1k_tokens: p.input_price_per_1k_tokens,
            output_price_per_1k_tokens: p.output_price_per_1k_tokens,
            currency: p.currency,
            effective_from: p.effective_from,
        }
    }
}

/// List pricing records for a model version.
pub async fn list_pricing(
    db: &AppDatabase,
    model_version_id: Uuid,
) -> Result<Vec<ModelPricingResponse>, AgentError> {
    let records = model_pricing::Entity::find()
        .filter(model_pricing::Column::ModelVersionId.eq(model_version_id))
        .order_by_desc(model_pricing::Column::EffectiveFrom)
        .all(db)
        .await?;
    Ok(records.into_iter().map(ModelPricingResponse::from).collect())
}

/// Get a single pricing record by ID.
pub async fn get_pricing(db: &AppDatabase, id: i64) -> Result<ModelPricingResponse, AgentError> {
    let record = model_pricing::Entity::find_by_id(id)
        .one(db)
        .await?
        .ok_or_else(|| AgentError::NotFound(format!("Pricing record not found: {}", id)))?;
    Ok(ModelPricingResponse::from(record))
}

/// Create a new pricing record.
pub async fn create_pricing(
    db: &AppDatabase,
    request: CreateModelPricingRequest,
) -> Result<ModelPricingResponse, AgentError> {
    let _ = request
        .currency
        .parse::<PricingCurrency>()
        .map_err(|_| AgentError::InvalidInput {
            field: "currency".into(),
            reason: "Invalid pricing currency".into(),
        })?;

    let active = model_pricing::ActiveModel {
        model_version_id: Set(request.model_version_id),
        input_price_per_1k_tokens: Set(request.input_price_per_1k_tokens),
        output_price_per_1k_tokens: Set(request.output_price_per_1k_tokens),
        currency: Set(request.currency),
        effective_from: Set(request.effective_from),
        ..Default::default()
    };
    let record = active.insert(db).await?;
    Ok(ModelPricingResponse::from(record))
}

/// Update a pricing record.
pub async fn update_pricing(
    db: &AppDatabase,
    id: i64,
    request: UpdateModelPricingRequest,
) -> Result<ModelPricingResponse, AgentError> {
    let record = model_pricing::Entity::find_by_id(id)
        .one(db)
        .await?
        .ok_or_else(|| AgentError::NotFound(format!("Pricing record not found: {}", id)))?;

    let mut active: model_pricing::ActiveModel = record.into();
    if let Some(v) = request.input_price_per_1k_tokens {
        active.input_price_per_1k_tokens = Set(v);
    }
    if let Some(v) = request.output_price_per_1k_tokens {
        active.output_price_per_1k_tokens = Set(v);
    }
    if let Some(v) = request.currency {
        let _ = v
            .parse::<PricingCurrency>()
            .map_err(|_| AgentError::InvalidInput {
                field: "currency".into(),
                reason: "Invalid pricing currency".into(),
            })?;
        active.currency = Set(v);
    }
    if let Some(v) = request.effective_from {
        active.effective_from = Set(v);
    }

    let record = active.update(db).await?;
    Ok(ModelPricingResponse::from(record))
}

/// Delete a pricing record.
pub async fn delete_pricing(db: &AppDatabase, id: i64) -> Result<(), AgentError> {
    model_pricing::Entity::delete_by_id(id).exec(db).await?;
    Ok(())
}
