use crate::ids::{AnnotationId, ReferenceId};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

/// A point in PDF coordinate space.
/// Origin is bottom-left; units are points (1/72 inch), matching ISO 32000.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

/// A rectangle in PDF coordinate space.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Rect {
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64,
}

/// A quadrilateral for text markup annotations (highlight, underline, etc.).
/// Four points define one region, typically one line of text.
/// Matches the PDF spec QuadPoints representation (4 vertices per quad).
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Quad {
    pub points: [Point; 4],
}

/// RGBA color.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Color {
    pub r: u8,
    pub g: u8,
    pub b: u8,
    pub a: u8,
}

impl Color {
    pub const YELLOW: Color = Color { r: 255, g: 255, b: 0, a: 128 };
    pub const RED: Color = Color { r: 255, g: 0, b: 0, a: 128 };
    pub const GREEN: Color = Color { r: 0, g: 255, b: 0, a: 128 };
}

/// The four text markup annotation types defined in ISO 32000.
/// All share the same QuadPoints-based geometry.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TextMarkupType {
    Highlight,
    Underline,
    Squiggly,
    StrikeOut,
}

/// The kind of annotation and its type-specific geometry/data.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum AnnotationType {
    /// Text markup (highlight, underline, squiggly, strikeout).
    /// Uses QuadPoints per PDF spec for precise multi-line region selection.
    TextMarkup {
        markup_type: TextMarkupType,
        quads: Vec<Quad>,
        color: Color,
        /// The selected text, stored for search and export without re-reading the PDF.
        selected_text: Option<String>,
    },
    /// Sticky note (popup comment).
    Note { position: Point },
    /// Inline text box.
    FreeText { rect: Rect },
    /// Freehand ink drawing (e.g., circling a diagram).
    Ink {
        /// Multiple strokes, each a sequence of connected points.
        paths: Vec<Vec<Point>>,
        color: Color,
        /// Stroke width in points.
        width: f64,
    },
    /// Area/image selection for extracting figures from PDFs.
    Area { rect: Rect },
}

/// A single annotation on a PDF page.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Annotation {
    pub id: AnnotationId,
    pub reference_id: ReferenceId,
    /// 0-indexed physical page number.
    pub page: u32,
    /// Display page label (e.g., "iv", "23") — may differ from the physical page index.
    pub page_label: Option<String>,
    pub annotation_type: AnnotationType,
    /// Free-form text: note body, comment on a highlight, etc.
    pub content: Option<String>,
    pub created_at: DateTime<Utc>,
    pub modified_at: DateTime<Utc>,
}

impl Annotation {
    pub fn new(reference_id: ReferenceId, page: u32, annotation_type: AnnotationType) -> Self {
        let now = Utc::now();
        Self {
            id: AnnotationId::new(),
            reference_id,
            page,
            page_label: None,
            annotation_type,
            content: None,
            created_at: now,
            modified_at: now,
        }
    }
}

/// All annotations for a single reference, stored as one file.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AnnotationSet {
    pub reference_id: ReferenceId,
    pub annotations: Vec<Annotation>,
}

impl AnnotationSet {
    pub fn new(reference_id: ReferenceId) -> Self {
        Self {
            reference_id,
            annotations: Vec::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn make_highlight() -> AnnotationType {
        AnnotationType::TextMarkup {
            markup_type: TextMarkupType::Highlight,
            quads: vec![Quad {
                points: [
                    Point { x: 10.0, y: 20.0 },
                    Point { x: 100.0, y: 20.0 },
                    Point { x: 10.0, y: 30.0 },
                    Point { x: 100.0, y: 30.0 },
                ],
            }],
            color: Color::YELLOW,
            selected_text: Some("important text".into()),
        }
    }

    #[test]
    fn annotation_serde_round_trip_highlight() {
        let ref_id = ReferenceId::new();
        let set = AnnotationSet {
            reference_id: ref_id,
            annotations: vec![Annotation::new(ref_id, 3, make_highlight())],
        };

        let json = serde_json::to_string(&set).expect("serialize");
        let set2: AnnotationSet = serde_json::from_str(&json).expect("deserialize");

        assert_eq!(set.reference_id, set2.reference_id);
        assert_eq!(set.annotations.len(), set2.annotations.len());
        assert_eq!(set.annotations[0].page, set2.annotations[0].page);
    }

    #[test]
    fn annotation_serde_round_trip_ink() {
        let ref_id = ReferenceId::new();
        let ink = AnnotationType::Ink {
            paths: vec![vec![Point { x: 0.0, y: 0.0 }, Point { x: 10.0, y: 10.0 }]],
            color: Color::RED,
            width: 2.0,
        };
        let set = AnnotationSet {
            reference_id: ref_id,
            annotations: vec![Annotation::new(ref_id, 0, ink)],
        };

        let json = serde_json::to_string(&set).expect("serialize");
        let set2: AnnotationSet = serde_json::from_str(&json).expect("deserialize");
        assert_eq!(set, set2);
    }

    #[test]
    fn all_markup_types_serialize() {
        let ref_id = ReferenceId::new();
        for markup_type in [
            TextMarkupType::Highlight,
            TextMarkupType::Underline,
            TextMarkupType::Squiggly,
            TextMarkupType::StrikeOut,
        ] {
            let ann = Annotation::new(
                ref_id,
                0,
                AnnotationType::TextMarkup {
                    markup_type,
                    quads: vec![],
                    color: Color::GREEN,
                    selected_text: None,
                },
            );
            let set = AnnotationSet {
                reference_id: ref_id,
                annotations: vec![ann],
            };
            let json = serde_json::to_string(&set).expect("serialize");
            let _: AnnotationSet = serde_json::from_str(&json).expect("deserialize");
        }
    }
}
