use rusqlite::{params, Connection};

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

pub struct SeriesWorldResult {
    pub world_id: String,
    pub world_name: String,
    pub history: Option<String>,
    pub politics: Option<String>,
    pub economy: Option<String>,
    pub religion: Option<String>,
    pub languages: Option<String>,
    pub element_id: Option<String>,
    pub element_name: Option<String>,
    pub element_description: Option<String>,
    pub element_type: Option<i64>,
}

pub struct SeriesWorldsTableResult {
    pub world_id: String,
    pub series_id: String,
    pub user_id: String,
    pub name: String,
    pub hashed_name: String,
    pub history: Option<String>,
    pub politics: Option<String>,
    pub economy: Option<String>,
    pub religion: Option<String>,
    pub languages: Option<String>,
    pub last_update: i64,
}

pub struct SeriesWorldElementsTableResult {
    pub element_id: String,
    pub world_id: String,
    pub user_id: String,
    pub element_type: i64,
    pub name: String,
    pub original_name: String,
    pub description: Option<String>,
    pub last_update: i64,
}

pub struct SyncedSeriesWorldResult {
    pub world_id: String,
    pub series_id: String,
    pub name: String,
    pub last_update: i64,
}

pub struct SyncedSeriesWorldElementResult {
    pub element_id: String,
    pub world_id: String,
    pub name: String,
    pub last_update: i64,
}

/// Checks if a world with the given hashed name already exists for a user and series.
pub fn check_world_exist(conn: &Connection, user_id: &str, series_id: &str, world_name: &str, lang: Lang) -> AppResult<bool> {
    let mut statement = conn
        .prepare("SELECT world_id FROM series_worlds WHERE user_id=?1 AND series_id=?2 AND hashed_name=?3")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du monde.".to_string() } else { "Unable to verify world existence.".to_string() }))?;

    let exists = statement
        .exists(params![user_id, series_id, world_name])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du monde.".to_string() } else { "Unable to verify world existence.".to_string() }))?;

    Ok(exists)
}

/// Inserts a new world into the series.
pub fn insert_new_world(conn: &Connection, world_id: &str, user_id: &str, series_id: &str, encrypted_name: &str, hashed_name: &str, last_update: i64, lang: Lang) -> AppResult<String> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_worlds (world_id, user_id, series_id, name, hashed_name, last_update) VALUES (?1,?2,?3,?4,?5,?6)",
            params![world_id, user_id, series_id, encrypted_name, hashed_name, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter le monde.".to_string() } else { "Unable to add world.".to_string() }))?;

    if insert_result > 0 {
        Ok(world_id.to_string())
    } else {
        Err(AppError::Internal(if lang == Lang::Fr { "Erreur lors de l'ajout du monde.".to_string() } else { "Error adding world.".to_string() }))
    }
}

/// Fetches all worlds and their elements for a given series.
pub fn fetch_worlds(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldResult>> {
    let mut statement = conn
        .prepare("SELECT world.world_id AS world_id, world.name AS world_name, world.history, world.politics, world.economy, world.religion, world.languages, element.element_id AS element_id, element.name AS element_name, element.description AS element_description, element.element_type FROM series_worlds AS world LEFT JOIN series_world_elements AS element ON world.world_id = element.world_id WHERE world.user_id = ?1 AND world.series_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes.".to_string() } else { "Unable to retrieve worlds.".to_string() }))?;

    let worlds = statement
        .query_map(params![user_id, series_id], |query_row| {
            Ok(SeriesWorldResult {
                world_id: query_row.get(0)?, world_name: query_row.get(1)?,
                history: query_row.get(2)?, politics: query_row.get(3)?,
                economy: query_row.get(4)?, religion: query_row.get(5)?,
                languages: query_row.get(6)?, element_id: query_row.get(7)?,
                element_name: query_row.get(8)?, element_description: query_row.get(9)?,
                element_type: query_row.get(10)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes.".to_string() } else { "Unable to retrieve worlds.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes.".to_string() } else { "Unable to retrieve worlds.".to_string() }))?;

    Ok(worlds)
}

/// Updates a world's information.
pub fn update_world(
    conn: &Connection, user_id: &str, world_id: &str, encrypted_name: &str, hashed_name: &str,
    history: Option<&str>, politics: Option<&str>, economy: Option<&str>, religion: Option<&str>,
    languages: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_worlds SET name=?1, hashed_name=?2, history=?3, politics=?4, economy=?5, religion=?6, languages=?7, last_update=?8 WHERE world_id=?9 AND user_id=?10",
            params![encrypted_name, hashed_name, history, politics, economy, religion, languages, last_update, world_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le monde.".to_string() } else { "Unable to update world.".to_string() }))?;

    Ok(update_result > 0)
}

/// Inserts a new element for a world.
pub fn insert_element(
    conn: &Connection, element_id: &str, world_id: &str, user_id: &str, element_type: i64,
    encrypted_name: &str, original_name: &str, description: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<String> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_world_elements (element_id, world_id, user_id, element_type, name, original_name, description, last_update) VALUES (?1,?2,?3,?4,?5,?6,?7,?8)",
            params![element_id, world_id, user_id, element_type, encrypted_name, original_name, description, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter l'élément.".to_string() } else { "Unable to add element.".to_string() }))?;

    if insert_result > 0 {
        Ok(element_id.to_string())
    } else {
        Err(AppError::Internal(if lang == Lang::Fr { "Erreur lors de l'ajout de l'élément.".to_string() } else { "Error adding element.".to_string() }))
    }
}

/// Deletes an element from a world.
pub fn delete_element(conn: &Connection, user_id: &str, element_id: &str, lang: Lang) -> AppResult<bool> {
    let delete_result = conn
        .execute(
            "DELETE FROM series_world_elements WHERE element_id=?1 AND user_id=?2",
            params![element_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer l'élément.".to_string() } else { "Unable to delete element.".to_string() }))?;

    Ok(delete_result > 0)
}

/// Fetches all worlds for a series for sync.
pub fn fetch_series_worlds_table(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldsTableResult>> {
    let mut statement = conn
        .prepare("SELECT world_id, series_id, user_id, name, hashed_name, history, politics, economy, religion, languages, last_update FROM series_worlds WHERE series_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes pour sync.".to_string() } else { "Unable to retrieve worlds for sync.".to_string() }))?;

    let worlds = statement
        .query_map(params![series_id, user_id], |query_row| {
            Ok(SeriesWorldsTableResult {
                world_id: query_row.get(0)?, series_id: query_row.get(1)?,
                user_id: query_row.get(2)?, name: query_row.get(3)?,
                hashed_name: query_row.get(4)?, history: query_row.get(5)?,
                politics: query_row.get(6)?, economy: query_row.get(7)?,
                religion: query_row.get(8)?, languages: query_row.get(9)?,
                last_update: query_row.get(10)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes pour sync.".to_string() } else { "Unable to retrieve worlds for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes pour sync.".to_string() } else { "Unable to retrieve worlds for sync.".to_string() }))?;

    Ok(worlds)
}

/// Fetches all series worlds for a user for sync comparison.
pub fn fetch_synced_series_worlds(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesWorldResult>> {
    let mut statement = conn
        .prepare("SELECT world_id, series_id, name, last_update FROM series_worlds WHERE user_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes de série pour sync.".to_string() } else { "Unable to retrieve series worlds for sync.".to_string() }))?;

    let worlds = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedSeriesWorldResult { world_id: query_row.get(0)?, series_id: query_row.get(1)?, name: query_row.get(2)?, last_update: query_row.get(3)? })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes de série pour sync.".to_string() } else { "Unable to retrieve series worlds for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les mondes de série pour sync.".to_string() } else { "Unable to retrieve series worlds for sync.".to_string() }))?;

    Ok(worlds)
}

/// Fetches all series world elements for a user for sync comparison.
pub fn fetch_synced_series_world_elements(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSeriesWorldElementResult>> {
    let mut statement = conn
        .prepare("SELECT element_id, world_id, name, last_update FROM series_world_elements WHERE user_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde pour sync.".to_string() } else { "Unable to retrieve world elements for sync.".to_string() }))?;

    let elements = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedSeriesWorldElementResult { element_id: query_row.get(0)?, world_id: query_row.get(1)?, name: query_row.get(2)?, last_update: query_row.get(3)? })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde pour sync.".to_string() } else { "Unable to retrieve world elements for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde pour sync.".to_string() } else { "Unable to retrieve world elements for sync.".to_string() }))?;

    Ok(elements)
}

/// Checks if a world exists.
pub fn is_world_exist(conn: &Connection, user_id: &str, world_id: &str, lang: Lang) -> AppResult<bool> {
    let mut statement = conn
        .prepare("SELECT 1 FROM series_worlds WHERE world_id=?1 AND user_id=?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du monde.".to_string() } else { "Unable to check world existence.".to_string() }))?;

    let exists = statement
        .exists(params![world_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du monde.".to_string() } else { "Unable to check world existence.".to_string() }))?;

    Ok(exists)
}

/// Checks if a world element exists.
pub fn is_world_element_exist(conn: &Connection, user_id: &str, element_id: &str, lang: Lang) -> AppResult<bool> {
    let mut statement = conn
        .prepare("SELECT 1 FROM series_world_elements WHERE element_id=?1 AND user_id=?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'élément.".to_string() } else { "Unable to check element existence.".to_string() }))?;

    let exists = statement
        .exists(params![element_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'élément.".to_string() } else { "Unable to check element existence.".to_string() }))?;

    Ok(exists)
}

/// Inserts a series world for sync.
pub fn insert_sync_world(
    conn: &Connection, world_id: &str, series_id: &str, user_id: &str, name: &str, hashed_name: &str,
    history: Option<&str>, politics: Option<&str>, economy: Option<&str>, religion: Option<&str>,
    languages: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_worlds (world_id, series_id, user_id, name, hashed_name, history, politics, economy, religion, languages, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11) ON CONFLICT(world_id) DO UPDATE SET name = excluded.name, hashed_name = excluded.hashed_name, history = excluded.history, politics = excluded.politics, economy = excluded.economy, religion = excluded.religion, languages = excluded.languages, last_update = excluded.last_update",
            params![world_id, series_id, user_id, name, hashed_name, history, politics, economy, religion, languages, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le monde pour sync.".to_string() } else { "Unable to insert world for sync.".to_string() }))?;

    Ok(insert_result > 0)
}

/// Inserts a series world element for sync.
pub fn insert_sync_world_element(
    conn: &Connection, element_id: &str, world_id: &str, user_id: &str, element_type: i64,
    name: &str, original_name: &str, description: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_world_elements (element_id, world_id, user_id, element_type, name, original_name, description, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8) ON CONFLICT(element_id) DO UPDATE SET element_type = excluded.element_type, name = excluded.name, original_name = excluded.original_name, description = excluded.description, last_update = excluded.last_update",
            params![element_id, world_id, user_id, element_type, name, original_name, description, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer l'élément de monde pour sync.".to_string() } else { "Unable to insert world element for sync.".to_string() }))?;

    Ok(insert_result > 0)
}

/// Fetches all world elements for a series by series ID.
pub fn fetch_series_world_elements_by_series_id(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesWorldElementsTableResult>> {
    let mut statement = conn
        .prepare("SELECT swe.element_id, swe.world_id, swe.user_id, swe.element_type, swe.name, swe.original_name, swe.description, swe.last_update FROM series_world_elements swe INNER JOIN series_worlds sw ON swe.world_id = sw.world_id WHERE sw.series_id = ?1 AND sw.user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde par série.".to_string() } else { "Unable to retrieve world elements by series.".to_string() }))?;

    let elements = statement
        .query_map(params![series_id, user_id], |query_row| {
            Ok(SeriesWorldElementsTableResult {
                element_id: query_row.get(0)?, world_id: query_row.get(1)?,
                user_id: query_row.get(2)?, element_type: query_row.get(3)?,
                name: query_row.get(4)?, original_name: query_row.get(5)?,
                description: query_row.get(6)?, last_update: query_row.get(7)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde par série.".to_string() } else { "Unable to retrieve world elements by series.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de monde par série.".to_string() } else { "Unable to retrieve world elements by series.".to_string() }))?;

    Ok(elements)
}

/// Checks if a series world exists (alias for is_world_exist).
pub fn series_world_exists(conn: &Connection, user_id: &str, world_id: &str, lang: Lang) -> AppResult<bool> {
    is_world_exist(conn, user_id, world_id, lang)
}

/// Checks if a series world element exists (alias for is_world_element_exist).
pub fn series_world_element_exists(conn: &Connection, user_id: &str, element_id: &str, lang: Lang) -> AppResult<bool> {
    is_world_element_exist(conn, user_id, element_id, lang)
}

/// Inserts a series world for sync (alias with compatible signature).
pub fn insert_sync_series_world(
    conn: &Connection, world_id: &str, series_id: &str, user_id: &str, name: &str, hashed_name: &str,
    history: Option<&str>, politics: Option<&str>, economy: Option<&str>, religion: Option<&str>,
    languages: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    insert_sync_world(conn, world_id, series_id, user_id, name, hashed_name, history, politics, economy, religion, languages, last_update, lang)
}

/// Updates a series world for sync (without hashed_name).
pub fn update_sync_series_world(
    conn: &Connection, world_id: &str, user_id: &str, name: &str, history: Option<&str>,
    politics: Option<&str>, economy: Option<&str>, religion: Option<&str>, languages: Option<&str>,
    last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_worlds SET name = ?1, history = ?2, politics = ?3, economy = ?4, religion = ?5, languages = ?6, last_update = ?7 WHERE world_id = ?8 AND user_id = ?9",
            params![name, history, politics, economy, religion, languages, last_update, world_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le monde série pour sync.".to_string() } else { "Unable to update series world for sync.".to_string() }))?;

    Ok(update_result > 0)
}

/// Inserts a series world element for sync (alias with compatible signature).
pub fn insert_sync_series_world_element(
    conn: &Connection, element_id: &str, world_id: &str, user_id: &str, element_type: i64,
    name: &str, original_name: &str, description: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    insert_sync_world_element(conn, element_id, world_id, user_id, element_type, name, original_name, description, last_update, lang)
}

/// Updates a series world element for sync (without element_type and original_name).
pub fn update_sync_series_world_element(conn: &Connection, element_id: &str, user_id: &str, name: &str, description: Option<&str>, last_update: i64, lang: Lang) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_world_elements SET name = ?1, description = ?2, last_update = ?3 WHERE element_id = ?4 AND user_id = ?5",
            params![name, description, last_update, element_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'élément de monde série pour sync.".to_string() } else { "Unable to update series world element for sync.".to_string() }))?;

    Ok(update_result > 0)
}
