//! 用户查询自己的积分余额与流水。

use crate::{
    domain::vo::ApiResponse,
    error::ErrorResponse,
    infra::middleware::UserId,
    repositories::quota_repository::QuotaRepository,
    services::{credit_rule_service::CreditRuleService, credit_service::CreditService},
    AppState,
};
use axum::{extract::Query, extract::State, Json};
use serde::Deserialize;

pub async fn get_balance(
    State(state): State<AppState>,
    UserId(user_id): UserId,
) -> Result<Json<ApiResponse<serde_json::Value>>, ErrorResponse> {
    let svc = CreditService::new(state.pool.clone());
    let rules = CreditRuleService::new(state.pool.clone())
        .get()
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    let account = svc
        .get_account(&user_id)
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    let usage = QuotaRepository::new(state.pool.clone())
        .get_today(&user_id)
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    let balance = account.as_ref().map(|value| value.balance).unwrap_or(0);
    let mailboxes_created = usage
        .as_ref()
        .map(|value| value.mailboxes_created)
        .unwrap_or(0);
    let emails_received = usage
        .as_ref()
        .map(|value| value.emails_received)
        .unwrap_or(0);
    let bytes_received = usage
        .as_ref()
        .map(|value| value.bytes_received)
        .unwrap_or(0);
    let check_in = svc
        .checked_in_today(&user_id)
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    Ok(Json(ApiResponse::success(serde_json::json!({
        "enabled": true,
        "balance": balance,
        "total_granted": account.as_ref().map(|value| value.total_granted).unwrap_or(0),
        "total_consumed": account.as_ref().map(|value| value.total_consumed).unwrap_or(0),
        "rule_version": rules.version,
        "pricing": {
            "create_mailbox": rules.create_mailbox_cost,
            "receive_email": rules.receive_email_cost,
        },
        "daily_limits": {
            "mailboxes": rules.daily_mailboxes_limit,
            "emails": rules.daily_emails_limit,
        },
        "usage_today": {
            "mailboxes_created": mailboxes_created,
            "emails_received": emails_received,
            "bytes_received": bytes_received,
        },
        "remaining_today": {
            "mailboxes": (rules.daily_mailboxes_limit - mailboxes_created).max(0),
            "emails": (rules.daily_emails_limit - emails_received).max(0),
        },
        "check_in": {
            "claimed": check_in.is_some(),
            "available": check_in.is_none(),
            "reward": rules.daily_check_in_reward,
            "reward_granted": check_in.as_ref().map(|value| value.reward_granted),
            "reward_balance_cap": rules.reward_balance_cap,
            "date": QuotaRepository::today().to_string(),
        }
    }))))
}

pub async fn check_in(
    State(state): State<AppState>,
    UserId(user_id): UserId,
) -> Result<Json<ApiResponse<serde_json::Value>>, ErrorResponse> {
    let rules = CreditRuleService::new(state.pool.clone())
        .get()
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    let result = CreditService::new(state.pool.clone())
        .check_in(
            &user_id,
            rules.daily_check_in_reward,
            rules.reward_balance_cap,
        )
        .await
        .map_err(|e| ErrorResponse::bad_request(e.to_string()))?;
    Ok(Json(ApiResponse::success(
        serde_json::to_value(result).map_err(|e| ErrorResponse::internal(e.to_string()))?,
    )))
}

#[derive(Deserialize)]
pub struct TxQuery {
    pub limit: Option<u64>,
    pub offset: Option<u64>,
}

pub async fn list_transactions(
    State(state): State<AppState>,
    UserId(user_id): UserId,
    Query(q): Query<TxQuery>,
) -> Result<Json<ApiResponse<Vec<serde_json::Value>>>, ErrorResponse> {
    let svc = CreditService::new(state.pool.clone());
    let limit = q.limit.unwrap_or(20).clamp(1, 100);
    let offset = q.offset.unwrap_or(0);
    let list = svc
        .list_transactions(&user_id, limit, offset)
        .await
        .map_err(|e| ErrorResponse::internal(e.to_string()))?;
    let value: Vec<_> = list
        .into_iter()
        .map(|t| {
            serde_json::json!({
                "id": t.id,
                "delta": t.delta,
                "balance_after": t.balance_after,
                "reason": t.reason,
                "related_mailbox_id": t.related_mailbox_id,
                "related_email_id": t.related_email_id,
                "description": t.description,
                "created_at": t.created_at.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(),
            })
        })
        .collect();
    Ok(Json(ApiResponse::success(value)))
}
