//! 设备管理 API — CRUD + 详情 + 级联下拉
//!
//! 提供柜子的增删改查、详情查询、安全码重新生成，
//! 以及柜子/仓板/仓体的级联下拉选项。

use axum::extract::{Path, Query, State};
use axum::Json;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use sqlx::MySqlPool;

use super::organizations::{
    generate_abstract_id, generate_auth_str,
    CabinetDetail, CabinetRow, CabinBoardRow, CabinBoardWithCompartments,
    CompartmentRow, CreateCabinetBody, UpdateCabinetBody,
};
use crate::error::AppError;
use crate::middleware::auth::{self, CurrentUser};

// ---------------------------------------------------------------------------
// 项目访问权限验证
// ---------------------------------------------------------------------------

/// 验证用户是否有权访问指定项目
///
/// 总管理员（role_level >= 2）可访问所有项目；
/// 企业管理员只能访问本组织下的项目。
async fn check_project_access(
    user: &CurrentUser,
    pool: &MySqlPool,
    project_id: i64,
) -> Result<(), AppError> {
    // 总管理员可访问所有项目
    if user.role_level >= 2 {
        return Ok(());
    }

    if let Some(org_id) = user.organization_id {
        // 验证项目是否属于用户的组织
        let project: Option<(i64,)> = sqlx::query_as(
            "SELECT id FROM projects WHERE id = ? AND organization_id = ?",
        )
        .bind(project_id)
        .bind(org_id)
        .fetch_optional(pool)
        .await?;

        if project.is_none() {
            return Err(AppError::Forbidden("无权访问该项目".into()));
        }
    } else {
        return Err(AppError::Forbidden("无组织关联".into()));
    }

    Ok(())
}

// ---------------------------------------------------------------------------
// 设备 CRUD
// ---------------------------------------------------------------------------

/// GET /api/projects/:project_id/cabinets — 按项目查询柜子列表
pub async fn list_cabinets(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Path(project_id): Path<i64>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "device:view")?;
    check_project_access(&user, &state.mysql, project_id).await?;

    let db = &state.mysql;
    let rows = sqlx::query_as::<_, CabinetRow>(
        r#"SELECT id, project_id, abstract_id, imei, name, status
           FROM cabinets WHERE project_id = ? ORDER BY id"#,
    )
    .bind(project_id)
    .fetch_all(db)
    .await?;

    Ok(Json(json!(rows)))
}

/// POST /api/cabinets — 批量添加柜子
///
/// 每个 IMEI 创建一个柜子，同时生成默认仓控板和 6 个仓体。
pub async fn create_cabinet(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Json(body): Json<CreateCabinetBody>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "device:create")?;

    let db = &state.mysql;
    if body.imeis.is_empty() {
        return Err(AppError::BadRequest("IMEI 列表不能为空".into()));
    }

    let mut unique_imeis: Vec<String> = Vec::new();
    for imei in &body.imeis {
        let trimmed = imei.trim().to_string();
        if trimmed.len() != 15 {
            return Err(AppError::BadRequest(
                format!("IMEI 必须为15位数字: {}", trimmed),
            ));
        }
        if !unique_imeis.contains(&trimmed) {
            unique_imeis.push(trimmed);
        }
    }

    let mut created_ids: Vec<i64> = Vec::new();

    for imei in &unique_imeis {
        let abstract_id = generate_abstract_id(imei);
        let auth_str = generate_auth_str();

        let mut tx = db.begin().await.map_err(AppError::Database)?;

        let result = sqlx::query(
            r#"INSERT INTO cabinets (project_id, abstract_id, imei, auth_str)
               VALUES (?, ?, ?, ?)"#,
        )
        .bind(body.project_id)
        .bind(&abstract_id)
        .bind(imei)
        .bind(&auth_str)
        .execute(&mut *tx)
        .await?;

        let cabinet_id = result.last_insert_id() as i64;

        let board_result = sqlx::query(
            r#"INSERT INTO cabin_boards (cabinet_id, board_id) VALUES (?, 1)"#,
        )
        .bind(cabinet_id)
        .execute(&mut *tx)
        .await?;

        let board_pk = board_result.last_insert_id() as i64;

        for ch in 1..=6 {
            sqlx::query(
                r#"INSERT INTO compartments (cabin_board_id, channel_id) VALUES (?, ?)"#,
            )
            .bind(board_pk)
            .bind(ch)
            .execute(&mut *tx)
            .await?;
        }

        tx.commit().await.map_err(AppError::Database)?;
        created_ids.push(cabinet_id);
        tracing::info!("创建柜子 id={} abstract_id={} imei={}", cabinet_id, abstract_id, imei);
    }

    Ok(Json(json!({ "ids": created_ids })))
}

/// PUT /api/cabinets/:id — 更新柜子（划拨项目等）
pub async fn update_cabinet(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Path(id): Path<i64>,
    Json(body): Json<UpdateCabinetBody>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "device:edit")?;

    let db = &state.mysql;
    let rows = sqlx::query(
        r#"UPDATE cabinets SET
            project_id = COALESCE(?, project_id),
            name = COALESCE(?, name)
         WHERE id = ?"#,
    )
    .bind(body.project_id)
    .bind(&body.name)
    .bind(id)
    .execute(db)
    .await?
    .rows_affected();

    if rows == 0 {
        return Err(AppError::NotFound(format!("柜子 {} 不存在", id)));
    }

    tracing::info!("更新柜子 id={}", id);
    Ok(Json(json!({})))
}

/// DELETE /api/cabinets/:id — 删除柜子（级联删除仓控板和仓体）
pub async fn delete_cabinet(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Path(id): Path<i64>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "device:delete")?;

    let db = &state.mysql;
    let mut tx = db.begin().await.map_err(AppError::Database)?;

    sqlx::query(
        r#"DELETE compartments FROM compartments
           INNER JOIN cabin_boards ON compartments.cabin_board_id = cabin_boards.id
           WHERE cabin_boards.cabinet_id = ?"#,
    )
    .bind(id)
    .execute(&mut *tx)
    .await?;

    sqlx::query(r#"DELETE FROM cabin_boards WHERE cabinet_id = ?"#)
        .bind(id)
        .execute(&mut *tx)
        .await?;

    let rows = sqlx::query(r#"DELETE FROM cabinets WHERE id = ?"#)
        .bind(id)
        .execute(&mut *tx)
        .await?
        .rows_affected();

    tx.commit().await.map_err(AppError::Database)?;

    if rows == 0 {
        return Err(AppError::NotFound(format!("柜子 {} 不存在", id)));
    }

    tracing::info!("删除柜子 id={}", id);
    Ok(Json(json!({})))
}

/// POST /api/cabinets/:id/regenerate-auth — 重新生成安全码
pub async fn regenerate_auth(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Path(id): Path<i64>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "device:edit")?;

    let db = &state.mysql;
    let auth_str = generate_auth_str();

    let rows = sqlx::query(r#"UPDATE cabinets SET auth_str = ? WHERE id = ?"#)
        .bind(&auth_str)
        .bind(id)
        .execute(db)
        .await?
        .rows_affected();

    if rows == 0 {
        return Err(AppError::NotFound(format!("柜子 {} 不存在", id)));
    }

    tracing::info!("重新生成 auth_str id={}", id);
    Ok(Json(json!({ "auth_str": auth_str })))
}

// ---------------------------------------------------------------------------
// 柜子详情（含仓控板 + 仓体）
// ---------------------------------------------------------------------------

/// GET /api/cabinets/:id — 柜子详情，包含仓控板和仓体
pub async fn get_cabinet_detail(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Path(id): Path<i64>,
) -> Result<Json<CabinetDetail>, AppError> {
    auth::check_permission(&user, "device:view")?;

    let db = &state.mysql;
    let cabinet = sqlx::query_as::<_, CabinetRow>(
        r#"SELECT id, project_id, abstract_id, imei, name, status
           FROM cabinets WHERE id = ?"#,
    )
    .bind(id)
    .fetch_optional(db)
    .await?
    .ok_or_else(|| AppError::NotFound(format!("柜子 {} 不存在", id)))?;

    // 验证用户是否有权访问该柜子所属的项目
    if let Some(pid) = cabinet.project_id {
        check_project_access(&user, db, pid).await?;
    }

    let boards: Vec<CabinBoardRow> = sqlx::query_as(
        r#"SELECT id, cabinet_id, board_id, status
           FROM cabin_boards WHERE cabinet_id = ? ORDER BY board_id"#,
    )
    .bind(id)
    .fetch_all(db)
    .await?;

    let mut cabin_boards = Vec::new();
    for board in boards {
        let compartments: Vec<CompartmentRow> = sqlx::query_as(
            r#"SELECT id, cabin_board_id, channel_id, status
               FROM compartments WHERE cabin_board_id = ? ORDER BY channel_id"#,
        )
        .bind(board.id)
        .fetch_all(db)
        .await?;

        cabin_boards.push(CabinBoardWithCompartments {
            id: board.id,
            board_id: board.board_id,
            status: board.status,
            compartments,
        });
    }

    Ok(Json(CabinetDetail {
        id: cabinet.id,
        project_id: cabinet.project_id,
        abstract_id: cabinet.abstract_id,
        imei: cabinet.imei,
        name: cabinet.name,
        status: cabinet.status,
        cabin_boards,
    }))
}

// ---------------------------------------------------------------------------
// 级联下拉选项
// ---------------------------------------------------------------------------

/// 柜子下拉查询参数
#[derive(Debug, Deserialize)]
pub struct CabinetListQuery {
    pub project_id: Option<i64>,
}

/// 仓板下拉查询参数
#[derive(Debug, Deserialize)]
pub struct CabinBoardListQuery {
    pub cabinet_id: i64,
}

/// 仓体下拉查询参数
#[derive(Debug, Deserialize)]
pub struct CompartmentListQuery {
    pub cabin_board_id: i64,
}

/// 柜子下拉选项
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct CabinetOption {
    pub id: i64,
    pub name: Option<String>,
    pub abstract_id: String,
}

/// 仓板下拉选项
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct CabinBoardOption {
    pub id: i64,
    pub board_id: i8,
}

/// 仓体下拉选项
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct CompartmentOption {
    pub id: i64,
    pub channel_id: i8,
}

/// GET /api/cabinets/options — 柜子下拉列表
pub async fn list_cabinet_options(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Query(params): Query<CabinetListQuery>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "device:view")?;

    let rows = if let Some(pid) = params.project_id {
        sqlx::query_as::<_, CabinetOption>(
            "SELECT id, name, abstract_id FROM cabinets WHERE project_id = ? ORDER BY id"
        )
        .bind(pid)
        .fetch_all(&state.mysql)
        .await?
    } else {
        sqlx::query_as::<_, CabinetOption>(
            "SELECT id, name, abstract_id FROM cabinets ORDER BY id"
        )
        .fetch_all(&state.mysql)
        .await?
    };

    Ok(Json(json!({ "code": 0, "data": rows, "message": "ok" })))
}

/// GET /api/cabin-boards/options — 仓板下拉列表
pub async fn list_cabin_board_options(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Query(params): Query<CabinBoardListQuery>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "device:view")?;

    let rows = sqlx::query_as::<_, CabinBoardOption>(
        "SELECT id, board_id FROM cabin_boards WHERE cabinet_id = ? ORDER BY board_id"
    )
    .bind(params.cabinet_id)
    .fetch_all(&state.mysql)
    .await?;

    Ok(Json(json!({ "code": 0, "data": rows, "message": "ok" })))
}

/// GET /api/compartments/options — 仓体下拉列表
pub async fn list_compartment_options(
    user: CurrentUser,
    State(state): State<super::AppState>,
    Query(params): Query<CompartmentListQuery>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "device:view")?;

    let rows = sqlx::query_as::<_, CompartmentOption>(
        "SELECT id, channel_id FROM compartments WHERE cabin_board_id = ? ORDER BY channel_id"
    )
    .bind(params.cabin_board_id)
    .fetch_all(&state.mysql)
    .await?;

    Ok(Json(json!({ "code": 0, "data": rows, "message": "ok" })))
}
