mod config;
pub use config::*;
mod style;
pub use style::*;
mod nodes;
pub use nodes::*;
mod builder;
pub use builder::*;

use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::sync::Arc;

#[derive(Debug, Clone)]
pub struct PageBreakInfo {
    pub page_number: usize,
    pub total_pages: usize,
    pub node_text: Option<String>,
    pub is_stack: bool,
}

pub struct PageBreakCallback(pub Arc<dyn Fn(&PageBreakInfo) -> bool + Send + Sync>);

impl std::fmt::Debug for PageBreakCallback {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("PageBreakCallback").field(&"<closure>").finish()
    }
}

impl Clone for PageBreakCallback {
    fn clone(&self) -> Self {
        Self(Arc::clone(&self.0))
    }
}

impl PartialEq for PageBreakCallback {
    fn eq(&self, _other: &Self) -> bool {
        false
    }
}

impl Serialize for PageBreakCallback {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_none()
    }
}

impl<'de> Deserialize<'de> for PageBreakCallback {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let _ = Option::<()>::deserialize(deserializer)?;
        Err(serde::de::Error::custom("cannot deserialize PageBreakCallback"))
    }
}

pub struct RenderOptions {
    pub compression_level: CompressionLevel,
    pub progress_callback: Option<Arc<dyn Fn(f32) + Send + Sync>>,
}

impl std::fmt::Debug for RenderOptions {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("RenderOptions")
            .field("compression_level", &self.compression_level)
            .field("progress_callback", &self.progress_callback.as_ref().map(|_| "<closure>"))
            .finish()
    }
}

impl Clone for RenderOptions {
    fn clone(&self) -> Self {
        Self {
            compression_level: self.compression_level,
            progress_callback: self.progress_callback.as_ref().map(Arc::clone),
        }
    }
}

impl Default for RenderOptions {
    fn default() -> Self {
        Self { compression_level: CompressionLevel::Default, progress_callback: None }
    }
}

impl RenderOptions {
    pub fn new() -> Self { Self::default() }
    pub fn compression_level(mut self, level: CompressionLevel) -> Self {
        self.compression_level = level;
        self
    }
    pub fn progress_callback(mut self, callback: Arc<dyn Fn(f32) + Send + Sync>) -> Self {
        self.progress_callback = Some(callback);
        self
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FontSource {
    pub path: String,
}

impl FontSource {
    pub fn path(path: impl Into<String>) -> Self {
        Self { path: path.into() }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum WatermarkFontSize {
    Fixed(f32),
    Auto,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WatermarkConfig {
    pub text: String,
    pub font_family: Option<String>,
    pub font_size: Option<WatermarkFontSize>,
    pub color: Option<String>,
    pub opacity: Option<f32>,
    pub angle: Option<f32>,
}

impl WatermarkConfig {
    pub fn new(text: impl Into<String>) -> Self {
        Self { text: text.into(), font_family: None, font_size: None, color: None, opacity: None, angle: None }
    }

    pub fn font_family(mut self, value: impl Into<String>) -> Self { self.font_family = Some(value.into()); self }
    pub fn font_size(mut self, value: f32) -> Self { self.font_size = Some(WatermarkFontSize::Fixed(value)); self }
    pub fn auto_font_size(mut self) -> Self { self.font_size = Some(WatermarkFontSize::Auto); self }
    pub fn color(mut self, value: impl Into<String>) -> Self { self.color = Some(value.into()); self }
    pub fn opacity(mut self, value: f32) -> Self { self.opacity = Some(value); self }
    pub fn angle(mut self, value: f32) -> Self { self.angle = Some(value); self }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct TableLayoutConfig {
    pub h_line_width: Option<f32>,
    pub v_line_width: Option<f32>,
    pub h_line_color: Option<String>,
    pub v_line_color: Option<String>,
    pub padding_left: Option<f32>,
    pub padding_right: Option<f32>,
    pub padding_top: Option<f32>,
    pub padding_bottom: Option<f32>,
}

impl TableLayoutConfig {
    pub fn new() -> Self { Self::default() }
    pub fn h_line_width(mut self, value: f32) -> Self { self.h_line_width = Some(value); self }
    pub fn v_line_width(mut self, value: f32) -> Self { self.v_line_width = Some(value); self }
    pub fn h_line_color(mut self, value: impl Into<String>) -> Self { self.h_line_color = Some(value.into()); self }
    pub fn v_line_color(mut self, value: impl Into<String>) -> Self { self.v_line_color = Some(value.into()); self }
    pub fn padding_left(mut self, value: f32) -> Self { self.padding_left = Some(value); self }
    pub fn padding_right(mut self, value: f32) -> Self { self.padding_right = Some(value); self }
    pub fn padding_top(mut self, value: f32) -> Self { self.padding_top = Some(value); self }
    pub fn padding_bottom(mut self, value: f32) -> Self { self.padding_bottom = Some(value); self }
}

pub enum HeaderFooter {
    Static(DocumentNode),
    Dynamic(Arc<dyn Fn(u32, u32, &PageSize) -> DocumentNode + Send + Sync>),
}

impl std::fmt::Debug for HeaderFooter {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Static(node) => f.debug_tuple("Static").field(node).finish(),
            Self::Dynamic(_) => f.debug_tuple("Dynamic").field(&"<closure>").finish(),
        }
    }
}

impl Clone for HeaderFooter {
    fn clone(&self) -> Self {
        match self {
            Self::Static(node) => Self::Static(node.clone()),
            Self::Dynamic(arc) => Self::Dynamic(Arc::clone(arc)),
        }
    }
}

impl PartialEq for HeaderFooter {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Self::Static(a), Self::Static(b)) => a == b,
            _ => false,
        }
    }
}

impl Serialize for HeaderFooter {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match self {
            Self::Static(node) => node.serialize(serializer),
            Self::Dynamic(_) => Err(serde::ser::Error::custom("cannot serialize dynamic HeaderFooter")),
        }
    }
}

impl<'de> Deserialize<'de> for HeaderFooter {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let node = DocumentNode::deserialize(deserializer)?;
        Ok(Self::Static(node))
    }
}

impl HeaderFooter {
    pub fn from_static(node: DocumentNode) -> Self {
        Self::Static(node)
    }

    pub fn from_fn<F>(f: F) -> Self
    where
        F: Fn(u32, u32, &PageSize) -> DocumentNode + Send + Sync + 'static,
    {
        Self::Dynamic(Arc::new(f))
    }

    pub fn resolve(&self, current_page: u32, total_pages: u32, page_size: &PageSize) -> DocumentNode {
        match self {
            Self::Static(node) => node.clone(),
            Self::Dynamic(f) => f(current_page, total_pages, page_size),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct DocumentInfo {
    pub title: Option<String>,
    pub author: Option<String>,
    pub subject: Option<String>,
    pub keywords: Option<String>,
    pub creator: Option<String>,
    pub producer: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PdfEncryption {
    pub user_password: Option<String>,
    pub owner_password: Option<String>,
    pub allow_print: bool,
    pub allow_copy: bool,
    pub allow_modify: bool,
}

impl Default for PdfEncryption {
    fn default() -> Self {
        Self {
            user_password: None,
            owner_password: None,
            allow_print: true,
            allow_copy: true,
            allow_modify: true,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OutlineEntry {
    pub id: String,
    pub text: String,
    pub parent_id: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Attachment {
    pub name: String,
    pub data: Vec<u8>,
    pub mime_type: String,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Document {
    pub content: Vec<DocumentNode>,
    pub page_size: PageSize,
    pub page_margins: Margins,
    pub default_style: Style,
    pub styles: BTreeMap<String, Style>,
    pub fonts: BTreeMap<String, FontSource>,
    pub watermark: Option<WatermarkConfig>,
    pub header: Option<HeaderFooter>,
    pub footer: Option<HeaderFooter>,
    pub background: Option<DocumentNode>,
    pub info: Option<DocumentInfo>,
    pub encryption: Option<PdfEncryption>,
    pub pdf_version: Option<String>,
    pub compress: Option<bool>,
    pub language: Option<String>,
    pub max_pages: Option<usize>,
    pub tagged: Option<bool>,
    pub named_dests: Vec<NamedDest>,
    pub outlines: Vec<OutlineEntry>,
    pub attachments: Vec<Attachment>,
    #[serde(skip)]
    pub page_break_before: Option<PageBreakCallback>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NamedDest {
    pub id: String,
    pub page_index: usize,
    pub x: f32,
    pub y: f32,
}

impl Default for Document {
    fn default() -> Self {
        Self {
            content: Vec::new(),
            page_size: PageSize::a4(),
            page_margins: Margins::all(40.0),
            default_style: Style::default(),
            styles: BTreeMap::new(),
            fonts: BTreeMap::new(),
            watermark: None,
            header: None,
            footer: None,
            background: None,
            info: None,
            encryption: None,
            pdf_version: None,
            compress: None,
            language: None,
            max_pages: None,
            tagged: None,
            named_dests: Vec::new(),
            outlines: Vec::new(),
            attachments: Vec::new(),
            page_break_before: None,
        }
    }
}

impl Document {
    pub fn builder() -> DocumentBuilder {
        DocumentBuilder { document: Self::default() }
    }
}



