use serde::{Deserialize, Serialize};
use tauri::State;

use crate::crypto::{encryption, key_manager};
use crate::db::connection::DbManager;
use crate::db::schema;
use crate::domains::user::service;
use crate::error::{AppError, AppResult};
use crate::shared::session::SessionState;

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateUserData {
    pub username: String,
    pub email: String,
    pub author_name: Option<String>,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitUserData {
    pub user_id: String,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InitUserResult {
    pub success: bool,
    pub error: Option<String>,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncUserData {
    pub user_id: String,
    pub username: String,
    pub email: String,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UserInfoResponse {
    pub id: String,
    pub username: String,
    pub email: String,
    pub author_name: String,
    pub group_id: i64,
}

#[tauri::command]
pub fn init_user(data: InitUserData, db: State<DbManager>, session: State<SessionState>) -> Result<InitUserResult, AppError> {
    let mut db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    db_manager.initialize(&data.user_id)?;
    let conn = db_manager.get_connection(&data.user_id)?;
    schema::initialize_schema(conn)?;
    if cfg!(debug_assertions) {
        schema::run_dev_queries(conn);
    } else {
        schema::run_migrations(conn)?;
    }

    let mut session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
    session_guard.user_id = Some(data.user_id.clone());

    key_manager::set_last_user_id(&data.user_id)?;

    if key_manager::get_user_encryption_key(&data.user_id).is_err() {
        let encryption_key = encryption::generate_user_encryption_key(&data.user_id)?;
        key_manager::set_user_encryption_key(&data.user_id, &encryption_key)?;
    }

    Ok(InitUserResult { success: true, error: None })
}

#[tauri::command]
pub fn db_initialize(user_id: String, encryption_key: String, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let mut db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    db_manager.initialize(&user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    schema::initialize_schema(conn)?;
    if cfg!(debug_assertions) {
        schema::run_dev_queries(conn);
    } else {
        schema::run_migrations(conn)?;
    }

    if key_manager::get_user_encryption_key(&user_id).is_err() {
        let key = if encryption_key.is_empty() {
            encryption::generate_user_encryption_key(&user_id)?
        } else {
            encryption_key
        };
        key_manager::set_user_encryption_key(&user_id, &key)?;
    }

    let mut session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
    session_guard.user_id = Some(user_id);

    Ok(true)
}

#[tauri::command]
pub fn get_token() -> Result<Option<String>, AppError> {
    key_manager::get_token()
}

#[tauri::command]
pub fn set_token(token: String) -> Result<(), AppError> {
    key_manager::set_token(&token)
}

#[tauri::command]
pub fn remove_token(db: State<DbManager>, session: State<SessionState>) -> Result<(), AppError> {
    let session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
    if let Ok(user_id) = session_guard.get_user_id() {
        let mut db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
        db_manager.close(user_id);
    }
    drop(session_guard);
    key_manager::remove_token()
}

#[tauri::command]
pub fn get_user_encryption_key(user_id: String) -> Result<Option<String>, AppError> {
    match key_manager::get_user_encryption_key(&user_id) {
        Ok(key) => Ok(Some(key)),
        Err(AppError::Keyring(msg)) if msg.contains("No encryption key") => Ok(None),
        Err(e) => Err(e),
    }
}

#[tauri::command]
pub fn get_platform() -> String {
    std::env::consts::OS.to_string()
}

#[tauri::command]
pub fn get_user_info(db: State<DbManager>, session: State<SessionState>) -> Result<UserInfoResponse, AppError> {
    let session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
    let user_id = session_guard.get_user_id().map_err(|e| AppError::Auth(e))?;
    let lang = session_guard.lang;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(user_id)?;

    let user_info = service::return_user_infos(conn, user_id, lang)?;

    Ok(UserInfoResponse {
        id: user_info.id,
        username: user_info.username,
        email: user_info.email,
        author_name: user_info.author_name,
        group_id: user_info.group_id,
    })
}

#[tauri::command]
pub fn sync_user(data: SyncUserData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
    let lang = session_guard.lang;
    drop(session_guard);

    let mut db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    db_manager.initialize(&data.user_id)?;
    let conn = db_manager.get_connection(&data.user_id)?;

    service::add_user(conn, &data.user_id, &data.username, &data.email, lang)?;
    Ok(true)
}

#[tauri::command]
pub fn update_user_info(data: UpdateUserData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
    let user_id = session_guard.get_user_id().map_err(|e| AppError::Auth(e))?.to_string();
    let lang = session_guard.lang;
    drop(session_guard);

    let user_key = key_manager::get_user_encryption_key(&user_id)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;

    service::update_user_infos(conn, &user_key, &user_id, &data.username, &data.email, data.author_name.as_deref(), lang)
}

#[tauri::command]
pub fn dev_reset_all(db: State<DbManager>, session: State<SessionState>) -> AppResult<bool> {
    if !cfg!(debug_assertions) {
        return Err(AppError::Internal("dev_reset_all is only available in dev mode".to_string()));
    }

    // 1. Close all DB connections
    let mut db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let base_path = db_manager.base_path().clone();
    db_manager.close_all();
    drop(db_manager);

    // 2. Delete all eritors-local-*.db files (+ WAL/SHM)
    if let Ok(entries) = std::fs::read_dir(&base_path) {
        for entry in entries.flatten() {
            let path = entry.path();
            if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
                if name.starts_with("eritors-local-") {
                    let _ = std::fs::remove_file(&path);
                }
            }
        }
    }

    // 3. Clear vault (tokens, encryption keys, PIN hashes)
    key_manager::clear_vault()?;

    // 4. Reset session
    let mut session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
    session_guard.user_id = None;

    Ok(true)
}
