use axum::Json;
use axum::extract::State;
use axum::http::{HeaderMap, StatusCode};
use serde::{Deserialize, Serialize};

use super::auth::AppState;
use super::billing_edge::require_cloud_sync;
use super::helpers::internal_error;

#[derive(Deserialize)]
pub(super) struct CommandEntry {
    pub command: String,
    #[serde(default)]
    pub source: String,
    #[serde(default)]
    pub count: i64,
    #[serde(default)]
    pub input_tokens: i64,
    #[serde(default)]
    pub output_tokens: i64,
    #[serde(default)]
    pub tokens_saved: i64,
}

#[derive(Deserialize)]
pub(super) struct CommandsEnvelope {
    pub commands: Vec<CommandEntry>,
}

#[derive(Serialize)]
pub(super) struct CommandRow {
    pub command: String,
    pub source: String,
    pub count: i64,
    pub input_tokens: i64,
    pub output_tokens: i64,
    pub tokens_saved: i64,
}

pub(super) async fn post_commands(
    State(state): State<AppState>,
    headers: HeaderMap,
    Json(body): Json<CommandsEnvelope>,
) -> Result<Json<serde_json::Value>, (StatusCode, String)> {
    let (user_id, _) = require_cloud_sync(&state, &headers).await?;
    super::devices::track(&state, user_id, &headers, "commands");
    let client = state.pool.get().await.map_err(internal_error)?;

    for cmd in &body.commands {
        let command = cmd.command.trim();
        if command.is_empty() {
            continue;
        }
        let source = if cmd.source.is_empty() {
            "unknown"
        } else {
            &cmd.source
        };
        client
            .execute(
                r"INSERT INTO command_stats (user_id, command, source, count, input_tokens, output_tokens, tokens_saved)
                   VALUES ($1, $2, $3, $4, $5, $6, $7)
                   ON CONFLICT (user_id, command) DO UPDATE SET
                     source = EXCLUDED.source,
                     count = EXCLUDED.count,
                     input_tokens = EXCLUDED.input_tokens,
                     output_tokens = EXCLUDED.output_tokens,
                     tokens_saved = EXCLUDED.tokens_saved,
                     updated_at = NOW()",
                &[
                    &user_id,
                    &command,
                    &source,
                    &cmd.count,
                    &cmd.input_tokens,
                    &cmd.output_tokens,
                    &cmd.tokens_saved,
                ],
            )
            .await
            .map_err(internal_error)?;
    }

    Ok(Json(serde_json::json!({"synced": body.commands.len()})))
}

pub(super) async fn get_commands(
    State(state): State<AppState>,
    headers: HeaderMap,
) -> Result<Json<Vec<CommandRow>>, (StatusCode, String)> {
    let (user_id, _) = require_cloud_sync(&state, &headers).await?;
    let client = state.pool.get().await.map_err(internal_error)?;

    let rows = client
        .query(
            r"SELECT command, source, count, input_tokens, output_tokens, tokens_saved
               FROM command_stats WHERE user_id = $1
               ORDER BY tokens_saved DESC LIMIT 200",
            &[&user_id],
        )
        .await
        .map_err(internal_error)?;

    let result: Vec<CommandRow> = rows
        .iter()
        .map(|r| CommandRow {
            command: r.get(0),
            source: r.get(1),
            count: r.get(2),
            input_tokens: r.get(3),
            output_tokens: r.get(4),
            tokens_saved: r.get(5),
        })
        .collect();

    Ok(Json(result))
}
