use rusqlite::{params, Connection};

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

pub struct SeriesSpellResult {
    pub spell_id: String,
    pub _series_id: String,
    pub name: String,
    pub description: String,
    pub appearance: String,
    pub tags: String,
    pub power_level: Option<String>,
    pub components: Option<String>,
    pub limitations: Option<String>,
    pub notes: Option<String>,
}

pub struct SeriesSpellTagResult {
    pub tag_id: String,
    pub name: String,
    pub color: Option<String>,
}

pub struct SeriesSpellsTableResult {
    pub spell_id: String,
    pub series_id: String,
    pub user_id: String,
    pub name: String,
    pub name_hash: String,
    pub description: Option<String>,
    pub appearance: Option<String>,
    pub tags: Option<String>,
    pub power_level: Option<String>,
    pub components: Option<String>,
    pub limitations: Option<String>,
    pub notes: Option<String>,
    pub last_update: i64,
}

pub struct SeriesSpellTagsTableResult {
    pub tag_id: String,
    pub series_id: String,
    pub user_id: String,
    pub name: String,
    pub hashed_name: String,
    pub color: Option<String>,
    pub last_update: i64,
}

pub struct SyncedSeriesSpellResult {
    pub spell_id: String,
    pub series_id: String,
    pub name: String,
    pub last_update: i64,
}

pub struct SyncedSeriesSpellTagResult {
    pub tag_id: String,
    pub series_id: String,
    pub name: String,
    pub last_update: i64,
}

/// Fetches all spells for a specific series.
pub fn fetch_spells(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellResult>> {
    let mut statement = conn
        .prepare("SELECT spell_id, series_id, name, description, appearance, tags, power_level, components, limitations, notes FROM series_spells WHERE user_id=?1 AND series_id=?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts.".to_string() } else { "Unable to retrieve spells.".to_string() }))?;

    let spells = statement
        .query_map(params![user_id, series_id], |query_row| {
            Ok(SeriesSpellResult {
                spell_id: query_row.get(0)?, _series_id: query_row.get(1)?,
                name: query_row.get(2)?, description: query_row.get(3)?,
                appearance: query_row.get(4)?, tags: query_row.get(5)?,
                power_level: query_row.get(6)?, components: query_row.get(7)?,
                limitations: query_row.get(8)?, notes: query_row.get(9)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts.".to_string() } else { "Unable to retrieve spells.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts.".to_string() } else { "Unable to retrieve spells.".to_string() }))?;

    Ok(spells)
}

/// Fetches a single spell by its ID.
pub fn fetch_spell_by_id(conn: &Connection, user_id: &str, spell_id: &str, lang: Lang) -> AppResult<Option<SeriesSpellResult>> {
    let mut statement = conn
        .prepare("SELECT spell_id, series_id, name, description, appearance, tags, power_level, components, limitations, notes FROM series_spells WHERE user_id=?1 AND spell_id=?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sort.".to_string() } else { "Unable to retrieve spell.".to_string() }))?;

    let spell = statement
        .query_row(params![user_id, spell_id], |query_row| {
            Ok(SeriesSpellResult {
                spell_id: query_row.get(0)?, _series_id: query_row.get(1)?,
                name: query_row.get(2)?, description: query_row.get(3)?,
                appearance: query_row.get(4)?, tags: query_row.get(5)?,
                power_level: query_row.get(6)?, components: query_row.get(7)?,
                limitations: query_row.get(8)?, notes: query_row.get(9)?,
            })
        });

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

/// Inserts a new spell.
pub fn insert_spell(
    conn: &Connection, spell_id: &str, series_id: &str, user_id: &str, name: &str, name_hash: &str,
    description: &str, appearance: &str, tags: &str, power_level: Option<&str>, components: Option<&str>,
    limitations: Option<&str>, notes: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<String> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_spells (spell_id, series_id, user_id, name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13)",
            params![spell_id, series_id, user_id, name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter le sort.".to_string() } else { "Unable to add spell.".to_string() }))?;

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

/// Updates an existing spell.
pub fn update_spell(
    conn: &Connection, user_id: &str, spell_id: &str, name: &str, name_hash: &str, description: &str,
    appearance: &str, tags: &str, power_level: Option<&str>, components: Option<&str>,
    limitations: Option<&str>, notes: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_spells SET name=?1, name_hash=?2, description=?3, appearance=?4, tags=?5, power_level=?6, components=?7, limitations=?8, notes=?9, last_update=?10 WHERE spell_id=?11 AND user_id=?12",
            params![name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update, spell_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le sort.".to_string() } else { "Unable to update spell.".to_string() }))?;

    Ok(update_result > 0)
}

/// Deletes a spell.
pub fn delete_spell(conn: &Connection, user_id: &str, spell_id: &str, lang: Lang) -> AppResult<bool> {
    let delete_result = conn
        .execute("DELETE FROM series_spells WHERE spell_id=?1 AND user_id=?2", params![spell_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer le sort.".to_string() } else { "Unable to delete spell.".to_string() }))?;

    Ok(delete_result > 0)
}

/// Fetches all spell tags for a series.
pub fn fetch_tags(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellTagResult>> {
    let mut statement = conn
        .prepare("SELECT tag_id, name, color FROM series_spell_tags WHERE user_id=?1 AND series_id=?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags.".to_string() } else { "Unable to retrieve tags.".to_string() }))?;

    let tags = statement
        .query_map(params![user_id, series_id], |query_row| {
            Ok(SeriesSpellTagResult { tag_id: query_row.get(0)?, name: query_row.get(1)?, color: query_row.get(2)? })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags.".to_string() } else { "Unable to retrieve tags.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags.".to_string() } else { "Unable to retrieve tags.".to_string() }))?;

    Ok(tags)
}

/// Inserts a new spell tag.
pub fn insert_tag(
    conn: &Connection, tag_id: &str, series_id: &str, user_id: &str, name: &str,
    hashed_name: &str, color: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<String> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_spell_tags (tag_id, series_id, user_id, name, hashed_name, color, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
            params![tag_id, series_id, user_id, name, hashed_name, color, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter le tag.".to_string() } else { "Unable to add tag.".to_string() }))?;

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

/// Updates an existing spell tag.
pub fn update_tag(
    conn: &Connection, user_id: &str, tag_id: &str, name: &str,
    hashed_name: &str, color: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_spell_tags SET name=?1, hashed_name=?2, color=?3, last_update=?4 WHERE tag_id=?5 AND user_id=?6",
            params![name, hashed_name, color, last_update, tag_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le tag.".to_string() } else { "Unable to update tag.".to_string() }))?;

    Ok(update_result > 0)
}

/// Deletes a spell tag.
pub fn delete_tag(conn: &Connection, user_id: &str, tag_id: &str, lang: Lang) -> AppResult<bool> {
    let delete_result = conn
        .execute("DELETE FROM series_spell_tags WHERE tag_id=?1 AND user_id=?2", params![tag_id, user_id])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer le tag.".to_string() } else { "Unable to delete tag.".to_string() }))?;

    Ok(delete_result > 0)
}

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

    let result = statement.query_row(params![spell_id, user_id], |_query_row| Ok(()));

    match result {
        Ok(_) => Ok(true),
        Err(rusqlite::Error::QueryReturnedNoRows) => Ok(false),
        Err(_) => Err(AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du sort.".to_string() } else { "Unable to check spell existence.".to_string() })),
    }
}

/// Fetches all spells for a series for sync.
pub fn fetch_series_spells_table(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellsTableResult>> {
    let mut statement = conn
        .prepare("SELECT spell_id, series_id, user_id, name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update FROM series_spells WHERE series_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts pour sync.".to_string() } else { "Unable to retrieve spells for sync.".to_string() }))?;

    let spells = statement
        .query_map(params![series_id, user_id], |query_row| {
            Ok(SeriesSpellsTableResult {
                spell_id: query_row.get(0)?, series_id: query_row.get(1)?,
                user_id: query_row.get(2)?, name: query_row.get(3)?,
                name_hash: query_row.get(4)?, description: query_row.get(5)?,
                appearance: query_row.get(6)?, tags: query_row.get(7)?,
                power_level: query_row.get(8)?, components: query_row.get(9)?,
                limitations: query_row.get(10)?, notes: query_row.get(11)?,
                last_update: query_row.get(12)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts pour sync.".to_string() } else { "Unable to retrieve spells for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts pour sync.".to_string() } else { "Unable to retrieve spells for sync.".to_string() }))?;

    Ok(spells)
}

/// Fetches all spell tags for a series for sync.
pub fn fetch_series_spell_tags_table(conn: &Connection, user_id: &str, series_id: &str, lang: Lang) -> AppResult<Vec<SeriesSpellTagsTableResult>> {
    let mut statement = conn
        .prepare("SELECT tag_id, series_id, user_id, name, hashed_name, color, last_update FROM series_spell_tags WHERE series_id = ?1 AND user_id = ?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sort pour sync.".to_string() } else { "Unable to retrieve spell tags for sync.".to_string() }))?;

    let tags = statement
        .query_map(params![series_id, user_id], |query_row| {
            Ok(SeriesSpellTagsTableResult {
                tag_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)?, color: query_row.get(5)?,
                last_update: query_row.get(6)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sort pour sync.".to_string() } else { "Unable to retrieve spell tags for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sort pour sync.".to_string() } else { "Unable to retrieve spell tags for sync.".to_string() }))?;

    Ok(tags)
}

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

    let spells = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedSeriesSpellResult { spell_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 sorts de série pour sync.".to_string() } else { "Unable to retrieve series spells for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sorts de série pour sync.".to_string() } else { "Unable to retrieve series spells for sync.".to_string() }))?;

    Ok(spells)
}

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

    let tags = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedSeriesSpellTagResult { tag_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 tags de sort pour sync.".to_string() } else { "Unable to retrieve spell tags for sync.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sort pour sync.".to_string() } else { "Unable to retrieve spell tags for sync.".to_string() }))?;

    Ok(tags)
}

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

    let result = statement.query_row(params![tag_id, user_id], |_query_row| Ok(()));

    match result {
        Ok(_) => Ok(true),
        Err(rusqlite::Error::QueryReturnedNoRows) => Ok(false),
        Err(_) => Err(AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du tag.".to_string() } else { "Unable to check tag existence.".to_string() })),
    }
}

/// Inserts a series spell for sync.
pub fn insert_sync_spell(
    conn: &Connection, spell_id: &str, series_id: &str, user_id: &str, name: &str, name_hash: &str,
    description: &str, appearance: &str, tags: &str, power_level: Option<&str>, components: Option<&str>,
    limitations: Option<&str>, notes: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let insert_result = conn
        .execute(
            "INSERT INTO series_spells (spell_id, series_id, user_id, name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13) ON CONFLICT(spell_id) DO UPDATE SET name = excluded.name, name_hash = excluded.name_hash, description = excluded.description, appearance = excluded.appearance, tags = excluded.tags, power_level = excluded.power_level, components = excluded.components, limitations = excluded.limitations, notes = excluded.notes, last_update = excluded.last_update",
            params![spell_id, series_id, user_id, name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le sort pour sync.".to_string() } else { "Unable to insert spell for sync.".to_string() }))?;

    Ok(insert_result > 0)
}

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

    Ok(insert_result > 0)
}

/// Checks if a series spell exists (alias for is_spell_exist).
pub fn series_spell_exists(conn: &Connection, user_id: &str, spell_id: &str, lang: Lang) -> AppResult<bool> {
    is_spell_exist(conn, user_id, spell_id, lang)
}

/// Checks if a series spell tag exists (alias for is_spell_tag_exist).
pub fn series_spell_tag_exists(conn: &Connection, user_id: &str, tag_id: &str, lang: Lang) -> AppResult<bool> {
    is_spell_tag_exist(conn, user_id, tag_id, lang)
}

/// Inserts a series spell for sync (alias with compatible signature).
pub fn insert_sync_series_spell(
    conn: &Connection, spell_id: &str, series_id: &str, user_id: &str, name: &str, name_hash: &str,
    description: &str, appearance: &str, tags: &str, power_level: Option<&str>, components: Option<&str>,
    limitations: Option<&str>, notes: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    insert_sync_spell(conn, spell_id, series_id, user_id, name, name_hash, description, appearance, tags, power_level, components, limitations, notes, last_update, lang)
}

/// Updates a series spell for sync (simplified signature).
pub fn update_sync_series_spell(
    conn: &Connection, spell_id: &str, user_id: &str, name: &str, description: &str, appearance: &str,
    tags: &str, power_level: Option<&str>, components: Option<&str>, limitations: Option<&str>,
    notes: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_spells SET name = ?1, description = ?2, appearance = ?3, tags = ?4, power_level = ?5, components = ?6, limitations = ?7, notes = ?8, last_update = ?9 WHERE spell_id = ?10 AND user_id = ?11",
            params![name, description, appearance, tags, power_level, components, limitations, notes, last_update, spell_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le sort série pour sync.".to_string() } else { "Unable to update series spell for sync.".to_string() }))?;

    Ok(update_result > 0)
}

/// Inserts a series spell tag for sync (alias with compatible signature).
pub fn insert_sync_series_spell_tag(
    conn: &Connection, tag_id: &str, series_id: &str, user_id: &str, name: &str,
    hashed_name: &str, color: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    insert_sync_spell_tag(conn, tag_id, series_id, user_id, name, hashed_name, color, last_update, lang)
}

/// Updates a series spell tag for sync (simplified signature).
pub fn update_sync_series_spell_tag(
    conn: &Connection, tag_id: &str, user_id: &str, name: &str,
    color: Option<&str>, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute(
            "UPDATE series_spell_tags SET name = ?1, color = ?2, last_update = ?3 WHERE tag_id = ?4 AND user_id = ?5",
            params![name, color, last_update, tag_id, user_id],
        )
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le tag série pour sync.".to_string() } else { "Unable to update series tag for sync.".to_string() }))?;

    Ok(update_result > 0)
}
