use std::io::Cursor;

use docx_rs::{
    AlignmentType as DocxAlignmentType, Docx, Paragraph as DocxParagraph, Run,
};
use epub_builder::{EpubBuilder, EpubContent, ReferenceType, ZipLibrary};
use printpdf::{Mm, PdfDocument};
use serde_json::Value;

use crate::domains::book::service::{CompleteBookData, CompleteChapterContent};
use crate::domains::chapter::service::{get_chapters_or_sheet, tip_tap_to_html, ChapterContentData};
use crate::error::{AppError, AppResult};
use crate::shared::types::Lang;

pub const MAIN_STYLE: &str = r#"h1 {
  font-size: 24px !important;
  font-weight: bold !important;
  text-indent: 24px !important;
}
p {
  text-indent: 30px !important;
  margin-top: 0.7em !important;
  margin-bottom: 0.7em !important;
  text-align: justify !important;
}"#;

pub struct ExportResult {
    pub buffer: Vec<u8>,
}

/// Transforms book data into a DOCX document.
/// * `book_data` - The complete book data to export
/// Returns the DOCX buffer and filename.
pub fn transform_to_docx(book_data: &CompleteBookData) -> AppResult<ExportResult> {
    let book_title: &str = &book_data.title;

    let mut docx: Docx = Docx::new();

    docx = docx.add_paragraph(
        DocxParagraph::new()
            .add_run(Run::new().add_text(book_title).bold().size(48))
            .align(DocxAlignmentType::Center)
    );

    if !book_data.sub_title.is_empty() {
        docx = docx.add_paragraph(
            DocxParagraph::new()
                .add_run(Run::new().add_text(&book_data.sub_title).italic().size(32))
                .align(DocxAlignmentType::Center)
        );
    }

    if !book_data.summary.is_empty() {
        docx = docx.add_paragraph(
            DocxParagraph::new()
                .add_run(Run::new().add_text(&book_data.summary).italic().size(24))
                .align(DocxAlignmentType::Both)
        );
    }

    let chapters: Vec<ChapterContentData> = get_chapters_or_sheet(&book_data.chapters);

    for chapter in &chapters {
        if chapter.content.is_empty() {
            continue;
        }

        docx = docx.add_paragraph(
            DocxParagraph::new()
                .add_run(Run::new().add_text(&chapter.title).bold().size(32))
                .align(DocxAlignmentType::Center)
                .page_break_before(true)
        );

        let paragraphs: Vec<&str> = chapter.content.split('\n').collect();

        for paragraph in paragraphs {
            if paragraph.trim().is_empty() {
                continue;
            }
            docx = docx.add_paragraph(
                DocxParagraph::new()
                    .add_run(Run::new().add_text(paragraph).size(24))
                    .align(DocxAlignmentType::Both)
            );
        }
    }

    let mut buffer: Vec<u8> = Vec::new();
    docx.build()
        .pack(&mut Cursor::new(&mut buffer))
        .map_err(|error| AppError::Internal(format!("DOCX generation failed: {}", error)))?;

    Ok(ExportResult { buffer })
}

/// Transforms book data into a PDF document.
/// * `book_data` - The complete book data to export
/// Returns the PDF buffer and filename.
pub fn transform_to_pdf(book_data: &CompleteBookData) -> AppResult<ExportResult> {
    let book_title: &str = &book_data.title;

    let (pdf_document, page_index, layer_index) = PdfDocument::new(book_title, Mm(210.0), Mm(297.0), "Title Page");
    let font = pdf_document
        .add_builtin_font(printpdf::BuiltinFont::Helvetica)
        .map_err(|error| AppError::Internal(format!("PDF font error: {}", error)))?;

    let current_layer = pdf_document.get_page(page_index).get_layer(layer_index);
    let mut current_y: f32 = 270.0;

    current_layer.use_text(book_title, 20.0, Mm(20.0), Mm(current_y), &font);
    current_y -= 10.0;

    if !book_data.sub_title.is_empty() && book_data.sub_title.trim() != "" {
        current_layer.use_text(&book_data.sub_title, 16.0, Mm(20.0), Mm(current_y), &font);
        current_y -= 10.0;
    }

    if !book_data.summary.is_empty() && book_data.summary.trim() != "" {
        current_layer.use_text(&book_data.summary, 12.0, Mm(20.0), Mm(current_y), &font);
    }

    let chapters: Vec<ChapterContentData> = get_chapters_or_sheet(&book_data.chapters);

    for chapter in &chapters {
        if chapter.content.is_empty() {
            continue;
        }

        let (first_page_index, first_layer_index) = pdf_document.add_page(Mm(210.0), Mm(297.0), &chapter.title);
        let mut current_layer = pdf_document.get_page(first_page_index).get_layer(first_layer_index);
        let mut chapter_y: f32 = 270.0;

        current_layer.use_text(&chapter.title, 16.0, Mm(20.0), Mm(chapter_y), &font);
        chapter_y -= 10.0;

        let lines: Vec<&str> = chapter.content.split('\n').collect();
        for line in lines {
            if chapter_y < 20.0 {
                let (next_page_index, next_layer_index) = pdf_document.add_page(Mm(210.0), Mm(297.0), &chapter.title);
                current_layer = pdf_document.get_page(next_page_index).get_layer(next_layer_index);
                chapter_y = 270.0;
            }
            current_layer.use_text(line, 12.0, Mm(20.0), Mm(chapter_y), &font);
            chapter_y -= 6.0;
        }
    }

    let buffer: Vec<u8> = pdf_document
        .save_to_bytes()
        .map_err(|error| AppError::Internal(format!("PDF generation failed: {}", error)))?;

    Ok(ExportResult { buffer })
}

/// Transforms book data into an EPUB document.
/// * `book_data` - The complete book data to export
/// Returns the EPUB buffer and filename.
pub fn transform_to_epub(book_data: &CompleteBookData, lang: Lang) -> AppResult<ExportResult> {
    let book_title: &str = &book_data.title;
    let book_id: &str = &book_data.book_id;

    let zip_library = ZipLibrary::new().map_err(|error| AppError::Internal(format!("EPUB zip error: {}", error)))?;
    let mut epub_builder: EpubBuilder<ZipLibrary> = EpubBuilder::new(zip_library)
        .map_err(|error| AppError::Internal(format!("EPUB builder error: {}", error)))?;

    let full_title: String = if book_data.sub_title.is_empty() {
        book_title.to_string()
    } else {
        format!("{} - {}", book_title, &book_data.sub_title)
    };

    epub_builder
        .metadata("title", &full_title)
        .map_err(|error| AppError::Internal(format!("EPUB metadata error: {}", error)))?;
    epub_builder
        .metadata("language", "fr")
        .map_err(|error| AppError::Internal(format!("EPUB metadata error: {}", error)))?;
    epub_builder
        .metadata("identifier", &format!("urn:uuid:{}", book_id))
        .map_err(|error| AppError::Internal(format!("EPUB metadata error: {}", error)))?;
    if book_data.user_infos.author_name.is_empty() {
        return Err(AppError::Validation(if lang == Lang::Fr { "Veuillez définir votre nom d'auteur avant d'exporter.".to_string() } else { "Please set your author name before exporting.".to_string() }));
    }
    epub_builder
        .metadata("author", &book_data.user_infos.author_name)
        .map_err(|error| AppError::Internal(format!("EPUB metadata error: {}", error)))?;
    epub_builder
        .metadata("publisher", "ERitors Scribe")
        .map_err(|error| AppError::Internal(format!("EPUB metadata error: {}", error)))?;

    epub_builder
        .stylesheet(MAIN_STYLE.as_bytes())
        .map_err(|error| AppError::Internal(format!("EPUB stylesheet error: {}", error)))?;

    let has_regular_chapters: bool = book_data.chapters.iter().any(|chapter| chapter.order > 0);
    let chapters_to_export: Vec<&CompleteChapterContent> = if has_regular_chapters {
        book_data.chapters.iter().filter(|chapter| chapter.order > 0).collect()
    } else {
        book_data.chapters.iter().filter(|chapter| chapter.order == -1).collect()
    };

    for chapter in &chapters_to_export {
        if chapter.content.is_empty() {
            continue;
        }

        let chapter_index: String = format!("chapter{}", chapter.order);
        let parsed_content: Value = serde_json::from_str(&chapter.content)
            .map_err(|error| AppError::Internal(format!("JSON parse error: {}", error)))?;
        let html_content: String = tip_tap_to_html(&parsed_content);

        let xhtml_page: String = format!(
            r#"<?xml version="1.0" encoding="utf-8"?>
            <html xmlns="http://www.w3.org/1999/xhtml">
              <head>
                <title>{}</title>
                <link rel="stylesheet" type="text/css" href="styles.css"/>
              </head>
              <body>
                {}
              </body>
            </html>"#,
            &chapter.title, html_content
        );

        epub_builder
            .add_content(
                EpubContent::new(format!("{}.xhtml", chapter_index), xhtml_page.as_bytes())
                    .title(&chapter.title)
                    .reftype(ReferenceType::Text),
            )
            .map_err(|error| AppError::Internal(format!("EPUB content error: {}", error)))?;
    }

    if !book_data.cover_image.is_empty() {
        let image_buffer: Vec<u8> = base64::Engine::decode(
            &base64::engine::general_purpose::STANDARD,
            &book_data.cover_image,
        )
        .map_err(|error| AppError::Internal(format!("Base64 decode error: {}", error)))?;

        epub_builder
            .add_cover_image("cover.jpg", image_buffer.as_slice(), "image/jpeg")
            .map_err(|error| AppError::Internal(format!("EPUB cover error: {}", error)))?;
    }

    let mut epub_buffer: Vec<u8> = Vec::new();
    epub_builder
        .generate(&mut epub_buffer)
        .map_err(|error| AppError::Internal(format!("EPUB generation failed: {}", error)))?;

    Ok(ExportResult { buffer: epub_buffer })
}
