//! 认证与权限校验中间件
//!
//! 提供 JWT 令牌解析和 RBAC 权限校验功能：
//! - `auth_middleware`：从请求头提取 JWT，解析用户信息并注入请求扩展
//! - `check_permission`：校验用户是否拥有指定权限码
//! - `CurrentUser`：从请求扩展中提取当前登录用户信息（Axum Extractor）
//! - `generate_token`：签发 JWT token
//! - `hash_password` / `verify_password`：密码哈希与验证

use std::collections::HashSet;

use argon2::{
    password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
    Argon2,
};
use axum::body::Body;
use axum::extract::{FromRequestParts, State};
use axum::http::request::Parts;
use axum::http::Request;
use axum::middleware::Next;
use axum::response::{IntoResponse, Response};
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use sqlx::MySqlPool;

use crate::commands::AppState;
use crate::config::JWT_SECRET_DEFAULT;
use crate::error::AppError;

/// JWT 签名密钥环境变量名
const JWT_SECRET_ENV: &str = "JWT_SECRET";

/// JWT Claims 结构
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
    /// 用户ID（subject）
    pub sub: i64,
    /// 手机号
    pub phone: String,
    /// 过期时间（Unix 时间戳，秒）
    pub exp: u64,
    /// 签发时间（Unix 时间戳，秒）
    pub iat: u64,
}

/// 当前登录用户信息（从请求扩展中提取）
#[derive(Debug, Clone)]
pub struct CurrentUser {
    /// 用户ID
    pub user_id: i64,
    /// 手机号
    #[allow(dead_code)]
    pub phone: String,
    /// 用户拥有的权限码集合
    pub permissions: HashSet<String>,
    /// 角色等级：0普通 1企业管理员 2总管理员
    pub role_level: i32,
    /// 所属组织ID（总管理员为 None）
    pub organization_id: Option<i64>,
}

/// 认证中间件 — 从请求头提取 JWT，解析用户信息并注入请求扩展
///
/// 配合 `axum::middleware::from_fn_with_state(state, auth_middleware)` 使用
pub async fn auth_middleware(
    State(state): State<AppState>,
    req: Request<Body>,
    next: Next,
) -> Response {
    let pool = &state.mysql;
    // 从 Authorization 头提取 Bearer token
    let auth_header = req.headers().get("Authorization")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("")
        .to_string();

    let token = match auth_header.strip_prefix("Bearer ") {
        Some(t) => t.trim().to_string(),
        None => {
            return AppError::Unauthorized("缺少认证令牌".into()).into_response();
        }
    };

    // 获取 JWT 密钥
    let secret = std::env::var(JWT_SECRET_ENV)
        .unwrap_or_else(|_| JWT_SECRET_DEFAULT.to_string());

    // 解码并验证 JWT
    let mut validation = Validation::new(Algorithm::HS256);
    validation.validate_exp = true;

    let token_data = match decode::<Claims>(
        &token,
        &DecodingKey::from_secret(secret.as_bytes()),
        &validation,
    ) {
        Ok(data) => data,
        Err(_) => {
            return AppError::Unauthorized("令牌无效或已过期".into()).into_response();
        }
    };

    let claims = token_data.claims;

    // 查询用户权限码集合
    let permissions = match load_user_permissions(pool, claims.sub).await {
        Ok(perms) => perms,
        Err(e) => {
            tracing::error!("查询用户权限失败: {}", e);
            return AppError::Internal("权限查询失败".into()).into_response();
        }
    };

    // 查询用户角色等级和组织ID
    let (role_level, organization_id) = match load_user_role_and_org(pool, claims.sub).await {
        Ok(data) => data,
        Err(e) => {
            tracing::error!("查询用户角色失败: {}", e);
            return AppError::Internal("角色查询失败".into()).into_response();
        }
    };

    // 构建当前用户信息并注入请求扩展
    let current_user = CurrentUser {
        user_id: claims.sub,
        phone: claims.phone,
        permissions,
        role_level,
        organization_id,
    };

    // 拆分请求，插入扩展，重新组合
    let (mut parts, body) = req.into_parts();
    parts.extensions.insert(current_user);
    let req = Request::from_parts(parts, body);

    next.run(req).await
}

/// 从请求中提取当前用户（Axum Extractor 实现）
impl FromRequestParts<AppState> for CurrentUser {
    type Rejection = Response;

    async fn from_request_parts(
        parts: &mut Parts,
        _state: &AppState,
    ) -> Result<Self, Self::Rejection> {
        parts.extensions.get::<CurrentUser>().cloned().ok_or_else(|| {
            AppError::Unauthorized("未认证".into()).into_response()
        })
    }
}

/// 权限校验 — 检查用户是否拥有指定权限码
///
/// 总管理员（role_level >= 2）自动拥有全部权限
pub fn check_permission(user: &CurrentUser, code: &str) -> Result<(), AppError> {
    if user.role_level >= 2 {
        return Ok(());
    }
    if user.permissions.contains(code) {
        Ok(())
    } else {
        Err(AppError::Forbidden(format!("缺少权限: {}", code)))
    }
}

/// 加载用户权限码集合（公开接口，供路由处理器调用）
pub async fn load_user_permissions(pool: &MySqlPool, user_id: i64) -> Result<HashSet<String>, sqlx::Error> {
    let rows: Vec<(String,)> = sqlx::query_as(
        r#"
        SELECT DISTINCT p.code
        FROM permissions p
        INNER JOIN role_permissions rp ON rp.permission_id = p.id
        INNER JOIN user_roles ur ON ur.role_id = rp.role_id
        WHERE ur.user_id = ?
        "#,
    )
    .bind(user_id)
    .fetch_all(pool)
    .await?;

    Ok(rows.into_iter().map(|(code,)| code).collect())
}

/// 加载用户最高角色等级和组织ID
async fn load_user_role_and_org(pool: &MySqlPool, user_id: i64) -> Result<(i32, Option<i64>), sqlx::Error> {
    let result: Option<(i32, Option<i64>)> = sqlx::query_as(
        "SELECT COALESCE(MAX(role), 0), organization_id FROM users WHERE id = ?",
    )
    .bind(user_id)
    .fetch_optional(pool)
    .await?;

    Ok(result.unwrap_or((0, None)))
}

/// 生成 JWT token（7天有效期）
pub fn generate_token(user_id: i64, phone: &str) -> Result<String, AppError> {
    let secret = std::env::var(JWT_SECRET_ENV)
        .unwrap_or_else(|_| JWT_SECRET_DEFAULT.to_string());

    let now = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map_err(|e| AppError::Internal(format!("时间错误: {}", e)))?
        .as_secs();

    let claims = Claims {
        sub: user_id,
        phone: phone.to_string(),
        exp: now + 86400 * 7,
        iat: now,
    };

    encode(
        &Header::default(),
        &claims,
        &EncodingKey::from_secret(secret.as_bytes()),
    )
    .map_err(|e| AppError::Internal(format!("Token生成失败: {}", e)))
}

// ---------------------------------------------------------------------------
// 密码哈希
// ---------------------------------------------------------------------------

/// 使用 argon2 对密码进行哈希
pub fn hash_password(password: &str) -> Result<String, AppError> {
    let salt = SaltString::generate(&mut OsRng);
    let argon2 = Argon2::default();
    let hash = argon2
        .hash_password(password.as_bytes(), &salt)
        .map_err(|e| AppError::Internal(format!("密码哈希失败: {}", e)))?;
    Ok(hash.to_string())
}

/// 验证密码与 argon2 哈希是否匹配
pub fn verify_password(password: &str, hash: &str) -> Result<bool, AppError> {
    let parsed_hash = PasswordHash::new(hash)
        .map_err(|e| AppError::Internal(format!("密码哈希解析失败: {}", e)))?;
    let argon2 = Argon2::default();
    Ok(argon2
        .verify_password(password.as_bytes(), &parsed_hash)
        .is_ok())
}

// ---------------------------------------------------------------------------
// 组织数据过滤
// ---------------------------------------------------------------------------

/// 返回角色等级 >= 2 时「总管理员」看全部
/// 否则按 organization_id 过滤（子查询方式，通过 cabinet → project → org 链路）
pub fn org_condition(user: &CurrentUser, cabinet_table: &str) -> (String, Vec<String>) {
    if user.role_level >= 2 {
        return (String::new(), vec![]);
    }
    if let Some(org_id) = user.organization_id {
        (
            format!(
                " AND {}.cabinet_id IN (SELECT c.id FROM cabinets c JOIN projects p ON c.project_id = p.id WHERE p.organization_id = ?)",
                cabinet_table
            ),
            vec![org_id.to_string()],
        )
    } else {
        (" AND 1=0".to_string(), vec![])
    }
}

/// 操作日志的组织过滤（通过 users.organization_id 链路）
pub fn org_condition_for_logs(user: &CurrentUser, log_table: &str) -> (String, Vec<String>) {
    if user.role_level >= 2 {
        return (String::new(), vec![]);
    }
    if let Some(org_id) = user.organization_id {
        (
            format!(
                " AND {}.user_id IN (SELECT id FROM users WHERE organization_id = ?)",
                log_table
            ),
            vec![org_id.to_string()],
        )
    } else {
        (" AND 1=0".to_string(), vec![])
    }
}
