use actix_multipart::Multipart;
use actix_web::{HttpResponse, Responder, Scope, get, post, put, web};
use futures_util::StreamExt;
use sea_orm::{ActiveModelTrait, EntityTrait, Set};
use serde::{Deserialize, Serialize};
use std::io::Write;
use uuid::Uuid;
use bcrypt;

use crate::app_data::AppData;
use crate::entity::user;
use crate::error::ApiError;
use crate::middleware::auth::AuthUser;

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MeResponse {
    pub id: Uuid,
    pub username: String,
    pub avatar: Option<String>,
    pub timezone: String,
    pub bark_url: Option<String>,
    pub inapp_enabled: bool,
    pub bark_enabled: bool,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateSettingsRequest {
    pub avatar: Option<String>,
    pub timezone: Option<String>,
    pub bark_url: Option<String>,
    pub inapp_enabled: Option<bool>,
    pub bark_enabled: Option<bool>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChangePasswordRequest {
    pub current_password: String,
    pub new_password: String,
}

#[get("")]
async fn get_me(
    app_data: web::Data<AppData>,
    auth: AuthUser,
) -> Result<impl Responder, ApiError> {
    let user = user::Entity::find_by_id(auth.user_id)
        .one(&app_data.db)
        .await?
        .ok_or_else(|| ApiError::NotFound("User not found".to_string()))?;

    Ok(HttpResponse::Ok().json(MeResponse {
        id: user.id,
        username: user.username,
        avatar: user.avatar,
        timezone: user.timezone,
        bark_url: user.bark_url,
        inapp_enabled: user.inapp_enabled,
        bark_enabled: user.bark_enabled,
    }))
}

#[put("/settings")]
async fn update_settings(
    app_data: web::Data<AppData>,
    auth: AuthUser,
    body: web::Json<UpdateSettingsRequest>,
) -> Result<impl Responder, ApiError> {
    let user = user::Entity::find_by_id(auth.user_id)
        .one(&app_data.db)
        .await?
        .ok_or_else(|| ApiError::NotFound("User not found".to_string()))?;

    let mut active: user::ActiveModel = user.into();

    if let Some(avatar) = &body.avatar {
        active.avatar = Set(Some(avatar.clone()));
    }
    if let Some(timezone) = &body.timezone {
        active.timezone = Set(timezone.clone());
    }
    if let Some(bark_url) = &body.bark_url {
        active.bark_url = Set(Some(bark_url.clone()));
    }
    if let Some(inapp_enabled) = body.inapp_enabled {
        active.inapp_enabled = Set(inapp_enabled);
    }
    if let Some(bark_enabled) = body.bark_enabled {
        active.bark_enabled = Set(bark_enabled);
    }
    active.updated_at = Set(chrono::Utc::now().fixed_offset());

    let updated = active.update(&app_data.db).await?;

    Ok(HttpResponse::Ok().json(MeResponse {
        id: updated.id,
        username: updated.username,
        avatar: updated.avatar,
        timezone: updated.timezone,
        bark_url: updated.bark_url,
        inapp_enabled: updated.inapp_enabled,
        bark_enabled: updated.bark_enabled,
    }))
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UploadAvatarResponse {
    pub avatar_url: String,
}

/// 上传用户头像
/// 支持的格式: jpg, jpeg, png, gif, webp
/// 最大文件大小: 5MB
#[post("/avatar")]
async fn upload_avatar(
    app_data: web::Data<AppData>,
    auth: AuthUser,
    mut payload: Multipart,
) -> Result<impl Responder, ApiError> {
    const MAX_FILE_SIZE: usize = 5 * 1024 * 1024; // 5MB
    const ALLOWED_EXTENSIONS: &[&str] = &["jpg", "jpeg", "png", "gif", "webp"];

    let mut file_data: Option<(Vec<u8>, String)> = None;

    // 解析 multipart 表单数据
    while let Some(item) = payload.next().await {
        let mut field = item.map_err(|e| ApiError::BadRequest(format!("Multipart error: {}", e)))?;

        let content_disposition = field
            .content_disposition()
            .ok_or_else(|| ApiError::BadRequest("Missing content disposition".to_string()))?;
        let field_name = content_disposition.get_name().unwrap_or("");

        if field_name != "avatar" {
            continue;
        }

        // 获取文件名和扩展名
        let filename = content_disposition
            .get_filename()
            .ok_or_else(|| ApiError::BadRequest("Missing filename".to_string()))?;

        let extension: String = filename
            .rsplit('.')
            .next()
            .map(|s| s.to_lowercase())
            .ok_or_else(|| ApiError::BadRequest("Invalid filename".to_string()))?;

        if !ALLOWED_EXTENSIONS.contains(&extension.as_str()) {
            return Err(ApiError::BadRequest(format!(
                "Unsupported file format. Allowed: {}",
                ALLOWED_EXTENSIONS.join(", ")
            )));
        }

        // 读取文件内容
        let mut data = Vec::new();
        while let Some(chunk) = field.next().await {
            let chunk =
                chunk.map_err(|e| ApiError::BadRequest(format!("Error reading chunk: {}", e)))?;

            if data.len() + chunk.len() > MAX_FILE_SIZE {
                return Err(ApiError::BadRequest(format!(
                    "File too large. Max size: {}MB",
                    MAX_FILE_SIZE / 1024 / 1024
                )));
            }
            data.extend_from_slice(&chunk);
        }

        file_data = Some((data, extension));
        break;
    }

    let (data, extension) = file_data.ok_or_else(|| {
        ApiError::BadRequest("No avatar file provided. Use field name 'avatar'".to_string())
    })?;

    // 生成唯一文件名
    let file_id = Uuid::new_v4();
    let filename = format!("{}.{}", file_id, extension);
    let file_path = app_data.upload_dir.join("avatars").join(&filename);

    // 保存文件
    let mut file = std::fs::File::create(&file_path)
        .map_err(|e| ApiError::Internal(format!("Failed to create file: {}", e)))?;
    file.write_all(&data)
        .map_err(|e| ApiError::Internal(format!("Failed to write file: {}", e)))?;

    // 生成头像 URL
    let avatar_url = format!("{}/uploads/avatars/{}", app_data.base_url, filename);

    // 更新用户头像
    let user = user::Entity::find_by_id(auth.user_id)
        .one(&app_data.db)
        .await?
        .ok_or_else(|| ApiError::NotFound("User not found".to_string()))?;

    // 如果用户之前有头像，尝试删除旧文件
    if let Some(old_avatar) = &user.avatar {
        if let Some(old_filename) = old_avatar.rsplit('/').next() {
            let old_path = app_data.upload_dir.join("avatars").join(old_filename);
            // 忽略删除错误，旧文件可能不存在
            let _ = std::fs::remove_file(old_path);
        }
    }

    let mut active: user::ActiveModel = user.into();
    active.avatar = Set(Some(avatar_url.clone()));
    active.updated_at = Set(chrono::Utc::now().fixed_offset());
    active.update(&app_data.db).await?;

    Ok(HttpResponse::Ok().json(UploadAvatarResponse { avatar_url }))
}

#[put("/password")]
async fn change_password(
    app_data: web::Data<AppData>,
    auth: AuthUser,
    body: web::Json<ChangePasswordRequest>,
) -> Result<impl Responder, ApiError> {
    if body.new_password.len() < 6 {
        return Err(ApiError::BadRequest("New password must be at least 6 characters".to_string()));
    }

    let user = user::Entity::find_by_id(auth.user_id)
        .one(&app_data.db)
        .await?
        .ok_or_else(|| ApiError::NotFound("User not found".to_string()))?;

    // Verify current password
    let valid = bcrypt::verify(&body.current_password, &user.password_hash)
        .map_err(|_| ApiError::Internal("Password verification failed".to_string()))?;

    if !valid {
        return Err(ApiError::BadRequest("Current password is incorrect".to_string()));
    }

    // Hash new password
    let new_password_hash = bcrypt::hash(&body.new_password, 10)
        .map_err(|_| ApiError::Internal("Password hashing failed".to_string()))?;

    // Update password
    let mut active: user::ActiveModel = user.into();
    active.password_hash = Set(new_password_hash);
    active.updated_at = Set(chrono::Utc::now().fixed_offset());
    active.update(&app_data.db).await?;

    Ok(HttpResponse::Ok().json(serde_json::json!({"success": true})))
}

pub fn routes() -> Scope {
    web::scope("/api/me")
        .service(get_me)
        .service(update_settings)
        .service(upload_avatar)
        .service(change_password)
}
