use rusqlite::{params, Connection};

use crate::error::{AppError, AppResult};
use crate::shared::types::Lang;

pub struct UserInfosQueryResponse {
    pub username: String,
    pub email: String,
    pub author_name: Option<String>,
    pub user_group: i64,
}

pub struct UserAccountQuery {
    pub username: String,
    pub author_name: Option<String>,
    pub email: String,
}

/// Inserts a new user into the database.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier for the user
/// * `first_name` - The user's first name
/// * `last_name` - The user's last name
/// * `username` - The user's username
/// * `origin_username` - The original username from the source platform
/// * `email` - The user's email address
/// * `origin_email` - The original email from the source platform
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns the user's UUID if insertion was successful.
/// Errors if the user cannot be registered.
pub fn insert_user(
    conn: &Connection, user_id: &str, username: &str, origin_username: &str,
    email: &str, origin_email: &str, lang: Lang,
) -> AppResult<String> {
    let insert_result = conn.execute(
        "INSERT OR IGNORE INTO erit_users (user_id, username, email, origin_email, origin_username) VALUES (?1,?2,?3,?4,?5)",
        params![user_id, username, email, origin_email, origin_username],
    ).map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'enregistrer l'utilisateur.".to_string() } else { "Unable to register user.".to_string() }))?;

    if insert_result == 0 {
        conn.execute(
            "UPDATE erit_users SET username = ?1, email = ?2, origin_email = ?3, origin_username = ?4 WHERE user_id = ?5",
            params![username, email, origin_email, origin_username, user_id],
        ).map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'utilisateur.".to_string() } else { "Unable to update user.".to_string() }))?;
    }

    Ok(user_id.to_string())
}

/// Fetches user information from the database.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user to fetch
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns the user information object.
/// Errors if the user is not found or cannot be retrieved.
pub fn fetch_user_infos(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<UserInfosQueryResponse> {
    let mut statement = conn
        .prepare("SELECT username, email, author_name, user_group FROM erit_users WHERE user_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les informations utilisateur.".to_string() } else { "Unable to retrieve user information.".to_string() }))?;

    let user_info = statement
        .query_row(params![user_id], |query_row| {
            Ok(UserInfosQueryResponse {
                username: query_row.get(0)?, email: query_row.get(1)?,
                author_name: query_row.get(2)?, user_group: query_row.get(3)?,
            })
        })
        .map_err(|error| match error {
            rusqlite::Error::QueryReturnedNoRows => AppError::NotFound(if lang == Lang::Fr { "Utilisateur non trouvé.".to_string() } else { "User not found.".to_string() }),
            _ => AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les informations utilisateur.".to_string() } else { "Unable to retrieve user information.".to_string() }),
        })?;

    Ok(user_info)
}

/// Updates user information in the database.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user to update
/// * `first_name` - The new first name
/// * `last_name` - The new last name
/// * `username` - The new username
/// * `origin_username` - The original username from the source platform
/// * `email` - The new email address
/// * `origin_email` - The original email from the source platform
/// * `original_author_name` - The original author name
/// * `author_name` - The new author name
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the update was successful, false otherwise.
/// Errors if the update fails.
pub fn update_user_infos(
    conn: &Connection, user_id: &str, username: &str, origin_username: &str,
    email: &str, origin_email: &str, original_author_name: &str, author_name: &str, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE erit_users SET username = ?1, email = ?2, origin_username = ?3, origin_author_name = ?4, author_name = ?5 WHERE user_id = ?6 AND origin_email = ?7",
            params![username, email, origin_username, original_author_name, author_name, user_id, origin_email],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour les informations utilisateur.".to_string() } else { "Unable to update user information.".to_string() }))?;

    Ok(update_result > 0)
}

/// Fetches account information for a user.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns the user account information object.
/// Errors if the account is not found or cannot be retrieved.
pub fn fetch_account_information(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<UserAccountQuery> {
    let mut statement = conn
        .prepare("SELECT username, author_name, email FROM erit_users WHERE user_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les informations du compte.".to_string() } else { "Unable to retrieve account information.".to_string() }))?;

    let account_info = statement
        .query_row(params![user_id], |query_row| {
            Ok(UserAccountQuery { username: query_row.get(0)?, author_name: query_row.get(1)?, email: query_row.get(2)? })
        })
        .map_err(|error| match error {
            rusqlite::Error::QueryReturnedNoRows => AppError::NotFound(if lang == Lang::Fr { "Compte non trouvé.".to_string() } else { "Account not found.".to_string() }),
            _ => AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les informations du compte.".to_string() } else { "Unable to retrieve account information.".to_string() }),
        })?;

    Ok(account_info)
}
