//! 组织 CRUD API + 共享类型
//!
//! 提供组织的增删改查，以及跨模块共享的请求体/行结构体。
//! 项目、设备、树状结构分别拆分到 projects / cabinets / cabinet_tree 模块。

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

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

// ---------------------------------------------------------------------------
// 共享请求体
// ---------------------------------------------------------------------------

/// 通用名称请求体（组织/项目共用）
#[derive(Debug, Deserialize)]
pub struct NameBody {
    pub name: String,
}

/// 批量创建柜子请求体
#[derive(Debug, Deserialize)]
pub struct CreateCabinetBody {
    pub imeis: Vec<String>,
    pub project_id: i64,
}

/// 更新柜子请求体
#[derive(Debug, Deserialize)]
pub struct UpdateCabinetBody {
    pub project_id: Option<i64>,
    pub name: Option<String>,
}

// ---------------------------------------------------------------------------
// 共享行结构体
// ---------------------------------------------------------------------------

/// 组织表行
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct OrganizationRow {
    pub id: i64,
    pub name: String,
}

/// 项目表行
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct ProjectRow {
    pub id: i64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub organization_id: Option<i64>,
    pub name: String,
}

/// 柜子表行
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
pub struct CabinetRow {
    pub id: i64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub project_id: Option<i64>,
    pub abstract_id: String,
    pub imei: String,
    pub name: Option<String>,
    pub status: i8,
}

/// 树节点
#[derive(Debug, Serialize)]
pub struct TreeNode {
    pub id: i64,
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub children: Option<Vec<TreeNode>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub abstract_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub imei: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<i8>,
}

/// 仓控板行
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct CabinBoardRow {
    pub id: i64,
    pub cabinet_id: i64,
    pub board_id: i8,
    pub status: i8,
}

/// 仓体行
#[derive(Debug, Serialize, sqlx::FromRow)]
pub struct CompartmentRow {
    pub id: i64,
    pub cabin_board_id: i64,
    pub channel_id: i8,
    pub status: i8,
}

/// 仓控板 + 仓体详情
#[derive(Debug, Serialize)]
pub struct CabinBoardWithCompartments {
    pub id: i64,
    pub board_id: i8,
    pub status: i8,
    pub compartments: Vec<CompartmentRow>,
}

/// 柜子详情（含仓控板 + 仓体）
#[derive(Debug, Serialize)]
pub struct CabinetDetail {
    pub id: i64,
    pub project_id: Option<i64>,
    pub abstract_id: String,
    pub imei: String,
    pub name: Option<String>,
    pub status: i8,
    pub cabin_boards: Vec<CabinBoardWithCompartments>,
}

// ---------------------------------------------------------------------------
// 辅助函数
// ---------------------------------------------------------------------------

/// 生成平台抽象 ID: `10-` + IMEI 的 SHA256 前 4 字节的十六进制
pub fn generate_abstract_id(imei: &str) -> String {
    use sha2::Digest;
    let hash = sha2::Sha256::digest(imei.as_bytes());
    let hex_str = hex::encode(&hash[..4]);
    format!("10-{hex_str}")
}

/// 生成 8 位随机字母数字安全码
pub fn generate_auth_str() -> String {
    use rand::Rng;
    const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    let mut rng = rand::thread_rng();
    (0..8).map(|_| CHARSET[rng.gen_range(0..CHARSET.len())] as char).collect()
}

// ---------------------------------------------------------------------------
// 组织 CRUD
// ---------------------------------------------------------------------------

/// GET /api/organizations — 组织列表
pub async fn list_organizations(
    user: CurrentUser,
    State(state): State<AppState>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "org:view")?;

    let db = &state.mysql;
    let rows = sqlx::query_as::<_, OrganizationRow>(
        r#"SELECT id, name FROM organizations ORDER BY id"#,
    )
    .fetch_all(db)
    .await?;

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

/// POST /api/organizations — 创建组织
pub async fn create_organization(
    user: CurrentUser,
    State(state): State<AppState>,
    Json(body): Json<NameBody>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "org:create")?;

    let db = &state.mysql;
    let name = body.name.trim();
    if name.is_empty() {
        return Err(AppError::BadRequest("组织名称不能为空".into()));
    }

    let result = sqlx::query(r#"INSERT INTO organizations (name) VALUES (?)"#)
        .bind(name)
        .execute(db)
        .await?;

    let id = result.last_insert_id() as i64;
    tracing::info!("创建组织 id={} name={}", id, name);

    Ok(Json(json!({ "id": id, "name": name })))
}

/// PUT /api/organizations/:id — 更新组织
pub async fn update_organization(
    user: CurrentUser,
    State(state): State<AppState>,
    Path(id): Path<i64>,
    Json(body): Json<NameBody>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "org:edit")?;

    let db = &state.mysql;
    let name = body.name.trim().to_string();
    if name.is_empty() {
        return Err(AppError::BadRequest("组织名称不能为空".into()));
    }

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

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

    tracing::info!("更新组织 id={} name={}", id, name);
    Ok(Json(json!({ "id": id, "name": name })))
}

/// DELETE /api/organizations/:id — 删除组织
pub async fn delete_organization(
    user: CurrentUser,
    State(state): State<AppState>,
    Path(id): Path<i64>,
) -> Result<Json<Value>, AppError> {
    auth::check_permission(&user, "org:delete")?;

    let db = &state.mysql;
    let count: (i64,) = sqlx::query_as(
        r#"SELECT COUNT(*) FROM projects WHERE organization_id = ?"#,
    )
    .bind(id)
    .fetch_one(db)
    .await?;

    if count.0 > 0 {
        return Err(AppError::BadRequest(
            format!("组织 {} 下仍有项目，无法删除", id),
        ));
    }

    let rows = sqlx::query(r#"DELETE FROM organizations WHERE id = ?"#)
        .bind(id)
        .execute(db)
        .await?
        .rows_affected();

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

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