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

use crate::db::connection::DbManager;
use crate::domains::series::service;
use crate::error::AppError;
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))
}

#[tauri::command]
pub fn get_series_list(db: State<DbManager>, session: State<SessionState>) -> Result<Vec<service::SeriesListItemProps>, 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_series_list(conn, &user_id, lang)
}

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

#[tauri::command]
pub fn get_series_detail(data: GetSeriesDetailData, db: State<DbManager>, session: State<SessionState>) -> Result<service::SeriesDetailProps, 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_series_detail(conn, &user_id, &data.series_id, lang)
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateSeriesData {
    pub name: String,
    pub description: String,
    pub book_ids: Option<Vec<String>>,
}

#[tauri::command]
pub fn create_series(data: CreateSeriesData, 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::create_series(conn, &user_id, &data.name, &data.description, lang, data.book_ids.as_deref())
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateSeriesData {
    pub series_id: String,
    pub name: String,
    pub description: String,
}

#[tauri::command]
pub fn update_series(data: UpdateSeriesData, 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_series(conn, &user_id, &data.series_id, &data.name, &data.description, lang)
}

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

#[tauri::command]
pub fn delete_series(data: DeleteSeriesData, 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::delete_series(conn, &user_id, &data.series_id, data.deleted_at, lang)
}

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

#[tauri::command]
pub fn get_series_books(data: GetSeriesBooksData, db: State<DbManager>, session: State<SessionState>) -> Result<Vec<service::SeriesBookProps>, 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_series_books(conn, &user_id, &data.series_id, lang)
}

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

#[tauri::command]
pub fn add_book_to_series(data: AddBookToSeriesData, 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 books = service::get_series_books(conn, &user_id, &data.series_id, lang)?;
    let next_order = books.len() as i64 + 1;
    service::add_book_to_series(conn, &user_id, &data.series_id, &data.book_id, next_order, lang)
}

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

#[tauri::command]
pub fn remove_book_from_series(data: RemoveBookFromSeriesData, 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_book_from_series(conn, &user_id, &data.series_id, &data.book_id, data.deleted_at, lang)
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReorderSeriesBooksData {
    pub series_id: String,
    pub book_ids: Vec<String>,
}

#[tauri::command]
pub fn reorder_series_books(data: ReorderSeriesBooksData, 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 books_order: Vec<service::BooksOrderPost> = data.book_ids.into_iter().enumerate().map(|(index, book_id)| service::BooksOrderPost {
        book_id,
        order: (index + 1) as i64,
    }).collect();
    service::update_books_order(conn, &user_id, &data.series_id, &books_order, lang)
}

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

#[tauri::command]
pub fn get_series_for_book(data: GetSeriesForBookData, db: State<DbManager>, session: State<SessionState>) -> Result<Option<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_series_id_for_book(conn, &data.book_id, lang)
}
