use rusqlite::{params, Connection};

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

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

pub struct BookSpellTagsTable {
    pub tag_id: String,
    pub book_id: String,
    pub user_id: String,
    pub name: String,
    pub name_hash: String,
    pub color: Option<String>,
    pub last_update: i64,
}

pub struct SyncedSpellTagResult {
    pub tag_id: String,
    pub book_id: String,
    pub name: String,
    pub last_update: i64,
}

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

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

    Ok(rows)
}

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

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

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

    Ok(update_result > 0)
}

/// Deletes a spell tag.
pub fn delete_spell_tag(conn: &Connection, user_id: &str, tag_id: &str, lang: Lang) -> AppResult<bool> {
    let delete_result = conn
        .execute("DELETE FROM book_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 de sort.".to_string() } else { "Unable to delete spell tag.".to_string() }))?;

    Ok(delete_result > 0)
}

/// Fetches all spell tags for a book with full table data for sync.
pub fn fetch_book_spell_tags_table(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<BookSpellTagsTable>> {
    let mut statement = conn
        .prepare("SELECT tag_id, book_id, user_id, name, name_hash, color, last_update FROM book_spell_tags WHERE user_id=?1 AND book_id=?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sorts.".to_string() } else { "Unable to retrieve spell tags.".to_string() }))?;

    let rows = statement
        .query_map(params![user_id, book_id], |query_row| {
            Ok(BookSpellTagsTable {
                tag_id: query_row.get(0)?, book_id: query_row.get(1)?,
                user_id: query_row.get(2)?, name: query_row.get(3)?,
                name_hash: 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 sorts.".to_string() } else { "Unable to retrieve spell tags.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sorts.".to_string() } else { "Unable to retrieve spell tags.".to_string() }))?;

    Ok(rows)
}

/// Fetches a complete spell tag record by its ID.
pub fn fetch_spell_tag_table_by_id(conn: &Connection, user_id: &str, tag_id: &str, lang: Lang) -> AppResult<Option<BookSpellTagsTable>> {
    let mut statement = conn
        .prepare("SELECT tag_id, book_id, user_id, name, name_hash, color, last_update FROM book_spell_tags WHERE user_id=?1 AND tag_id=?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le tag de sort.".to_string() } else { "Unable to retrieve spell tag.".to_string() }))?;

    let spell_tags = statement
        .query_map(params![user_id, tag_id], |query_row| {
            Ok(BookSpellTagsTable {
                tag_id: query_row.get(0)?, book_id: query_row.get(1)?,
                user_id: query_row.get(2)?, name: query_row.get(3)?,
                name_hash: 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 le tag de sort.".to_string() } else { "Unable to retrieve spell tag.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le tag de sort.".to_string() } else { "Unable to retrieve spell tag.".to_string() }))?;

    Ok(if spell_tags.is_empty() { None } else { Some(spell_tags.into_iter().next().unwrap()) })
}

/// Fetches all synced spell tags for a user.
pub fn fetch_synced_spell_tags(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedSpellTagResult>> {
    let mut statement = conn
        .prepare("SELECT tag_id, book_id, name, last_update FROM book_spell_tags WHERE user_id=?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sorts synchronisés.".to_string() } else { "Unable to retrieve synced spell tags.".to_string() }))?;

    let rows = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedSpellTagResult {
                tag_id: query_row.get(0)?, book_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 sorts synchronisés.".to_string() } else { "Unable to retrieve synced spell tags.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags de sorts synchronisés.".to_string() } else { "Unable to retrieve synced spell tags.".to_string() }))?;

    Ok(rows)
}

/// Inserts or updates a spell tag from synchronization data.
pub fn insert_sync_spell_tag(conn: &Connection, tag_id: &str, book_id: &str, user_id: &str, name: &str, name_hash: &str, color: Option<&str>, last_update: i64, lang: Lang) -> AppResult<bool> {
    let insert_result = conn
        .execute("INSERT OR REPLACE INTO book_spell_tags (tag_id, book_id, user_id, name, name_hash, color, last_update) VALUES (?1,?2,?3,?4,?5,?6,?7)", params![tag_id, book_id, user_id, name, name_hash, color, last_update])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le tag de sort.".to_string() } else { "Unable to insert spell tag.".to_string() }))?;

    Ok(insert_result > 0)
}

/// 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 book_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 exists = statement
        .exists(params![tag_id, user_id])
        .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() }))?;

    Ok(exists)
}

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

    Ok(update_result > 0)
}
