use rusqlite::{params, Connection};

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

pub struct BookElementSeriesLink {
    pub series_id: Option<String>,
}

const CHARACTER_ALLOWED_FIELDS: &[&str] = &["first_name", "last_name", "nickname", "age", "gender", "species", "nationality", "status", "title", "category", "role", "biography", "history", "speech_pattern", "catchphrase", "residence", "notes", "color"];

const WORLD_ALLOWED_FIELDS: &[&str] = &["name", "history", "politics", "economy", "religion", "languages"];

const LOCATION_ALLOWED_FIELDS: &[&str] = &["name"];

const LOCATION_BOOK_ALLOWED_FIELDS: &[&str] = &["loc_name"];

const SPELL_ALLOWED_FIELDS: &[&str] = &["name", "description", "type", "level", "range", "duration", "cost", "effect", "components", "notes"];

/// Gets the series element ID linked to a book character.
pub fn get_character_series_link(conn: &Connection, user_id: &str, character_id: &str, lang: Lang) -> AppResult<Option<String>> {
    let mut statement = conn
        .prepare("SELECT series_character_id AS series_id FROM book_characters WHERE character_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lien série du personnage.".to_string() } else { "Unable to retrieve character series link.".to_string() }))?;

    let result = statement
        .query_row(params![character_id, user_id], |query_row| {
            Ok(BookElementSeriesLink { series_id: query_row.get(0)? })
        });

    match result {
        Ok(link) => Ok(link.series_id),
        Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
        Err(_) => Err(AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lien série du personnage.".to_string() } else { "Unable to retrieve character series link.".to_string() })),
    }
}

/// Gets the series element ID linked to a book world.
pub fn get_world_series_link(conn: &Connection, user_id: &str, world_id: &str, lang: Lang) -> AppResult<Option<String>> {
    let mut statement = conn
        .prepare("SELECT series_world_id AS series_id FROM book_world WHERE world_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lien série du monde.".to_string() } else { "Unable to retrieve world series link.".to_string() }))?;

    let result = statement
        .query_row(params![world_id, user_id], |query_row| {
            Ok(BookElementSeriesLink { series_id: query_row.get(0)? })
        });

    match result {
        Ok(link) => Ok(link.series_id),
        Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
        Err(_) => Err(AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lien série du monde.".to_string() } else { "Unable to retrieve world series link.".to_string() })),
    }
}

/// Gets the series element ID linked to a book location.
pub fn get_location_series_link(conn: &Connection, user_id: &str, location_id: &str, lang: Lang) -> AppResult<Option<String>> {
    let mut statement = conn
        .prepare("SELECT series_location_id AS series_id FROM book_location WHERE loc_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lien série du lieu.".to_string() } else { "Unable to retrieve location series link.".to_string() }))?;

    let result = statement
        .query_row(params![location_id, user_id], |query_row| {
            Ok(BookElementSeriesLink { series_id: query_row.get(0)? })
        });

    match result {
        Ok(link) => Ok(link.series_id),
        Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
        Err(_) => Err(AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lien série du lieu.".to_string() } else { "Unable to retrieve location series link.".to_string() })),
    }
}

/// Gets the series element ID linked to a book spell.
pub fn get_spell_series_link(conn: &Connection, user_id: &str, spell_id: &str, lang: Lang) -> AppResult<Option<String>> {
    let mut statement = conn
        .prepare("SELECT series_spell_id AS series_id FROM book_spells WHERE spell_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lien série du sort.".to_string() } else { "Unable to retrieve spell series link.".to_string() }))?;

    let result = statement
        .query_row(params![spell_id, user_id], |query_row| {
            Ok(BookElementSeriesLink { series_id: query_row.get(0)? })
        });

    match result {
        Ok(link) => Ok(link.series_id),
        Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
        Err(_) => Err(AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lien série du sort.".to_string() } else { "Unable to retrieve spell series link.".to_string() })),
    }
}

/// Updates a field in series_characters table.
pub fn update_series_character_field(conn: &Connection, user_id: &str, series_character_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<bool> {
    if !CHARACTER_ALLOWED_FIELDS.contains(&field) {
        return Err(AppError::Validation(if lang == Lang::Fr { format!("Champ non autorisé: {}", field) } else { format!("Field not allowed: {}", field) }));
    }

    let query = format!("UPDATE series_characters SET {} = ?1, last_update = ?2 WHERE character_id = ?3 AND user_id = ?4", field);

    let update_result = conn
        .execute(&query, params![encrypted_value, last_update, series_character_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le personnage série.".to_string() } else { "Unable to update series character.".to_string() }))?;

    Ok(update_result > 0)
}

/// Updates a field in all book_characters linked to a series character.
pub fn update_linked_book_characters_field(conn: &Connection, user_id: &str, series_character_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<usize> {
    if !CHARACTER_ALLOWED_FIELDS.contains(&field) {
        return Err(AppError::Validation(if lang == Lang::Fr { format!("Champ non autorisé: {}", field) } else { format!("Field not allowed: {}", field) }));
    }

    let query = format!("UPDATE book_characters SET {} = ?1, last_update = ?2 WHERE series_character_id = ?3 AND user_id = ?4", field);

    let update_result = conn
        .execute(&query, params![encrypted_value, last_update, series_character_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour les personnages liés.".to_string() } else { "Unable to update linked characters.".to_string() }))?;

    Ok(update_result)
}

/// Updates a field in series_worlds table.
pub fn update_series_world_field(conn: &Connection, user_id: &str, series_world_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<bool> {
    if !WORLD_ALLOWED_FIELDS.contains(&field) {
        return Err(AppError::Validation(if lang == Lang::Fr { format!("Champ non autorisé: {}", field) } else { format!("Field not allowed: {}", field) }));
    }

    let query = format!("UPDATE series_worlds SET {} = ?1, last_update = ?2 WHERE world_id = ?3 AND user_id = ?4", field);

    let update_result = conn
        .execute(&query, params![encrypted_value, last_update, series_world_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le monde série.".to_string() } else { "Unable to update series world.".to_string() }))?;

    Ok(update_result > 0)
}

/// Updates a field in all book_world linked to a series world.
pub fn update_linked_book_worlds_field(conn: &Connection, user_id: &str, series_world_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<usize> {
    if !WORLD_ALLOWED_FIELDS.contains(&field) {
        return Err(AppError::Validation(if lang == Lang::Fr { format!("Champ non autorisé: {}", field) } else { format!("Field not allowed: {}", field) }));
    }

    let query = format!("UPDATE book_world SET {} = ?1, last_update = ?2 WHERE series_world_id = ?3 AND user_id = ?4", field);

    let update_result = conn
        .execute(&query, params![encrypted_value, last_update, series_world_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour les mondes liés.".to_string() } else { "Unable to update linked worlds.".to_string() }))?;

    Ok(update_result)
}

/// Updates a field in series_locations table.
pub fn update_series_location_field(conn: &Connection, user_id: &str, series_location_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<bool> {
    if !LOCATION_ALLOWED_FIELDS.contains(&field) {
        return Err(AppError::Validation(if lang == Lang::Fr { format!("Champ non autorisé: {}", field) } else { format!("Field not allowed: {}", field) }));
    }

    let query = format!("UPDATE series_locations SET {} = ?1, last_update = ?2 WHERE location_id = ?3 AND user_id = ?4", field);

    let update_result = conn
        .execute(&query, params![encrypted_value, last_update, series_location_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le lieu série.".to_string() } else { "Unable to update series location.".to_string() }))?;

    Ok(update_result > 0)
}

/// Updates a field in all book_location linked to a series location.
pub fn update_linked_book_locations_field(conn: &Connection, user_id: &str, series_location_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<usize> {
    if !LOCATION_BOOK_ALLOWED_FIELDS.contains(&field) {
        return Err(AppError::Validation(if lang == Lang::Fr { format!("Champ non autorisé: {}", field) } else { format!("Field not allowed: {}", field) }));
    }

    let query = format!("UPDATE book_location SET {} = ?1, last_update = ?2 WHERE series_location_id = ?3 AND user_id = ?4", field);

    let update_result = conn
        .execute(&query, params![encrypted_value, last_update, series_location_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour les lieux liés.".to_string() } else { "Unable to update linked locations.".to_string() }))?;

    Ok(update_result)
}

/// Updates a field in series_spells table.
pub fn update_series_spell_field(conn: &Connection, user_id: &str, series_spell_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<bool> {
    if !SPELL_ALLOWED_FIELDS.contains(&field) {
        return Err(AppError::Validation(if lang == Lang::Fr { format!("Champ non autorisé: {}", field) } else { format!("Field not allowed: {}", field) }));
    }

    let query = format!("UPDATE series_spells SET {} = ?1, last_update = ?2 WHERE spell_id = ?3 AND user_id = ?4", field);

    let update_result = conn
        .execute(&query, params![encrypted_value, last_update, series_spell_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le sort série.".to_string() } else { "Unable to update series spell.".to_string() }))?;

    Ok(update_result > 0)
}

/// Updates a field in all book_spells linked to a series spell.
pub fn update_linked_book_spells_field(conn: &Connection, user_id: &str, series_spell_id: &str, field: &str, encrypted_value: &str, last_update: i64, lang: Lang) -> AppResult<usize> {
    if !SPELL_ALLOWED_FIELDS.contains(&field) {
        return Err(AppError::Validation(if lang == Lang::Fr { format!("Champ non autorisé: {}", field) } else { format!("Field not allowed: {}", field) }));
    }

    let query = format!("UPDATE book_spells SET {} = ?1, last_update = ?2 WHERE series_spell_id = ?3 AND user_id = ?4", field);

    let update_result = conn
        .execute(&query, params![encrypted_value, last_update, series_spell_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour les sorts liés.".to_string() } else { "Unable to update linked spells.".to_string() }))?;

    Ok(update_result)
}
