//! Interactive PDF Features
//!
//! Advanced interactive capabilities:
//! - JavaScript actions
//! - Page transitions
//! - Multimedia (video/audio)
//! - 3D objects
//! - Complete annotation types

use super::error::{EnhancedError, Result};
use std::fs;
use std::path::Path;

/// JavaScript action types
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ActionType {
    /// Document-level action
    DocumentOpen,
    DocumentClose,
    DocumentSave,
    DocumentPrint,
    /// Page-level action
    PageOpen,
    PageClose,
    /// Field-level action
    FieldFocus,
    FieldBlur,
    FieldChange,
    FieldValidate,
    FieldCalculate,
    /// Mouse actions
    MouseEnter,
    MouseExit,
    MouseDown,
    MouseUp,
}

/// JavaScript action
#[derive(Debug, Clone)]
pub struct JavaScriptAction {
    pub action_type: ActionType,
    pub script: String,
}

impl JavaScriptAction {
    /// Create new JavaScript action
    pub fn new(action_type: ActionType, script: impl Into<String>) -> Self {
        Self {
            action_type,
            script: script.into(),
        }
    }
}

/// Add JavaScript action to PDF
pub fn add_javascript_action(
    pdf_path: &str,
    action: &JavaScriptAction,
    _target: Option<&str>,
) -> Result<()> {
    if !Path::new(pdf_path).exists() {
        return Err(EnhancedError::Io(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            format!("PDF file not found: {}", pdf_path),
        )));
    }

    let mut data = fs::read(pdf_path)?;
    if !data.starts_with(b"%PDF-") {
        return Err(EnhancedError::InvalidParameter(
            "Not a valid PDF file".into(),
        ));
    }

    let escaped_script = action
        .script
        .replace('\\', "\\\\")
        .replace('(', "\\(")
        .replace(')', "\\)");

    let max_obj = crate::enhanced::overlay::find_max_object_number(&data);
    let js_obj_num = max_obj + 1;
    let names_obj_num = max_obj + 2;

    let js_action = format!(
        "{} 0 obj\n<< /Type /Action /S /JavaScript /JS ({}) >>\nendobj\n",
        js_obj_num, escaped_script
    );

    let trigger_key = match action.action_type {
        ActionType::DocumentOpen => "/OpenAction",
        ActionType::DocumentClose => "/AA << /WC",
        ActionType::DocumentSave => "/AA << /WS",
        ActionType::DocumentPrint => "/AA << /WP",
        _ => "/OpenAction",
    };

    let content = String::from_utf8_lossy(&data).to_string();
    if let Some(catalog_pos) = content.find("/Type /Catalog") {
        if let Some(endobj_pos) = content[catalog_pos..].find(">>") {
            let insert_pos = catalog_pos + endobj_pos;
            let insert_str = format!(" {} {} 0 R", trigger_key, js_obj_num);
            let insert_bytes = insert_str.as_bytes();
            data.splice(insert_pos..insert_pos, insert_bytes.iter().copied());
        }
    }

    data.extend_from_slice(js_action.as_bytes());

    let catalog_obj_num = crate::enhanced::overlay::find_catalog_obj_num_from_bytes(&data);
    let obj_count = crate::enhanced::overlay::find_max_object_number(&data) + 1;
    crate::enhanced::overlay::rebuild_xref_and_trailer(&mut data, obj_count, catalog_obj_num)?;
    fs::write(pdf_path, &data)?;

    Ok(())
}

/// Page transition effect
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransitionEffect {
    /// No transition
    None,
    /// Split (in/out, horizontal/vertical)
    Split,
    /// Blinds (horizontal/vertical)
    Blinds,
    /// Box (in/out)
    Box,
    /// Wipe (left/right/up/down)
    Wipe,
    /// Dissolve
    Dissolve,
    /// Glitter (left/down/diagonal)
    Glitter,
    /// Replace
    Replace,
    /// Fly (in/out)
    Fly,
    /// Push (left/right/up/down)
    Push,
    /// Cover (left/right/up/down)
    Cover,
    /// Uncover (left/right/up/down)
    Uncover,
    /// Fade
    Fade,
}

/// Page transition configuration
#[derive(Debug, Clone)]
pub struct PageTransition {
    pub effect: TransitionEffect,
    pub duration: f32, // seconds
    pub direction: Option<TransitionDirection>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TransitionDirection {
    Horizontal,
    Vertical,
    Inward,
    Outward,
    LeftToRight,
    RightToLeft,
    TopToBottom,
    BottomToTop,
}

impl PageTransition {
    /// Create new transition
    pub fn new(effect: TransitionEffect, duration: f32) -> Self {
        Self {
            effect,
            duration,
            direction: None,
        }
    }

    /// Set direction
    pub fn with_direction(mut self, direction: TransitionDirection) -> Self {
        self.direction = Some(direction);
        self
    }
}

/// Add page transition
pub fn add_page_transition(pdf_path: &str, page: u32, transition: &PageTransition) -> Result<()> {
    if !Path::new(pdf_path).exists() {
        return Err(EnhancedError::Io(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            format!("PDF file not found: {}", pdf_path),
        )));
    }

    let mut data = fs::read(pdf_path)?;
    if !data.starts_with(b"%PDF-") {
        return Err(EnhancedError::InvalidParameter(
            "Not a valid PDF file".into(),
        ));
    }

    if super::page_ops::has_object_streams(&data) {
        data = super::page_ops::flatten_object_streams_to_vec(&data)?;
    }

    let page_obj_num = super::page_copy::find_page_object(&data, page as usize)?;
    let content = String::from_utf8_lossy(&data).to_string();
    let obj_header = format!("{} 0 obj", page_obj_num);

    let effect_name = match transition.effect {
        TransitionEffect::None => "R",
        TransitionEffect::Split => "Split",
        TransitionEffect::Blinds => "Blinds",
        TransitionEffect::Box => "Box",
        TransitionEffect::Wipe => "Wipe",
        TransitionEffect::Dissolve => "Dissolve",
        TransitionEffect::Glitter => "Glitter",
        TransitionEffect::Replace => "R",
        TransitionEffect::Fly => "Fly",
        TransitionEffect::Push => "Push",
        TransitionEffect::Cover => "Cover",
        TransitionEffect::Uncover => "Uncover",
        TransitionEffect::Fade => "Fade",
    };

    let trans_dict = format!(
        "/Trans << /Type /Trans /S /{} /D {:.1} >>",
        effect_name, transition.duration
    );

    if let Some(obj_pos) = content.find(&obj_header) {
        if let Some(enddict_pos) = content[obj_pos..].find(">>") {
            let insert_pos = obj_pos + enddict_pos;
            let insert_bytes = format!(" {}", trans_dict).into_bytes();
            data.splice(insert_pos..insert_pos, insert_bytes.into_iter());
        }
    }

    let catalog_obj_num = crate::enhanced::overlay::find_catalog_obj_num_from_bytes(&data);
    let obj_count = crate::enhanced::overlay::find_max_object_number(&data) + 1;
    crate::enhanced::overlay::rebuild_xref_and_trailer(&mut data, obj_count, catalog_obj_num)?;
    fs::write(pdf_path, &data)?;

    Ok(())
}

/// All 28 PDF annotation types
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AnnotationType {
    // Basic annotations (already have some)
    Text,
    Link,
    FreeText,

    // Shape annotations
    Line,
    Square,
    Circle,
    Polygon,
    PolyLine,

    // Markup annotations
    Highlight,
    Underline,
    Squiggly,
    StrikeOut,

    // Other annotations
    Stamp,
    Caret,
    Ink,
    Popup,
    FileAttachment,
    Sound,
    Movie,
    Screen,
    Widget,
    PrinterMark,
    TrapNet,
    Watermark,
    ThreeD,
    Redact,
    Projection,
    RichMedia,
}

/// Annotation configuration
#[derive(Debug, Clone)]
pub struct Annotation {
    pub annotation_type: AnnotationType,
    pub page: u32,
    pub rect: (f32, f32, f32, f32),
    pub contents: Option<String>,
    pub author: Option<String>,
    pub color: Option<(f32, f32, f32)>,
    pub opacity: f32,
}

impl Annotation {
    /// Create new annotation
    pub fn new(annotation_type: AnnotationType, page: u32, rect: (f32, f32, f32, f32)) -> Self {
        Self {
            annotation_type,
            page,
            rect,
            contents: None,
            author: None,
            color: None,
            opacity: 1.0,
        }
    }

    /// Set contents
    pub fn with_contents(mut self, contents: impl Into<String>) -> Self {
        self.contents = Some(contents.into());
        self
    }

    /// Set color
    pub fn with_color(mut self, r: f32, g: f32, b: f32) -> Self {
        self.color = Some((r, g, b));
        self
    }

    /// Set opacity
    pub fn with_opacity(mut self, opacity: f32) -> Self {
        self.opacity = opacity;
        self
    }
}

/// Add annotation to PDF
pub fn add_annotation(pdf_path: &str, annotation: &Annotation) -> Result<()> {
    if !Path::new(pdf_path).exists() {
        return Err(EnhancedError::Io(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            format!("PDF file not found: {}", pdf_path),
        )));
    }

    let mut data = fs::read(pdf_path)?;
    if !data.starts_with(b"%PDF-") {
        return Err(EnhancedError::InvalidParameter(
            "Not a valid PDF file".into(),
        ));
    }

    if super::page_ops::has_object_streams(&data) {
        data = super::page_ops::flatten_object_streams_to_vec(&data)?;
    }

    let max_obj = crate::enhanced::overlay::find_max_object_number(&data);
    let annot_obj_num = max_obj + 1;

    let subtype = match annotation.annotation_type {
        AnnotationType::Text => "Text",
        AnnotationType::Link => "Link",
        AnnotationType::FreeText => "FreeText",
        AnnotationType::Line => "Line",
        AnnotationType::Square => "Square",
        AnnotationType::Circle => "Circle",
        AnnotationType::Polygon => "Polygon",
        AnnotationType::PolyLine => "PolyLine",
        AnnotationType::Highlight => "Highlight",
        AnnotationType::Underline => "Underline",
        AnnotationType::Squiggly => "Squiggly",
        AnnotationType::StrikeOut => "StrikeOut",
        AnnotationType::Stamp => "Stamp",
        AnnotationType::Caret => "Caret",
        AnnotationType::Ink => "Ink",
        AnnotationType::Popup => "Popup",
        AnnotationType::FileAttachment => "FileAttachment",
        AnnotationType::Sound => "Sound",
        AnnotationType::Movie => "Movie",
        AnnotationType::Screen => "Screen",
        AnnotationType::Widget => "Widget",
        AnnotationType::PrinterMark => "PrinterMark",
        AnnotationType::TrapNet => "TrapNet",
        AnnotationType::Watermark => "Watermark",
        AnnotationType::ThreeD => "3D",
        AnnotationType::Redact => "Redact",
        AnnotationType::Projection => "Projection",
        AnnotationType::RichMedia => "RichMedia",
    };

    let (x1, y1, x2, y2) = annotation.rect;
    let mut annot_dict = format!(
        "{} 0 obj\n<< /Type /Annot /Subtype /{} /Rect [{:.2} {:.2} {:.2} {:.2}]",
        annot_obj_num, subtype, x1, y1, x2, y2
    );

    if let Some(ref contents) = annotation.contents {
        let escaped = contents
            .replace('\\', "\\\\")
            .replace('(', "\\(")
            .replace(')', "\\)");
        annot_dict.push_str(&format!(" /Contents ({})", escaped));
    }

    if let Some((r, g, b)) = annotation.color {
        annot_dict.push_str(&format!(" /C [{:.2} {:.2} {:.2}]", r, g, b));
    }

    if annotation.opacity < 1.0 {
        annot_dict.push_str(&format!(" /CA {:.2}", annotation.opacity));
    }

    annot_dict.push_str(" >>\nendobj\n");

    let page_obj_num = super::page_copy::find_page_object(&data, annotation.page as usize)?;
    let content = String::from_utf8_lossy(&data).to_string();
    let obj_header = format!("{} 0 obj", page_obj_num);

    if let Some(obj_pos) = content.find(&obj_header) {
        let after_obj = &content[obj_pos..];
        if let Some(annots_pos) = after_obj.find("/Annots") {
            if let Some(bracket_pos) = after_obj[annots_pos..].find(']') {
                let insert_pos = obj_pos + annots_pos + bracket_pos;
                let ref_str = format!(" {} 0 R", annot_obj_num);
                data.splice(insert_pos..insert_pos, ref_str.bytes());
            }
        } else if let Some(enddict_pos) = after_obj.find(">>") {
            let insert_pos = obj_pos + enddict_pos;
            let annots_str = format!(" /Annots [{} 0 R]", annot_obj_num);
            data.splice(insert_pos..insert_pos, annots_str.bytes());
        }
    }

    data.extend_from_slice(annot_dict.as_bytes());

    let catalog_obj_num = crate::enhanced::overlay::find_catalog_obj_num_from_bytes(&data);
    let obj_count = crate::enhanced::overlay::find_max_object_number(&data) + 1;
    crate::enhanced::overlay::rebuild_xref_and_trailer(&mut data, obj_count, catalog_obj_num)?;
    fs::write(pdf_path, &data)?;

    Ok(())
}

/// Multimedia configuration
#[derive(Debug, Clone)]
pub struct Multimedia {
    pub media_type: MediaType,
    pub file_path: String,
    pub page: u32,
    pub rect: (f32, f32, f32, f32),
    pub autoplay: bool,
    pub controls: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MediaType {
    Video,
    Audio,
    ThreeD,
}

/// Embed multimedia in PDF
///
/// Multimedia embedding requires rendition dictionaries and media clip support
/// which depend on PDF viewer capabilities. Currently unsupported.
pub fn embed_multimedia(pdf_path: &str, multimedia: &Multimedia) -> Result<()> {
    if !Path::new(pdf_path).exists() {
        return Err(EnhancedError::Io(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            format!("PDF file not found: {}", pdf_path),
        )));
    }

    if !Path::new(&multimedia.file_path).exists() {
        return Err(EnhancedError::Io(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            format!("Media file not found: {}", multimedia.file_path),
        )));
    }

    Err(EnhancedError::Unsupported(
        "Multimedia embedding requires rendition dictionary support".to_string(),
    ))
}

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

    #[test]
    fn test_javascript_action() {
        let action = JavaScriptAction::new(ActionType::DocumentOpen, "app.alert('Welcome!');");
        assert_eq!(action.action_type, ActionType::DocumentOpen);
        assert!(action.script.contains("Welcome"));
    }

    #[test]
    fn test_page_transition() {
        let transition = PageTransition::new(TransitionEffect::Dissolve, 1.5)
            .with_direction(TransitionDirection::LeftToRight);
        assert_eq!(transition.effect, TransitionEffect::Dissolve);
        assert_eq!(transition.duration, 1.5);
    }

    #[test]
    fn test_annotation_creation() {
        let annotation =
            Annotation::new(AnnotationType::Highlight, 0, (100.0, 100.0, 200.0, 120.0))
                .with_contents("Important text")
                .with_color(1.0, 1.0, 0.0)
                .with_opacity(0.5);

        assert_eq!(annotation.annotation_type, AnnotationType::Highlight);
        assert_eq!(annotation.opacity, 0.5);
    }

    #[test]
    fn test_multimedia_config() {
        let media = Multimedia {
            media_type: MediaType::Video,
            file_path: "video.mp4".to_string(),
            page: 0,
            rect: (100.0, 100.0, 400.0, 300.0),
            autoplay: false,
            controls: true,
        };
        assert_eq!(media.media_type, MediaType::Video);
        assert!(media.controls);
    }
}
