use rusqlite::{params, Connection};

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

pub struct ChapterContentQueryResult {
    pub chapter_id: String,
    pub version: i64,
    pub content: String,
    pub words_count: i64,
    pub title: String,
    pub chapter_order: i64,
}

pub struct ContentQueryResult {
    pub content: String,
}

pub struct CompanionContentQueryResult {
    pub version: i64,
    pub content: String,
    pub words_count: i64,
}

pub struct BookChapterContentTable {
    pub content_id: String,
    pub chapter_id: String,
    pub author_id: String,
    pub version: i64,
    pub content: Option<String>,
    pub _words_count: i64,
    pub _time_on_it: i64,
    pub last_update: i64,
}

pub struct SyncedChapterContentResult {
    pub content_id: String,
    pub chapter_id: String,
    pub last_update: i64,
}

/// Fetches the last chapter content for a given book.
/// * `conn` - Database connection
/// * `user_id` - The ID of the user/author
/// * `book_id` - The ID of the book
/// * `lang` - The language for error messages
/// Returns an array of chapter content results ordered by chapter order and version descending.
pub fn fetch_last_chapter_content(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<ChapterContentQueryResult>> {
    let mut statement = conn
        .prepare("SELECT book_chapters.chapter_id as chapter_id, COALESCE(book_chapter_content.version, 2) AS version, COALESCE(book_chapter_content.content, '') AS content, COALESCE(book_chapter_content.words_count, 0) AS words_count, book_chapters.title, book_chapters.chapter_order FROM book_chapters LEFT JOIN book_chapter_content ON book_chapters.chapter_id = book_chapter_content.chapter_id WHERE book_chapters.author_id = ?1 AND book_chapters.book_id = ?2 ORDER BY book_chapters.chapter_order DESC, book_chapter_content.version DESC LIMIT 1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le dernier chapitre.".to_string() } else { "Unable to retrieve last chapter.".to_string() }))?;

    let rows = statement
        .query_map(params![user_id, book_id], |query_row| {
            Ok(ChapterContentQueryResult {
                chapter_id: query_row.get(0)?, version: query_row.get(1)?,
                content: query_row.get(2)?, words_count: query_row.get(3)?,
                title: query_row.get(4)?, chapter_order: query_row.get(5)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le dernier chapitre.".to_string() } else { "Unable to retrieve last chapter.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le dernier chapitre.".to_string() } else { "Unable to retrieve last chapter.".to_string() }))?;

    Ok(rows)
}

/// Updates the content of a chapter. If no existing content is found, inserts a new record.
/// * `conn` - Database connection
/// * `user_id` - The ID of the user/author
/// * `chapter_id` - The ID of the chapter
/// * `version` - The version number of the content
/// * `encrypt_content` - The encrypted content string
/// * `words_count` - The word count of the content
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages
/// Returns true if the update or insert was successful.
pub fn update_chapter_content(
    conn: &Connection, user_id: &str, chapter_id: &str, version: i64,
    encrypt_content: &str, words_count: i64, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let update_result = conn
        .execute("UPDATE book_chapter_content SET content=?1, words_count=?2, last_update=?3 WHERE chapter_id=?4 AND author_id=?5 AND version=?6", params![encrypt_content, words_count, last_update, chapter_id, user_id, version])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le contenu du chapitre.".to_string() } else { "Unable to update chapter content.".to_string() }))?;

    if update_result > 0 {
        return Ok(true);
    }

    let content_id = create_unique_id(None);
    let insert_result = conn
        .execute("INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, last_update) VALUES (?1,?2,?3,?4,?5,?6,?7)", params![content_id, chapter_id, user_id, version, encrypt_content, words_count, last_update])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le contenu du chapitre.".to_string() } else { "Unable to update chapter content.".to_string() }))?;

    Ok(insert_result > 0)
}

/// Fetches companion content for a specific chapter and version.
/// * `conn` - Database connection
/// * `user_id` - The ID of the user/author
/// * `chapter_id` - The ID of the chapter
/// * `version` - The version number to fetch
/// * `lang` - The language for error messages
/// Returns an array of companion content results.
pub fn fetch_companion_content(conn: &Connection, user_id: &str, chapter_id: &str, version: i64, lang: Lang) -> AppResult<Vec<CompanionContentQueryResult>> {
    let mut statement = conn
        .prepare("SELECT version, content, words_count FROM book_chapter_content WHERE author_id=?1 AND chapter_id=?2 AND version=?3")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu compagnon.".to_string() } else { "Unable to retrieve companion content.".to_string() }))?;

    let rows = statement
        .query_map(params![user_id, chapter_id, version], |query_row| {
            Ok(CompanionContentQueryResult {
                version: query_row.get(0)?, content: query_row.get(1)?,
                words_count: query_row.get(2)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu compagnon.".to_string() } else { "Unable to retrieve companion content.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu compagnon.".to_string() } else { "Unable to retrieve companion content.".to_string() }))?;

    Ok(rows)
}

/// Fetches chapter content by its order position within a book.
/// * `conn` - Database connection
/// * `user_id` - The ID of the user/author
/// * `chapter_order` - The order position of the chapter
/// * `book_id` - The ID of the book
/// * `lang` - The language for error messages
/// Returns the content query result for the specified chapter.

/// Fetches chapter content by chapter ID and version number.
/// * `conn` - Database connection
/// * `user_id` - The ID of the user/author
/// * `chapter_id` - The ID of the chapter
/// * `version` - The version number to fetch
/// * `lang` - The language for error messages
/// Returns the content query result for the specified version.
pub fn fetch_chapter_content_by_version(conn: &Connection, user_id: &str, chapter_id: &str, version: i64, lang: Lang) -> AppResult<ContentQueryResult> {
    let mut statement = conn
        .prepare("SELECT content FROM book_chapter_content WHERE author_id=?1 AND chapter_id=?2 AND version=?3")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu du chapitre.".to_string() } else { "Unable to retrieve chapter content.".to_string() }))?;

    let chapter_content = statement
        .query_row(params![user_id, chapter_id, version], |query_row| {
            Ok(ContentQueryResult { content: query_row.get(0)? })
        })
        .map_err(|error| match error {
            rusqlite::Error::QueryReturnedNoRows => AppError::NotFound(if lang == Lang::Fr { "Aucun chapitre trouvé avec cette version.".to_string() } else { "No chapter found with this version.".to_string() }),
            _ => AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu du chapitre.".to_string() } else { "Unable to retrieve chapter content.".to_string() }),
        })?;

    Ok(chapter_content)
}

/// Checks whether chapter content exists for a given content ID and user.
/// * `conn` - Database connection
/// * `user_id` - The ID of the user/author
/// * `content_id` - The ID of the content to check
/// * `lang` - The language for error messages
/// Returns true if the chapter content exists, false otherwise.
pub fn is_chapter_content_exist(conn: &Connection, user_id: &str, content_id: &str, lang: Lang) -> AppResult<bool> {
    let mut statement = conn
        .prepare("SELECT 1 FROM book_chapter_content WHERE content_id=?1 AND author_id=?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du contenu du chapitre.".to_string() } else { "Unable to check chapter content existence.".to_string() }))?;

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

    Ok(exists)
}

pub fn fetch_all_chapter_contents_by_book(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<BookChapterContentTable>> {
    let mut statement = conn
        .prepare("SELECT bcc.content_id, bcc.chapter_id, bcc.author_id, bcc.version, bcc.content, bcc.words_count, bcc.time_on_it, bcc.last_update FROM book_chapter_content bcc INNER JOIN book_chapters bc ON bcc.chapter_id = bc.chapter_id WHERE bcc.author_id=?1 AND bc.book_id=?2")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres.".to_string() } else { "Unable to retrieve chapter contents.".to_string() }))?;
    let rows = statement
        .query_map(params![user_id, book_id], |query_row| {
            Ok(BookChapterContentTable {
                content_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
                author_id: query_row.get(2)?, version: query_row.get(3)?,
                content: query_row.get(4)?, _words_count: query_row.get(5)?,
                _time_on_it: query_row.get(6)?, last_update: query_row.get(7)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres.".to_string() } else { "Unable to retrieve chapter contents.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres.".to_string() } else { "Unable to retrieve chapter contents.".to_string() }))?;
    Ok(rows)
}

/// Fetches all synced chapter contents for a user (content ID, chapter ID, and last update timestamp).
/// * `conn` - Database connection
/// * `user_id` - The ID of the user/author
/// * `lang` - The language for error messages
/// Returns an array of synced chapter content results.
pub fn fetch_synced_chapter_contents(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedChapterContentResult>> {
    let mut statement = conn
        .prepare("SELECT content_id, chapter_id, last_update FROM book_chapter_content WHERE author_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres synchronisés.".to_string() } else { "Unable to retrieve synced chapter contents.".to_string() }))?;

    let rows = statement
        .query_map(params![user_id], |query_row| {
            Ok(SyncedChapterContentResult {
                content_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
                last_update: query_row.get(2)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres synchronisés.".to_string() } else { "Unable to retrieve synced chapter contents.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu des chapitres synchronisés.".to_string() } else { "Unable to retrieve synced chapter contents.".to_string() }))?;

    Ok(rows)
}

/// Inserts a new chapter content record during synchronization.
/// * `conn` - Database connection
/// * `content_id` - The unique ID for the content
/// * `chapter_id` - The ID of the chapter
/// * `author_id` - The ID of the author
/// * `version` - The version number of the content
/// * `content` - The content string (can be null)
/// * `words_count` - The word count of the content
/// * `time_on_it` - The time spent on this content
/// * `last_update` - The timestamp of the last update
/// * `lang` - The language for error messages
/// Returns true if the insert was successful.
pub fn insert_sync_chapter_content(
    conn: &Connection, content_id: &str, chapter_id: &str, author_id: &str, version: i64,
    content: Option<&str>, words_count: i64, time_on_it: i64, last_update: i64, lang: Lang,
) -> AppResult<bool> {
    let insert_result = conn
        .execute("INSERT INTO book_chapter_content (content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", params![content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update])
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le contenu du chapitre.".to_string() } else { "Unable to insert chapter content.".to_string() }))?;

    Ok(insert_result > 0)
}

/// Fetches the complete chapter content record by its content ID.
/// * `conn` - Database connection
/// * `content_id` - The ID of the content to fetch
/// * `lang` - The language for error messages
/// Returns an array of book chapter content records.
pub fn fetch_complete_chapter_content_by_id(conn: &Connection, content_id: &str, lang: Lang) -> AppResult<Vec<BookChapterContentTable>> {
    let mut statement = conn
        .prepare("SELECT content_id, chapter_id, author_id, version, content, words_count, time_on_it, last_update FROM book_chapter_content WHERE content_id = ?1")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu de chapitre complet.".to_string() } else { "Unable to retrieve complete chapter content.".to_string() }))?;

    let rows = statement
        .query_map(params![content_id], |query_row| {
            Ok(BookChapterContentTable {
                content_id: query_row.get(0)?, chapter_id: query_row.get(1)?,
                author_id: query_row.get(2)?, version: query_row.get(3)?,
                content: query_row.get(4)?, _words_count: query_row.get(5)?,
                _time_on_it: query_row.get(6)?, last_update: query_row.get(7)?,
            })
        })
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu de chapitre complet.".to_string() } else { "Unable to retrieve complete chapter content.".to_string() }))?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le contenu de chapitre complet.".to_string() } else { "Unable to retrieve complete chapter content.".to_string() }))?;

    Ok(rows)
}

/// Fetches a complete chapter with its content by joining chapters and chapter content tables.
/// * `conn` - Database connection
/// * `user_id` - The ID of the user/author
/// * `chapter_id` - The ID of the chapter
/// * `version` - The version number of the content to fetch
/// * `lang` - The language for error messages
/// Returns the chapter content query result with chapter metadata.
pub fn fetch_whole_chapter(conn: &Connection, user_id: &str, chapter_id: &str, version: i64, lang: Lang) -> AppResult<ChapterContentQueryResult> {
    let mut statement = conn
        .prepare("SELECT chapter.chapter_id as chapter_id, chapter.title as title, chapter.chapter_order, chapter.words_count, content.content AS content, content.version as version FROM book_chapters AS chapter LEFT JOIN book_chapter_content AS content ON content.chapter_id = chapter.chapter_id AND content.version = ?1 WHERE chapter.chapter_id = ?2 AND chapter.author_id = ?3")
        .map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le chapitre.".to_string() } else { "Unable to retrieve chapter.".to_string() }))?;

    let whole_chapter = statement
        .query_row(params![version, chapter_id, user_id], |query_row| {
            Ok(ChapterContentQueryResult {
                chapter_id: query_row.get(0)?, title: query_row.get(1)?,
                chapter_order: query_row.get(2)?, words_count: query_row.get::<_, Option<i64>>(3)?.unwrap_or(0),
                content: query_row.get::<_, Option<String>>(4)?.unwrap_or_default(),
                version: query_row.get::<_, Option<i64>>(5)?.unwrap_or(2),
            })
        })
        .map_err(|error| match error {
            rusqlite::Error::QueryReturnedNoRows => AppError::NotFound(if lang == Lang::Fr { "Aucun chapitre trouvé avec cet ID.".to_string() } else { "No chapter found with this ID.".to_string() }),
            _ => AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le chapitre.".to_string() } else { "Unable to retrieve chapter.".to_string() }),
        })?;

    Ok(whole_chapter)
}
