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

use crate::crypto::encryption::{decrypt_data_with_user_key, encrypt_data_with_user_key, hash_element};
use crate::crypto::key_manager::get_user_encryption_key;
use crate::domains::act::service::ActChapter;
use crate::domains::plotpoint::repo;
use crate::domains::tombstone::repo as tombstone_repo;
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 PlotPointProps {
    pub plot_point_id: String,
    pub title: String,
    pub summary: String,
    pub linked_incident_id: Option<String>,
    pub chapters: Option<Vec<ActChapter>>,
}

/// Retrieves all plot points for a specific book with their associated chapters.
/// Decrypts plot point titles and 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
/// * `act_chapters` - Array of act chapters to associate with plot points
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns a list of plot point properties with their associated chapters.
/// Errors if the plot points cannot be retrieved or decrypted.
pub fn get_plot_points(
    conn: &Connection, user_id: &str, book_id: &str,
    act_chapters: &[ActChapter], lang: Lang,
) -> AppResult<Vec<PlotPointProps>> {
    let plot_point_query_results: Vec<repo::PlotPointQuery> = repo::fetch_all_plot_points(conn, user_id, book_id, lang)?;
    let user_encryption_key: String = get_user_encryption_key(user_id)?;
    let mut plot_points: Vec<PlotPointProps> = Vec::new();

    if !plot_point_query_results.is_empty() {
        for plot_point_row in &plot_point_query_results {
            let mut associated_chapters: Vec<ActChapter> = Vec::new();

            for chapter in act_chapters {
                if chapter.plot_point_id.as_deref() == Some(&plot_point_row.plot_point_id) {
                    associated_chapters.push(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(),
                    });
                }
            }

            let decrypted_title: String = if !plot_point_row.title.is_empty() { decrypt_data_with_user_key(&plot_point_row.title, &user_encryption_key)? } else { String::new() };
            let decrypted_summary: String = if !plot_point_row.summary.is_empty() { decrypt_data_with_user_key(&plot_point_row.summary, &user_encryption_key)? } else { String::new() };

            plot_points.push(PlotPointProps {
                plot_point_id: plot_point_row.plot_point_id.clone(),
                title: decrypted_title,
                summary: decrypted_summary,
                linked_incident_id: plot_point_row.linked_incident_id.clone(),
                chapters: Some(associated_chapters),
            });
        }
    }

    Ok(plot_points)
}

/// Creates a new plot point for a book, encrypting the name before storage.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book
/// * `incident_id` - The identifier of the linked incident
/// * `name` - The name/title of the plot point
/// * `lang` - The language for error messages ("fr" or "en")
/// * `existing_plot_point_id` - Optional existing plot point ID to use instead of generating a new one
/// Returns the unique identifier of the created plot point.
/// Errors if the plot point already exists or cannot be added.
pub fn add_new_plot_point(
    conn: &Connection, user_id: &str, book_id: &str, incident_id: &str,
    name: &str, lang: Lang, existing_plot_point_id: Option<&str>,
) -> AppResult<String> {
    let user_encryption_key: String = get_user_encryption_key(user_id)?;
    let encrypted_name: String = encrypt_data_with_user_key(name, &user_encryption_key)?;
    let hashed_name: String = hash_element(name);
    let plot_point_id: String = create_unique_id(existing_plot_point_id);
    let last_update: i64 = timestamp_in_seconds();

    repo::insert_new_plot_point(conn, &plot_point_id, user_id, book_id, &encrypted_name, &hashed_name, incident_id, last_update, lang)
}

/// Removes a plot point from the database.
/// * `conn` - Database connection
/// * `user_id` - The unique identifier of the user
/// * `book_id` - The unique identifier of the book
/// * `plot_id` - The unique identifier of the plot point to remove
/// * `deleted_at` - The timestamp of deletion
/// * `lang` - The language for error messages ("fr" or "en")
/// Returns true if the plot point was successfully deleted, false otherwise.
/// Errors if the plot point cannot be deleted.
pub fn remove_plot_point(
    conn: &Connection, user_id: &str, book_id: &str, plot_id: &str,
    deleted_at: i64, lang: Lang,
) -> AppResult<bool> {
    let deleted: bool = repo::delete_plot_point(conn, user_id, plot_id, lang)?;
    if deleted {
        tombstone_repo::insert(conn, book_id, "book_plot_points", plot_id, None, user_id, deleted_at, lang)?;
    }
    Ok(deleted)
}
