use rusqlite::Connection;
use serde::{Deserialize, Serialize};

use crate::crypto::encryption::{encrypt_data_with_user_key, decrypt_data_with_user_key, hash_element};
use crate::crypto::key_manager::get_user_encryption_key;
use crate::domains::act::repo;
use crate::domains::chapter::repo as chapter_repo;
use crate::domains::chapter::service as chapter_service;
use crate::domains::incident::repo as incident_repo;
use crate::domains::incident::service as incident_service;
use crate::domains::plotpoint::repo as plotpoint_repo;
use crate::domains::plotpoint::service as plotpoint_service;
use crate::error::AppResult;
use crate::helpers::{create_unique_id, timestamp_in_seconds};
use crate::shared::types::Lang;

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ActProps {
    pub id: i64,
    pub summary: Option<String>,
    pub incidents: Option<Vec<IncidentProps>>,
    pub plot_points: Option<Vec<PlotPointProps>>,
    pub chapters: Option<Vec<ActChapter>>,
}

#[derive(Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ActChapter {
    pub chapter_info_id: i64,
    pub chapter_id: String,
    pub title: String,
    pub chapter_order: i64,
    pub act_id: i64,
    pub incident_id: Option<String>,
    pub plot_point_id: Option<String>,
    pub summary: String,
    pub goal: String,
}

pub use incident_service::IncidentProps;
pub use plotpoint_service::PlotPointProps;

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

/// Retrieves all chapters linked to acts for a specific book.
/// Decrypts titles, summaries, and goals using the user's encryption key.
/// Uses a cache to avoid decrypting the same chapter title multiple times.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns a list of decrypted act chapters.
fn get_all_chapter_from_acts(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<ActChapter>> {
    let act_chapter_query_results: Vec<chapter_repo::ActChapterQuery> = chapter_repo::fetch_all_chapter_for_acts(conn, user_id, book_id, lang)?;
    let user_encryption_key: String = get_user_encryption_key(user_id)?;

    if act_chapter_query_results.is_empty() {
        return Ok(vec![]);
    }

    let mut act_chapters: Vec<ActChapter> = Vec::new();
    let mut decrypted_title_cache: Vec<(String, String)> = Vec::new();

    for chapter_query_result in &act_chapter_query_results {
        let decrypted_title: String = if let Some(cached) = decrypted_title_cache.iter().find(|(id, _)| id == &chapter_query_result.chapter_id) {
            cached.1.clone()
        } else {
            let title: String = decrypt_data_with_user_key(&chapter_query_result.title, &user_encryption_key)?;
            decrypted_title_cache.push((chapter_query_result.chapter_id.clone(), title.clone()));
            title
        };

        let decrypted_goal: String = if chapter_query_result.goal.is_empty() { String::new() } else { decrypt_data_with_user_key(&chapter_query_result.goal, &user_encryption_key)? };
        let decrypted_summary: String = if chapter_query_result.summary.is_empty() { String::new() } else { decrypt_data_with_user_key(&chapter_query_result.summary, &user_encryption_key)? };

        act_chapters.push(ActChapter {
            chapter_info_id: chapter_query_result.chapter_info_id,
            chapter_id: chapter_query_result.chapter_id.clone(),
            title: decrypted_title,
            chapter_order: chapter_query_result.chapter_order,
            act_id: chapter_query_result.act_id,
            incident_id: chapter_query_result.incident_id.clone(),
            plot_point_id: chapter_query_result.plot_point_id.clone(),
            summary: decrypted_summary,
            goal: decrypted_goal,
        });
    }

    Ok(act_chapters)
}

/// Updates chapter information for multiple chapters including summary and goal.
/// Encrypts summaries and goals before storing in the database.
/// * `conn` - Database connection
/// * `chapters` - Array of ActChapter objects containing updated information

/// Updates a chapter's title and order.
/// Encrypts the title and generates a hash before storing.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `chapter_id` - The unique identifier of the chapter
/// * `title` - The plain text title
/// * `chapter_order` - The chapter order position
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the update was successful.
fn update_chapter(conn: &Connection, user_id: &str, chapter_id: &str, title: &str, chapter_order: i64, lang: Lang) -> AppResult<bool> {
    let hashed_title: String = hash_element(title);
    let user_encryption_key: String = get_user_encryption_key(user_id)?;
    let encrypted_title: String = encrypt_data_with_user_key(title, &user_encryption_key)?;
    chapter_repo::update_chapter(conn, user_id, chapter_id, &encrypted_title, &hashed_title, chapter_order, timestamp_in_seconds(), lang)
}

/// Retrieves all acts data for a specific book, including chapters, incidents, and plot points.
/// Decrypts summaries using the user's encryption key.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns a list of Act objects with their associated data.
pub fn get_acts_data(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<ActProps>> {
    let user_encryption_key: String = get_user_encryption_key(user_id)?;
    let act_chapters: Vec<ActChapter> = get_all_chapter_from_acts(conn, user_id, book_id, lang)?;
    let act_queries: Vec<repo::ActQuery> = repo::fetch_all_acts(conn, user_id, book_id, lang)?;
    let book_incidents: Vec<IncidentProps> = incident_service::get_incitents_incidents(conn, user_id, book_id, &act_chapters, lang)?;
    let book_plot_points: Vec<PlotPointProps> = plotpoint_service::get_plot_points(conn, user_id, book_id, &act_chapters, lang)?;

    let mut acts: Vec<ActProps> = Vec::new();

    acts.push(ActProps {
        id: 1,
        summary: Some(String::new()),
        incidents: None,
        plot_points: None,
        chapters: Some(act_chapters.iter().filter(|chapter| chapter.act_id == 1).map(|chapter| ActChapter {
            chapter_info_id: chapter.chapter_info_id,
            chapter_id: chapter.chapter_id.clone(),
            title: chapter.title.clone(),
            chapter_order: chapter.chapter_order,
            act_id: chapter.act_id,
            incident_id: chapter.incident_id.clone(),
            plot_point_id: chapter.plot_point_id.clone(),
            summary: chapter.summary.clone(),
            goal: chapter.goal.clone(),
        }).collect()),
    });

    acts.push(ActProps {
        id: 2,
        summary: Some(String::new()),
        incidents: Some(book_incidents),
        plot_points: None,
        chapters: None,
    });

    acts.push(ActProps {
        id: 3,
        summary: Some(String::new()),
        incidents: None,
        plot_points: Some(book_plot_points),
        chapters: None,
    });

    acts.push(ActProps {
        id: 4,
        summary: Some(String::new()),
        incidents: None,
        plot_points: None,
        chapters: Some(act_chapters.iter().filter(|chapter| chapter.act_id == 4).map(|chapter| ActChapter {
            chapter_info_id: chapter.chapter_info_id,
            chapter_id: chapter.chapter_id.clone(),
            title: chapter.title.clone(),
            chapter_order: chapter.chapter_order,
            act_id: chapter.act_id,
            incident_id: chapter.incident_id.clone(),
            plot_point_id: chapter.plot_point_id.clone(),
            summary: chapter.summary.clone(),
            goal: chapter.goal.clone(),
        }).collect()),
    });

    acts.push(ActProps {
        id: 5,
        summary: Some(String::new()),
        incidents: None,
        plot_points: None,
        chapters: Some(act_chapters.iter().filter(|chapter| chapter.act_id == 5).map(|chapter| ActChapter {
            chapter_info_id: chapter.chapter_info_id,
            chapter_id: chapter.chapter_id.clone(),
            title: chapter.title.clone(),
            chapter_order: chapter.chapter_order,
            act_id: chapter.act_id,
            incident_id: chapter.incident_id.clone(),
            plot_point_id: chapter.plot_point_id.clone(),
            summary: chapter.summary.clone(),
            goal: chapter.goal.clone(),
        }).collect()),
    });

    if !act_queries.is_empty() {
        for act_query in &act_queries {
            let act_index: usize = (act_query.act_index - 1) as usize;
            if act_index < acts.len() {
                acts[act_index].summary = if !act_query.summary.is_empty() {
                    Some(decrypt_data_with_user_key(&act_query.summary, &user_encryption_key)?)
                } else {
                    Some(String::new())
                };
            }
        }
    }

    Ok(acts)
}

/// Updates multiple acts including their summaries, incidents, plot points, and chapter information.
/// Encrypts all sensitive data before storing in the database.
/// * `conn` - Database connection
/// * `acts` - Array of act properties to update
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book
/// * `user_key` - The user's encryption key for data encryption
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true when all updates are complete.
pub fn update_act(
    conn: &Connection, acts: &[ActProps], user_id: &str, book_id: &str, user_key: &str, lang: Lang,
) -> AppResult<bool> {
    for act in acts {
        let act_incidents: &[IncidentProps] = act.incidents.as_deref().unwrap_or(&[]);
        let act_id: i64 = act.id;

        if act_id == 1 || act_id == 4 || act_id == 5 {
            let encrypted_act_summary: String = if let Some(ref summary) = act.summary {
                if summary.is_empty() { String::new() } else { encrypt_data_with_user_key(summary, user_key)? }
            } else {
                String::new()
            };
            let update_result = repo::update_act_summary(conn, user_id, book_id, act_id, &encrypted_act_summary, timestamp_in_seconds(), lang);
            if update_result.is_err() {
                let new_act_summary_id: String = create_unique_id(None);
                repo::insert_act_summary(conn, &new_act_summary_id, user_id, book_id, act_id, &encrypted_act_summary, timestamp_in_seconds(), lang)?;
            }
            if let Some(ref chapters) = act.chapters {
                chapter_service::update_chapter_infos(conn, chapters, user_id, act_id, book_id, None, None, lang)?;
            }
        } else if act_id == 2 {
            for incident in act_incidents {
                let encrypted_incident_summary: String = if incident.summary.is_empty() { String::new() } else { encrypt_data_with_user_key(&incident.summary, user_key)? };
                let incident_id: &str = &incident.incident_id;
                let incident_title: &str = &incident.title;
                let hashed_incident_title: String = hash_element(incident_title);
                let encrypted_incident_title: String = encrypt_data_with_user_key(incident_title, user_key)?;
                incident_repo::update_incident(conn, user_id, book_id, incident_id, &encrypted_incident_title, &hashed_incident_title, &encrypted_incident_summary, timestamp_in_seconds(), lang)?;
                if let Some(ref chapters) = incident.chapters {
                    chapter_service::update_chapter_infos(conn, chapters, user_id, act_id, book_id, Some(incident_id), None, lang)?;
                }
            }
        } else {
            let act_plot_points: &[PlotPointProps] = act.plot_points.as_deref().unwrap_or(&[]);
            for plot_point in act_plot_points {
                let encrypted_plot_point_summary: String = if plot_point.summary.is_empty() { String::new() } else { encrypt_data_with_user_key(&plot_point.summary, user_key)? };
                let plot_point_id: &str = &plot_point.plot_point_id;
                let plot_point_title: &str = &plot_point.title;
                let hashed_plot_point_title: String = hash_element(plot_point_title);
                let encrypted_plot_point_title: String = encrypt_data_with_user_key(plot_point_title, user_key)?;
                plotpoint_repo::update_plot_point(conn, user_id, book_id, plot_point_id, &encrypted_plot_point_title, &hashed_plot_point_title, &encrypted_plot_point_summary, timestamp_in_seconds(), lang)?;
                if let Some(ref chapters) = plot_point.chapters {
                    chapter_service::update_chapter_infos(conn, chapters, user_id, act_id, book_id, None, Some(plot_point_id), lang)?;
                }
            }
        }
    }

    Ok(true)
}

/// Updates the story structure including acts and main chapters.
/// Encrypts chapter titles and updates their order in the database.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book
/// * `acts` - Array of act properties to update
/// * `main_chapters` - Array of main chapter properties to update
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true when all updates are complete.
pub fn update_story(
    conn: &Connection, user_id: &str, book_id: &str, acts: &[ActProps], main_chapters: &[ChapterProps], lang: Lang,
) -> AppResult<bool> {
    let user_encryption_key: String = get_user_encryption_key(user_id)?;
    update_act(conn, acts, user_id, book_id, &user_encryption_key, lang)?;

    for chapter in main_chapters {
        let chapter_id: &str = &chapter.chapter_id;
        let chapter_title: &str = &chapter.title;
        let chapter_order: i64 = chapter.chapter_order;
        update_chapter(conn, user_id, chapter_id, chapter_title, chapter_order, lang)?;
    }

    Ok(true)
}
