//! 充电记录 API — 列表查询 + 异步导出

use axum::extract::{Query, State};
use axum::Json;
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};

use crate::error::AppError;
use crate::middleware::auth::{self, CurrentUser};

/// 充电记录列表查询参数
#[derive(Debug, Deserialize)]
pub struct ChargeRecordQuery {
    pub cabinet_id: Option<i64>,
    pub cabin_board_id: Option<i64>,
    pub compartment_id: Option<i64>,
    pub start_time: Option<String>,
    pub end_time: Option<String>,
    #[serde(default = "default_page")]
    pub page: i64,
    #[serde(default = "default_page_size")]
    pub page_size: i64,
}

/// 充电记录导出查询参数
#[derive(Debug, Deserialize)]
pub struct ChargeRecordExportQuery {
    pub cabinet_id: Option<i64>,
    pub start_time: Option<String>,
    pub end_time: Option<String>,
}

/// 充电记录列表项
#[derive(Debug, Serialize)]
pub struct ChargeRecordItem {
    pub id: i64,
    pub cabinet_id: i64,
    pub cabinet_name: Option<String>,
    pub cabin_board_id: i64,
    pub board_id: i8,
    pub compartment_id: i64,
    pub channel_id: i8,
    pub start_time: Option<String>,
    pub end_time: Option<String>,
    pub energy: Option<f64>,
    pub max_temperature: Option<f64>,
    pub status: i8,
    pub status_text: String,
}

fn default_page() -> i64 { 1 }
fn default_page_size() -> i64 { 20 }

/// 状态码转中文描述
fn status_text(status: i8) -> &'static str {
    match status {
        0 => "正常",
        1 => "异常",
        2 => "手动停止",
        _ => "未知",
    }
}

/// 数据库行映射 — DECIMAL 列通过 CAST 转为 f64
#[derive(Debug, sqlx::FromRow)]
struct ChargeRecordRow {
    id: i64,
    cabinet_id: i64,
    cabinet_name: Option<String>,
    compartment_id: i64,
    cabin_board_id: Option<i64>,
    board_id: Option<i8>,
    channel_id: Option<i8>,
    start_time: Option<NaiveDateTime>,
    end_time: Option<NaiveDateTime>,
    energy: Option<f64>,
    max_temperature: Option<f64>,
    status: i8,
}

/// 构建 WHERE 条件子句及绑定值
fn build_where_clause(
    cabinet_id: Option<i64>,
    cabin_board_id: Option<i64>,
    compartment_id: Option<i64>,
    start_time: Option<&str>,
    end_time: Option<&str>,
) -> (String, Vec<String>) {
    let mut conditions = Vec::new();
    let mut bind_values = Vec::new();

    if let Some(id) = cabinet_id {
        conditions.push("cr.cabinet_id = ?");
        bind_values.push(id.to_string());
    }
    if let Some(id) = cabin_board_id {
        conditions.push("cb.id = ?");
        bind_values.push(id.to_string());
    }
    if let Some(id) = compartment_id {
        conditions.push("cr.compartment_id = ?");
        bind_values.push(id.to_string());
    }
    if let Some(t) = start_time {
        conditions.push("cr.start_time >= ?");
        bind_values.push(t.to_string());
    }
    if let Some(t) = end_time {
        conditions.push("cr.start_time <= ?");
        bind_values.push(t.to_string());
    }

    let clause = if conditions.is_empty() {
        String::new()
    } else {
        format!("WHERE {}", conditions.join(" AND "))
    };
    (clause, bind_values)
}

/// 查询充电记录行（复用 SQL 模板）
async fn fetch_charge_rows(
    pool: &sqlx::MySqlPool,
    where_clause: &str,
    bind_values: &[String],
    limit: Option<i64>,
    offset: Option<i64>,
) -> Result<Vec<ChargeRecordRow>, sqlx::Error> {
    let limit_clause = match (limit, offset) {
        (Some(l), Some(o)) => format!("LIMIT {} OFFSET {}", l, o),
        (Some(l), None) => format!("LIMIT {}", l),
        _ => String::new(),
    };

    let sql = format!(
        "SELECT cr.id, cr.cabinet_id, c.name AS cabinet_name, \
         cr.compartment_id, cb.id AS cabin_board_id, cb.board_id, comp.channel_id, \
         cr.start_time, cr.end_time, \
         CAST(cr.energy AS DOUBLE) AS energy, \
         CAST(cr.max_temperature AS DOUBLE) AS max_temperature, \
         cr.status \
         FROM charge_records cr \
         LEFT JOIN cabinets c ON cr.cabinet_id = c.id \
         LEFT JOIN compartments comp ON cr.compartment_id = comp.id \
         LEFT JOIN cabin_boards cb ON comp.cabin_board_id = cb.id \
         {} ORDER BY cr.start_time DESC {}",
        where_clause, limit_clause
    );

    let mut query = sqlx::query_as::<_, ChargeRecordRow>(&sql);
    for val in bind_values {
        query = query.bind(val);
    }
    query.fetch_all(pool).await
}

/// GET /api/charge-records — 充电记录分页列表
pub async fn list_charge_records(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Query(params): Query<ChargeRecordQuery>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "charge_record:view")?;
    let offset = (params.page - 1) * params.page_size;

    let (mut where_clause, mut bind_values) = build_where_clause(
        params.cabinet_id,
        params.cabin_board_id,
        params.compartment_id,
        params.start_time.as_deref(),
        params.end_time.as_deref(),
    );

    // 追加组织数据隔离
    let (org_cond, org_binds) = auth::org_condition(&user, "cr");
    if !org_cond.is_empty() {
        if where_clause.is_empty() {
            where_clause = format!("WHERE {}", org_cond.trim_start_matches(" AND "));
        } else {
            where_clause.push_str(&org_cond);
        }
        bind_values.extend(org_binds);
    }

    // 查询总数（需要与数据查询保持一致的 JOIN 链路）
    let count_sql = format!(
        "SELECT COUNT(*) FROM charge_records cr \
         LEFT JOIN cabinets c ON cr.cabinet_id = c.id \
         LEFT JOIN compartments comp ON cr.compartment_id = comp.id \
         LEFT JOIN cabin_boards cb ON comp.cabin_board_id = cb.id \
         {}",
        where_clause
    );
    let mut count_query = sqlx::query_scalar::<_, i64>(&count_sql);
    for val in &bind_values {
        count_query = count_query.bind(val);
    }
    let total = count_query.fetch_one(&state.mysql).await?;

    // 查询列表
    let rows = fetch_charge_rows(
        &state.mysql,
        &where_clause,
        &bind_values,
        Some(params.page_size),
        Some(offset),
    )
    .await?;

    let list: Vec<ChargeRecordItem> = rows
        .into_iter()
        .map(|r| ChargeRecordItem {
            id: r.id,
            cabinet_id: r.cabinet_id,
            cabinet_name: r.cabinet_name,
            cabin_board_id: r.cabin_board_id.unwrap_or(0),
            board_id: r.board_id.unwrap_or(0),
            compartment_id: r.compartment_id,
            channel_id: r.channel_id.unwrap_or(0),
            start_time: r.start_time.map(|t| t.to_string()),
            end_time: r.end_time.map(|t| t.to_string()),
            energy: r.energy,
            max_temperature: r.max_temperature,
            status: r.status,
            status_text: status_text(r.status).to_string(),
        })
        .collect();

    Ok(Json(json!({
        "code": 0,
        "data": {
            "list": list,
            "total": total,
            "page": params.page,
            "page_size": params.page_size,
        },
        "message": "ok"
    })))
}

/// GET /api/charge-records/export — 创建异步导出任务
pub async fn export_charge_records(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Query(params): Query<ChargeRecordExportQuery>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "charge_record:export")?;
    let user_id = user.user_id;
    let file_name = generate_export_filename(&params);

    // 序列化查询参数
    let query_json = serde_json::to_string(&json!({
        "cabinet_id": params.cabinet_id,
        "start_time": params.start_time,
        "end_time": params.end_time,
    }))
    .unwrap_or_default();

    // 插入下载任务（error_message 临时传递查询参数）
    let result = sqlx::query(
        "INSERT INTO download_tasks (user_id, task_type, status, file_name, error_message) \
         VALUES (?, ?, 0, ?, ?)"
    )
    .bind(user_id)
    .bind("charge_records")
    .bind(&file_name)
    .bind(&query_json)
    .execute(&state.mysql)
    .await?;

    let task_id = result.last_insert_id() as i64;

    // 启动后台异步生成 Excel
    let pool = state.mysql.clone();
    tokio::spawn(async move {
        if let Err(e) = generate_charge_records_excel(pool, task_id).await {
            tracing::error!("导出充电记录失败 task_id={}: {}", task_id, e);
        }
    });

    Ok(Json(json!({
        "code": 0,
        "data": { "task_id": task_id },
        "message": "导出任务已创建"
    })))
}

/// 生成导出文件名：充电记录_柜名_时间范围.xlsx
fn generate_export_filename(params: &ChargeRecordExportQuery) -> String {
    let start = params
        .start_time
        .as_ref()
        .map(|s| s.replace(['-', ':', ' '], ""))
        .unwrap_or_default();
    let end = params
        .end_time
        .as_ref()
        .map(|s| s.replace(['-', ':', ' '], ""))
        .unwrap_or_default();
    let cabinet_part = params
        .cabinet_id
        .map(|id| format!("柜{}", id))
        .unwrap_or_default();
    format!("充电记录_{}_{}-{}.xlsx", cabinet_part, start, end)
}

/// 后台异步生成 Excel 文件
async fn generate_charge_records_excel(
    pool: sqlx::MySqlPool,
    task_id: i64,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // 更新状态为处理中
    sqlx::query("UPDATE download_tasks SET status = 1 WHERE id = ?")
        .bind(task_id)
        .execute(&pool)
        .await?;

    // 获取查询参数
    let row: Option<(String, String)> = sqlx::query_as(
        "SELECT file_name, error_message FROM download_tasks WHERE id = ?"
    )
    .bind(task_id)
    .fetch_optional(&pool)
    .await?;

    let (file_name, query_json) = row.ok_or("任务不存在")?;

    let params: Value = serde_json::from_str(&query_json).unwrap_or(json!({}));

    let (where_clause, bind_values) = build_where_clause(
        params.get("cabinet_id").and_then(|v| v.as_i64()),
        None,
        None,
        params.get("start_time").and_then(|v| v.as_str()),
        params.get("end_time").and_then(|v| v.as_str()),
    );

    let rows = fetch_charge_rows(&pool, &where_clause, &bind_values, None, None).await?;

    // 更新进度 50%
    sqlx::query("UPDATE download_tasks SET progress = 50 WHERE id = ?")
        .bind(task_id)
        .execute(&pool)
        .await?;

    // 生成 Excel
    let mut workbook = rust_xlsxwriter::Workbook::new();
    let sheet = workbook.add_worksheet();
    sheet.set_name("充电记录")?;

    let headers = [
        "ID", "柜子", "仓板", "通道", "开始时间", "结束时间",
        "充电量(kWh)", "最高温度(°C)", "状态",
    ];
    for (col, header) in headers.iter().enumerate() {
        sheet.write_string(0, col as u16, *header)?;
    }

    for (row_idx, record) in rows.iter().enumerate() {
        let row = (row_idx + 1) as u32;
        sheet.write_number(row, 0, record.id as f64)?;
        sheet.write_string(row, 1, record.cabinet_name.as_deref().unwrap_or("-"))?;
        sheet.write_number(row, 2, record.board_id.unwrap_or(0) as f64)?;
        sheet.write_number(row, 3, record.channel_id.unwrap_or(0) as f64)?;
        sheet.write_string(
            row, 4,
            record.start_time.map(|t| t.to_string()).as_deref().unwrap_or("-"),
        )?;
        sheet.write_string(
            row, 5,
            record.end_time.map(|t| t.to_string()).as_deref().unwrap_or("-"),
        )?;
        sheet.write_number(row, 6, record.energy.unwrap_or(0.0))?;
        sheet.write_number(row, 7, record.max_temperature.unwrap_or(0.0))?;
        sheet.write_string(row, 8, status_text(record.status))?;
    }

    // 保存文件
    let file_dir = "./exports";
    tokio::fs::create_dir_all(file_dir).await?;
    let file_path = format!("{}/{}", file_dir, file_name);
    workbook.save(&file_path)?;

    // 更新任务状态为完成
    sqlx::query(
        "UPDATE download_tasks SET status = 2, progress = 100, file_path = ?, \
         error_message = NULL, completed_at = NOW() WHERE id = ?"
    )
    .bind(&file_path)
    .bind(task_id)
    .execute(&pool)
    .await?;

    tracing::info!("充电记录导出完成 task_id={} file={}", task_id, file_path);
    Ok(())
}
