use serde::Deserialize;
use tauri::State;

use crate::db::connection::DbManager;
use crate::domains::act::service as act_service;
use crate::domains::book::service;
use crate::domains::chapter::service as chapter_service;
use crate::domains::download::service as download_service;
use crate::domains::export::service as export_service;
use crate::domains::guideline::service as guideline_service;
use crate::domains::incident::service as incident_service;
use crate::domains::issue::service as issue_service;
use crate::domains::plotpoint::service as plotpoint_service;
use crate::domains::sync::service as sync_service;
use crate::domains::upload::service as upload_service;
use crate::domains::world::service as world_service;
use crate::error::AppError;
use crate::shared::session::SessionState;

// ─── Helpers ──────────────────────────────────────────

fn get_session(session: &State<SessionState>) -> Result<(String, crate::shared::types::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))
}

fn get_conn<'a>(db: &'a State<DbManager>, _user_id: &str) -> Result<std::sync::MutexGuard<'a, crate::db::connection::DatabaseManager>, AppError> {
    db.lock().map_err(|e| AppError::Internal(format!("DB lock failed: {}", e)))
}

// ─── Book CRUD ────────────────────────────────────────

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateBookData {
    pub title: String,
    pub sub_title: Option<String>,
    pub summary: Option<String>,
    #[serde(rename = "type")]
    pub book_type: String,
    pub serie_id: Option<i64>,
    pub desired_release_date: Option<String>,
    pub desired_word_count: Option<i64>,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateBookBasicInfoData {
    pub book_id: String,
    pub title: String,
    pub sub_title: String,
    pub summary: String,
    pub publication_date: String,
    pub word_count: i64,
}

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

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateBookToolData {
    pub book_id: String,
    pub tool_name: String,
    pub enabled: bool,
}

#[tauri::command]
pub fn get_books(db: State<DbManager>, session: State<SessionState>) -> Result<Vec<service::BookProps>, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    service::get_books(conn, &user_id, lang)
}

#[tauri::command]
pub fn get_book(book_id: String, db: State<DbManager>, session: State<SessionState>) -> Result<service::BookProps, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    service::get_book(conn, &user_id, &book_id, lang)
}

#[tauri::command]
pub fn get_book_basic_information(book_id: String, db: State<DbManager>, session: State<SessionState>) -> Result<service::BookProps, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    service::get_book(conn, &user_id, &book_id, lang)
}

#[tauri::command]
pub fn create_book(data: CreateBookData, db: State<DbManager>, session: State<SessionState>) -> Result<String, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    service::add_book(conn, None, &user_id, &data.title, data.sub_title.as_deref().unwrap_or(""), data.summary.as_deref().unwrap_or(""), &data.book_type, data.serie_id.unwrap_or(0), data.desired_release_date.as_deref(), data.desired_word_count.unwrap_or(0), lang)
}

#[tauri::command]
pub fn update_book_basic_info(data: UpdateBookBasicInfoData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    service::update_book_basic_information(conn, &user_id, &data.title, &data.sub_title, &data.summary, Some(&data.publication_date), data.word_count, &data.book_id, lang)
}

#[tauri::command]
pub fn delete_book(data: DeleteBookData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    service::remove_book(conn, &user_id, &data.id, data.deleted_at, lang)
}

#[tauri::command]
pub fn update_book_tool_setting(data: UpdateBookToolData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    service::update_book_tool_setting(conn, &user_id, &data.book_id, &data.tool_name, data.enabled, lang)
}

// ─── Story ────────────────────────────────────────────

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

#[tauri::command]
pub fn get_book_story(data: GetStoryData, db: State<DbManager>, session: State<SessionState>) -> Result<serde_json::Value, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    let acts = act_service::get_acts_data(conn, &user_id, &data.book_id, lang)?;
    let issues = issue_service::get_issues_from_book(conn, &user_id, &data.book_id, lang)?;
    let main_chapters = chapter_service::get_all_chapters_from_a_book(conn, &user_id, &data.book_id, lang)?;
    Ok(serde_json::json!({ "acts": acts, "issues": issues, "mainChapter": main_chapters }))
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateStoryData {
    pub book_id: String,
    pub acts: Vec<act_service::ActProps>,
    pub main_chapters: Vec<act_service::ChapterProps>,
}

#[tauri::command]
pub fn update_book_story(data: UpdateStoryData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    act_service::update_story(conn, &user_id, &data.book_id, &data.acts, &data.main_chapters, lang)
}

// ─── Incidents ────────────────────────────────────────

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddIncidentData {
    pub book_id: String,
    pub name: String,
    pub incident_id: Option<String>,
}

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

#[tauri::command]
pub fn add_incident(data: AddIncidentData, db: State<DbManager>, session: State<SessionState>) -> Result<String, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    incident_service::add_new_incident(conn, &user_id, &data.book_id, &data.name, lang, data.incident_id.as_deref())
}

#[tauri::command]
pub fn remove_incident(data: RemoveIncidentData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    incident_service::remove_incident(conn, &user_id, &data.book_id, &data.incident_id, data.deleted_at, lang)
}

// ─── Plot Points ──────────────────────────────────────

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddPlotPointData {
    pub book_id: String,
    pub name: String,
    pub incident_id: String,
    pub plot_id: Option<String>,
}

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

#[tauri::command]
pub fn add_plot_point(data: AddPlotPointData, db: State<DbManager>, session: State<SessionState>) -> Result<String, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    plotpoint_service::add_new_plot_point(conn, &user_id, &data.book_id, &data.incident_id, &data.name, lang, data.plot_id.as_deref())
}

#[tauri::command]
pub fn remove_plot_point(data: RemovePlotPointData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    plotpoint_service::remove_plot_point(conn, &user_id, &data.book_id, &data.plot_id, data.deleted_at, lang)
}

// ─── Issues ───────────────────────────────────────────

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddIssueData {
    pub book_id: String,
    pub name: String,
    pub issue_id: Option<String>,
}

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

#[tauri::command]
pub fn add_issue(data: AddIssueData, db: State<DbManager>, session: State<SessionState>) -> Result<String, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    issue_service::add_new_issue(conn, &user_id, &data.book_id, &data.name, lang, data.issue_id.as_deref())
}

#[tauri::command]
pub fn remove_issue(data: RemoveIssueData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    issue_service::remove_issue(conn, &user_id, &data.book_id, &data.issue_id, data.deleted_at, lang)
}

// ─── Worlds ───────────────────────────────────────────

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

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddWorldData {
    pub book_id: String,
    pub world_name: String,
    pub id: Option<String>,
    pub series_world_id: Option<String>,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddWorldElementData {
    pub world_id: String,
    pub element_name: String,
    pub element_type: String,
    pub id: Option<String>,
}

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

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateWorldDataCmd {
    pub world: world_service::WorldProps,
}

#[tauri::command]
pub fn get_worlds(data: GetWorldsData, db: State<DbManager>, session: State<SessionState>) -> Result<world_service::WorldListResponse, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    world_service::get_worlds(conn, &user_id, &data.book_id, lang)
}

#[tauri::command]
pub fn add_world(data: AddWorldData, db: State<DbManager>, session: State<SessionState>) -> Result<String, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    world_service::add_new_world(conn, &user_id, &data.book_id, &data.world_name, lang, data.id.as_deref(), data.series_world_id.as_deref())
}

#[tauri::command]
pub fn add_world_element(data: AddWorldElementData, db: State<DbManager>, session: State<SessionState>) -> Result<String, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    world_service::add_new_element_to_world(conn, &user_id, &data.world_id, &data.element_name, &data.element_type, lang, data.id.as_deref())
}

#[tauri::command]
pub fn remove_world_element(data: RemoveWorldElementData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    world_service::remove_element_from_world(conn, &user_id, &data.book_id, &data.element_id, data.deleted_at, lang)
}

#[tauri::command]
pub fn update_world(data: UpdateWorldDataCmd, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    world_service::update_world(conn, &user_id, &data.world, lang)
}

// ─── Guidelines ───────────────────────────────────────

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

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateGuidelineData {
    pub book_id: String,
    pub tone: Option<String>,
    pub atmosphere: Option<String>,
    pub writing_style: Option<String>,
    pub themes: Option<String>,
    pub symbolism: Option<String>,
    pub motifs: Option<String>,
    pub narrative_voice: Option<String>,
    pub pacing: Option<String>,
    pub intended_audience: Option<String>,
    pub key_messages: Option<String>,
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateAIGuidelineData {
    pub book_id: String,
    pub narrative_type: i64,
    pub dialogue_type: i64,
    pub plot_summary: String,
    pub tone_atmosphere: String,
    pub verb_tense: i64,
    pub language: i64,
    pub themes: String,
}

#[tauri::command]
pub fn get_guideline(data: GetGuidelineData, db: State<DbManager>, session: State<SessionState>) -> Result<Option<guideline_service::GuideLineProps>, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    guideline_service::get_guide_line(conn, &user_id, &data.id, lang)
}

#[tauri::command]
pub fn update_guideline(data: UpdateGuidelineData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    guideline_service::update_guide_line(conn, &user_id, &data.book_id, data.tone.as_deref(), data.atmosphere.as_deref(), data.writing_style.as_deref(), data.themes.as_deref(), data.symbolism.as_deref(), data.motifs.as_deref(), data.narrative_voice.as_deref(), data.pacing.as_deref(), data.key_messages.as_deref(), data.intended_audience.as_deref(), lang)
}

#[tauri::command]
pub fn get_ai_guideline(data: GetGuidelineData, db: State<DbManager>, session: State<SessionState>) -> Result<guideline_service::GuideLineAI, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    guideline_service::get_guide_line_ai(conn, &user_id, &data.id, lang)
}

#[tauri::command]
pub fn update_ai_guideline(data: UpdateAIGuidelineData, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    guideline_service::set_ai_guide_line(conn, &user_id, &data.book_id, data.narrative_type, data.dialogue_type, &data.plot_summary, &data.tone_atmosphere, data.verb_tense, data.language, &data.themes, lang)
}

// ─── Export ───────────────────────────────────────────

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

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExportBookData {
    pub book_id: String,
    pub format: String,
    pub selections: Option<Vec<crate::domains::chapter::repo::ChapterSelectionParam>>,
}

#[tauri::command]
pub fn get_book_export_info(data: ExportInfoData, db: State<DbManager>, session: State<SessionState>) -> Result<Vec<chapter_service::ChapterExportInfo>, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    chapter_service::get_chapters_export_info(conn, &user_id, &data.book_id, lang)
}

#[tauri::command]
pub fn export_book(data: ExportBookData, db: State<DbManager>, session: State<SessionState>) -> Result<Vec<u8>, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    let book_data = chapter_service::get_complete_book_data_with_selections(conn, &user_id, &data.book_id, data.selections.as_deref(), lang)?;
    match data.format.as_str() {
        "epub" => { let result = export_service::transform_to_epub(&book_data, lang)?; Ok(result.buffer) },
        "pdf" => { let result = export_service::transform_to_pdf(&book_data)?; Ok(result.buffer) },
        "docx" => { let result = export_service::transform_to_docx(&book_data)?; Ok(result.buffer) },
        _ => Err(AppError::Validation(if lang == crate::shared::types::Lang::Fr { "Format non supporté.".to_string() } else { "Unsupported format.".to_string() })),
    }
}

// ─── Sync ─────────────────────────────────────────────

#[tauri::command]
pub fn get_synced_books(db: State<DbManager>, session: State<SessionState>) -> Result<Vec<sync_service::SyncedBookFull>, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    sync_service::get_synced_books(conn, &user_id, lang)
}

#[tauri::command]
pub fn upload_book_to_server(book_id: String, db: State<DbManager>, session: State<SessionState>) -> Result<service::CompleteBook, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    upload_service::upload_book_for_sync(conn, &user_id, &book_id, lang)
}

#[tauri::command]
pub fn sync_save_book(data: service::CompleteBook, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    download_service::save_complete_book(conn, &user_id, &data, lang)
}

#[tauri::command]
pub fn sync_book_to_client(data: service::CompleteBook, db: State<DbManager>, session: State<SessionState>) -> Result<bool, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    sync_service::sync_book_from_server_to_client(conn, &user_id, &data, lang)
}

#[tauri::command]
pub fn sync_book_to_server(data: service::BookSyncCompare, db: State<DbManager>, session: State<SessionState>) -> Result<service::CompleteBook, AppError> {
    let (user_id, lang) = get_session(&session)?;
    let db_manager = get_conn(&db, &user_id)?;
    let conn = db_manager.get_connection(&user_id)?;
    sync_service::get_complete_sync_book(conn, &user_id, &data, lang)
}
