use chrono::{Utc, Duration};
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode};
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};

use crate::error::AppError;
use crate::models::{Claims, TempTokenClaims};
use actix_web::{
    Error, HttpMessage, body::MessageBody, dev::ServiceRequest, dev::ServiceResponse,
    middleware::Next,
};

// 常量
const ACCESS_TOKEN_EXPIRE_HOURS: i64 = 24;
const REFRESH_TOKEN_EXPIRE_DAYS: i64 = 7;

pub async fn jwt_middleware<B: MessageBody>(
    req: ServiceRequest,
    next: Next<B>,
) -> Result<ServiceResponse<B>, Error> {
    let auth_header = req
        .headers()
        .get("Authorization")
        .ok_or_else(|| AppError::Unauthorized("请先登录".to_string()))?
        .to_str()
        .map_err(|_| AppError::Unauthorized("Authorization格式无效".to_string()))?;

    let token = auth_header.strip_prefix("Bearer ").ok_or_else(|| {
        AppError::Unauthorized("请使用有效的登录凭证".to_string())
    })?;

    let jwt_secret = std::env::var("JWT_SECRET")
        .map_err(|_| AppError::Internal("服务器配置错误".to_string()))?;

    let claims = verify_token(token, &jwt_secret)
        .map_err(|e| AppError::Unauthorized(format!("登录已过期，请重新登录: {}", e)))?;

    req.extensions_mut().insert(claims);
    next.call(req).await
}

// 生成 access_token
pub fn generate_token(
    user_id: i32,
    openid: &str,
    user_type: i32,
    secret: &str,
) -> Result<String, String> {
    let now = Utc::now();
    let exp = (now + Duration::hours(ACCESS_TOKEN_EXPIRE_HOURS)).timestamp();
    let iat = now.timestamp();

    let claims = Claims {
        exp,
        iat,
        user_id,
        openid: openid.to_string(),
        user_type,
    };

    encode(
        &Header::new(Algorithm::HS256),
        &claims,
        &EncodingKey::from_secret(secret.as_bytes()),
    )
    .map_err(|e| format!("生成JWT失败: {}", e))
}

// 生成 refresh_token（简单 base64 编码的随机字符串）
pub fn generate_refresh_token(user_id: i32, secret: &str) -> Result<String, String> {
    let now = Utc::now();
    let exp = (now + Duration::days(REFRESH_TOKEN_EXPIRE_DAYS)).timestamp();

    let payload = format!("{}:{}:{}", user_id, exp, secret);
    let token = BASE64.encode(payload.as_bytes());

    Ok(token)
}

// 解析 refresh_token，返回 (user_id, expires_at)
pub fn verify_refresh_token(token: &str, secret: &str) -> Result<(i32, i64), String> {
    let decoded = BASE64.decode(token)
        .map_err(|e| format!("Refresh token 格式错误: {}", e))?;

    let payload = String::from_utf8(decoded)
        .map_err(|_| "Refresh token 解析失败".to_string())?;

    let parts: Vec<&str> = payload.split(':').collect();
    if parts.len() != 3 {
        return Err("Refresh token 结构错误".to_string());
    }

    let user_id: i32 = parts[0].parse()
        .map_err(|_| "Refresh token user_id 解析失败".to_string())?;
    let exp: i64 = parts[1].parse()
        .map_err(|_| "Refresh token exp 解析失败".to_string())?;

    let now = Utc::now().timestamp();
    if now > exp {
        return Err("Refresh token 已过期".to_string());
    }

    Ok((user_id, exp))
}

// 验证并解析JWT的函数
pub fn verify_token(token: &str, secret: &str) -> Result<Claims, String> {
    // 验证配置（指定算法，默认会检查exp等字段）
    let validation = Validation::new(Algorithm::HS256);

    // 解析token
    let decoded = decode::<Claims>(
        token,
        &DecodingKey::from_secret(secret.as_bytes()), // 与签发时相同的密钥
        &validation,
    )
    .map_err(|e| format!("JWT验证失败: {}", e))?;

    Ok(decoded.claims)
}

// 生成临时token（包含openid和resource_id，短期有效）
pub fn generate_temp_token(
    openid: &str,
    resource_id: i32,
    secret: &str,
    expire_minutes: i64, // 有效期（分钟）
) -> Result<String, String> {
    let now = Utc::now();
    let exp = (now + chrono::Duration::minutes(expire_minutes)).timestamp();
    let iat = now.timestamp();

    let claims = TempTokenClaims {
        exp,
        iat,
        openid: openid.to_string(),
        resource_id,
    };

    encode(
        &Header::new(Algorithm::HS256),
        &claims,
        &EncodingKey::from_secret(secret.as_bytes()),
    )
    .map_err(|e| format!("生成临时token失败: {}", e))
}

// 验证临时token并返回载荷
pub fn verify_temp_token(token: &str, secret: &str) -> Result<TempTokenClaims, String> {
    let validation = Validation::new(Algorithm::HS256);
    let decoded = decode::<TempTokenClaims>(
        token,
        &DecodingKey::from_secret(secret.as_bytes()),
        &validation,
    )
    .map_err(|e| format!("临时token验证失败: {}", e))?;

    Ok(decoded.claims)
}
