use serde::{Deserialize, Serialize};
use serde_json::Value;
use tauri::State;

use crate::db::connection::DbManager;
use crate::domains::chapter::service;
use crate::error::AppError;
use crate::helpers::timestamp_in_seconds;
use crate::shared::session::SessionState;
use crate::shared::types::Lang;

fn get_session(session: &State<SessionState>) -> Result<(String, Lang), AppError> {
    let session_guard = session.lock().map_err(|e| AppError::Internal(format!("Session lock failed: {}", e)))?;
    let user_id = session_guard.get_user_id().map_err(|e| AppError::Auth(e))?.to_string();
    let lang = session_guard.lang;
    Ok((user_id, lang))
}

// ─── Queries ──────────────────────────────────────────

#[tauri::command]
pub fn get_chapters(book_id: String, db: State<DbManager>, session: State<SessionState>) -> Result<Vec<service::ChapterProps>, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::get_all_chapters_from_a_book(conn, &user_id, &book_id, lang)
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetWholeChapterData {
    pub id: String,
    pub version: i64,
    pub book_id: String,
}

#[tauri::command]
pub fn get_whole_chapter(data: GetWholeChapterData, db: State<DbManager>, session: State<SessionState>) -> Result<service::ChapterProps, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::get_whole_chapter(conn, &user_id, &data.id, data.version, Some(&data.book_id), lang)
}

#[tauri::command]
pub fn get_chapter_story(chapter_id: String, db: State<DbManager>, session: State<SessionState>) -> Result<Vec<service::ActStory>, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::get_chapter_story(conn, &user_id, &chapter_id, lang)
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetCompanionData {
    pub chapter_id: String,
    pub version: i64,
}

#[tauri::command]
pub fn get_companion_content(data: GetCompanionData, db: State<DbManager>, session: State<SessionState>) -> Result<service::CompanionContent, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::get_companion_content(conn, &user_id, &data.chapter_id, data.version, lang)
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetChapterContentData {
    pub chapter_id: String,
    pub version: i64,
}

#[tauri::command]
pub fn get_chapter_content(data: GetChapterContentData, db: State<DbManager>, session: State<SessionState>) -> Result<String, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::get_chapter_content_by_version(conn, &user_id, &data.chapter_id, data.version, lang)
}

// ─── Mutations ────────────────────────────────────────

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SaveChapterContentData {
    pub chapter_id: String,
    pub version: i64,
    pub content: Value,
    pub total_word_count: i64,
}

#[tauri::command]
pub fn save_chapter_content(data: SaveChapterContentData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    let content_str = serde_json::to_string(&data.content).map_err(|e| AppError::Internal(format!("JSON serialize failed: {}", e)))?;
    service::save_chapter_content(conn, &user_id, &data.chapter_id, data.version, &content_str, data.total_word_count, timestamp_in_seconds(), lang)
}

#[tauri::command]
pub fn get_last_chapter(book_id: String, db: State<DbManager>, session: State<SessionState>) -> Result<Option<service::ChapterProps>, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::get_last_chapter(conn, &user_id, &book_id, lang)
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddChapterData {
    pub book_id: String,
    pub title: String,
    pub chapter_order: i64,
    pub chapter_id: Option<String>,
}

#[tauri::command]
pub fn add_chapter(data: AddChapterData, db: State<DbManager>, session: State<SessionState>) -> Result<String, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::add_chapter(conn, &user_id, &data.book_id, &data.title, 0, data.chapter_order, lang, data.chapter_id.as_deref())
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RemoveChapterData {
    pub chapter_id: String,
    pub book_id: String,
    pub deleted_at: i64,
}

#[tauri::command]
pub fn remove_chapter(data: RemoveChapterData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::remove_chapter(conn, &user_id, &data.book_id, &data.chapter_id, data.deleted_at, lang)
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateChapterData {
    pub chapter_id: String,
    pub title: String,
    pub chapter_order: i64,
}

#[tauri::command]
pub fn update_chapter(data: UpdateChapterData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::update_chapter(conn, &user_id, &data.chapter_id, &data.title, data.chapter_order, lang)
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddChapterInfoData {
    pub chapter_id: String,
    pub act_id: i64,
    pub book_id: String,
    pub plot_id: Option<String>,
    pub incident_id: Option<String>,
    pub chapter_info_id: Option<String>,
}

#[tauri::command]
pub fn add_chapter_information(data: AddChapterInfoData, db: State<DbManager>, session: State<SessionState>) -> Result<String, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::add_chapter_information(conn, &user_id, &data.chapter_id, data.act_id, &data.book_id, data.plot_id.as_deref(), data.incident_id.as_deref(), lang, data.chapter_info_id.as_deref())
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RemoveChapterInfoData {
    pub chapter_info_id: String,
    pub book_id: String,
    pub deleted_at: i64,
}

#[tauri::command]
pub fn remove_chapter_information(data: RemoveChapterInfoData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;
    service::remove_chapter_information(conn, &user_id, &data.book_id, &data.chapter_info_id, data.deleted_at, lang)
}

// ─── Book Tags (aggregate) ───────────────────────────

#[derive(Serialize)]
pub struct Tag {
    pub label: String,
    pub value: String,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BookTags {
    pub characters: Vec<Tag>,
    pub locations: Vec<Tag>,
    pub objects: Vec<Tag>,
    pub world_elements: Vec<Tag>,
}

#[tauri::command]
pub fn get_book_tags(book_id: String, db: State<DbManager>, session: State<SessionState>) -> Result<BookTags, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))?;
    let conn = db_manager.get_connection(&user_id)?;

    let character_response = crate::domains::character::service::get_character_list(conn, &user_id, &book_id, lang)?;
    let character_tags: Vec<Tag> = character_response.characters.into_iter().map(|character| Tag { label: character.name, value: character.id }).collect();

    let location_elements = crate::domains::location::service::get_location_tags(conn, &user_id, &book_id, lang)?;
    let location_tags: Vec<Tag> = location_elements.into_iter().map(|element| Tag { label: element.name, value: element.id }).collect();

    let spell_response = crate::domains::spell::service::get_spell_list(conn, &user_id, &book_id, lang)?;
    let object_tags: Vec<Tag> = spell_response.spells.into_iter().map(|spell| Tag { label: spell.name, value: spell.id }).collect();

    let world_response = crate::domains::world::service::get_worlds(conn, &user_id, &book_id, lang)?;
    let mut world_tags: Vec<Tag> = Vec::new();
    for world in &world_response.worlds {
        for element_list in [&world.laws, &world.biomes, &world.issues, &world.customs, &world.kingdoms, &world.climate, &world.resources, &world.wildlife, &world.arts, &world.ethnic_groups, &world.social_classes, &world.important_characters] {
            for element in element_list {
                world_tags.push(Tag { label: element.name.clone(), value: element.id.clone() });
            }
        }
    }

    Ok(BookTags { characters: character_tags, locations: location_tags, objects: object_tags, world_elements: world_tags })
}
