#![cfg(feature = "svg")]

use pdfmake_rust::{Document, DocumentNode, PdfMake, Style, TextNode};

#[test]
fn named_dest_builder_adds_to_document() {
    let doc = Document::builder()
        .named_dest("intro", 0, 100.0, 200.0)
        .content(DocumentNode::text("Hello"))
        .build();

    assert_eq!(doc.named_dests.len(), 1);
    assert_eq!(doc.named_dests[0].id, "intro");
    assert_eq!(doc.named_dests[0].page_index, 0);
    assert_eq!(doc.named_dests[0].x, 100.0);
    assert_eq!(doc.named_dests[0].y, 200.0);
}

#[test]
fn multiple_named_dests() {
    let doc = Document::builder()
        .named_dest("dest1", 0, 0.0, 0.0)
        .named_dest("dest2", 1, 50.0, 100.0)
        .named_dest("dest3", 2, 200.0, 300.0)
        .content(DocumentNode::text("Hello"))
        .build();

    assert_eq!(doc.named_dests.len(), 3);
    assert_eq!(doc.named_dests[0].id, "dest1");
    assert_eq!(doc.named_dests[1].id, "dest2");
    assert_eq!(doc.named_dests[2].id, "dest3");
}

#[test]
fn outline_builder_adds_entry() {
    let doc = Document::builder()
        .outline("ch1", "Chapter 1")
        .content(DocumentNode::text("Chapter 1 content"))
        .build();

    assert_eq!(doc.outlines.len(), 1);
    assert_eq!(doc.outlines[0].id, "ch1");
    assert_eq!(doc.outlines[0].text, "Chapter 1");
    assert_eq!(doc.outlines[0].parent_id, None);
}

#[test]
fn outline_with_parent_creates_hierarchy() {
    let doc = Document::builder()
        .outline("ch1", "Chapter 1")
        .outline_child("ch1s1", "Section 1.1", "ch1")
        .outline_child("ch1s2", "Section 1.2", "ch1")
        .content(DocumentNode::text("Content"))
        .build();

    assert_eq!(doc.outlines.len(), 3);
    assert_eq!(doc.outlines[0].parent_id, None);
    assert_eq!(doc.outlines[1].parent_id, Some("ch1".to_string()));
    assert_eq!(doc.outlines[2].parent_id, Some("ch1".to_string()));
}

#[test]
fn outline_renders_in_pdf() {
    let doc = Document::builder()
        .outline("ch1", "Chapter 1")
        .content(DocumentNode::text("Chapter 1 content"))
        .build();

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with outline should render successfully");

    let pdf_bytes = result.unwrap();
    let pdf_str = String::from_utf8_lossy(&pdf_bytes);
    assert!(pdf_str.contains("/Outlines"), "PDF should contain /Outlines in catalog");
}

#[test]
fn outline_hierarchy_renders_in_pdf() {
    let doc = Document::builder()
        .outline("ch1", "Chapter 1")
        .outline_child("ch1s1", "Section 1.1", "ch1")
        .outline_child("ch1s2", "Section 1.2", "ch1")
        .content(DocumentNode::text("Chapter 1 content"))
        .content(DocumentNode::text("Section 1.1 content"))
        .content(DocumentNode::text("Section 1.2 content"))
        .build();

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with outline hierarchy should render successfully");

    let pdf_bytes = result.unwrap();
    let pdf_str = String::from_utf8_lossy(&pdf_bytes);
    assert!(pdf_str.contains("/Outlines"), "PDF should contain /Outlines");
    assert!(pdf_str.contains("/Type /Outline"), "PDF should contain outline objects");
}

#[test]
fn named_dest_appears_in_pdf_catalog() {
    let doc = Document::builder()
        .named_dest("my_dest", 0, 50.0, 100.0)
        .content(DocumentNode::text("Some content"))
        .build();

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok());

    let pdf_bytes = result.unwrap();
    let pdf_str = String::from_utf8_lossy(&pdf_bytes);
    assert!(pdf_str.contains("/Dests"), "PDF catalog should contain /Dests");
    assert!(pdf_str.contains("my_dest"), "PDF should contain the named dest id");
}

#[test]
fn link_to_destination_creates_annotation() {
    let doc = Document::builder()
        .content(DocumentNode::Text(TextNode::new("Chapter 1").id("ch1")))
        .content(DocumentNode::Text(
            TextNode::new("Go to Chapter 1")
                .inline_style(Style::new().link("#ch1")),
        ))
        .build();

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with link to destination should render");

    let pdf_bytes = result.unwrap();
    let pdf_str = String::from_utf8_lossy(&pdf_bytes);
    assert!(pdf_str.contains("/Annots"), "PDF should contain annotations");
}

#[test]
fn svg_link_to_destination_creates_annotation() {
    let svg_markup = r##"<svg xmlns="http://www.w3.org/2000/svg" width="100" height="50">
        <a href="#ch1"><rect width="100" height="50" fill="blue"/></a>
    </svg>"##;

    let doc = Document::builder()
        .content(DocumentNode::Text(TextNode::new("Chapter 1").id("ch1")))
        .content(DocumentNode::Svg(pdfmake_rust::SvgNode::new(svg_markup)))
        .build();

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with SVG link should render");
}

#[test]
fn text_link_creates_url_annotation() {
    let doc = Document::builder()
        .content(DocumentNode::Text(
            TextNode::new("Visit website")
                .inline_style(Style::new().link("https://example.com")),
        ))
        .build();

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with URL link should render");

    let pdf_bytes = result.unwrap();
    let pdf_str = String::from_utf8_lossy(&pdf_bytes);
    assert!(pdf_str.contains("/Annots"), "PDF should contain annotations");
    assert!(pdf_str.contains("example.com"), "PDF should contain the URL");
}

#[test]
fn image_link_to_page_creates_named_dest() {
    let doc = Document::builder()
        .content(DocumentNode::Text(TextNode::new("Target page").id("target")))
        .content(DocumentNode::text("More content"))
        .build();

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with image link should render");

    let pdf_bytes = result.unwrap();
    let pdf_str = String::from_utf8_lossy(&pdf_bytes);
    assert!(pdf_str.contains("target"), "PDF should contain the named dest");
}

#[test]
fn svg_link_to_page_creates_named_dest() {
    let svg_markup = r##"<svg xmlns="http://www.w3.org/2000/svg" width="100" height="50">
        <a href="#target"><rect width="100" height="50" fill="red"/></a>
    </svg>"##;

    let doc = Document::builder()
        .content(DocumentNode::Text(TextNode::new("Target").id("target")))
        .content(DocumentNode::Svg(pdfmake_rust::SvgNode::new(svg_markup)))
        .build();

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with SVG link to page should render");
}

#[test]
fn combined_outlines_and_named_dests() {
    let doc = Document::builder()
        .outline("intro", "Introduction")
        .outline("chapter1", "Chapter 1")
        .outline_child("section1_1", "Section 1.1", "chapter1")
        .named_dest("intro", 0, 0.0, 0.0)
        .named_dest("chapter1", 0, 0.0, 400.0)
        .named_dest("section1_1", 0, 0.0, 600.0)
        .named_dest("extra_dest", 0, 100.0, 100.0)
        .content(DocumentNode::text("Introduction content"))
        .content(DocumentNode::text("Chapter 1 content"))
        .content(DocumentNode::text("Section 1.1 content"))
        .build();

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with combined outlines and named dests should render");

    let pdf_bytes = result.unwrap();
    let pdf_str = String::from_utf8_lossy(&pdf_bytes);
    assert!(pdf_str.contains("/Outlines"), "PDF should contain outlines");
    // Named dests sharing IDs with outlines are embedded in outline entries as /Dest,
    // while unique named dests go to /Dests catalog entry
    assert!(pdf_str.contains("extra_dest"), "PDF should contain the extra named dest");
}

#[test]
fn link_to_destination_with_multipage_document() {
    let mut builder = Document::builder()
        .content(DocumentNode::Text(TextNode::new("Chapter 1 Start").id("ch1")));

    for i in 0..50 {
        builder = builder.content(DocumentNode::text(format!("Fill line {}", i)));
    }

    builder = builder.content(DocumentNode::Text(
        TextNode::new("Go back to Chapter 1")
            .inline_style(Style::new().link("#ch1")),
    ));

    let doc = builder.build();
    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with cross-page link should render");

    let pdf_bytes = result.unwrap();
    let pdf_str = String::from_utf8_lossy(&pdf_bytes);
    assert!(pdf_str.contains("/Annots"), "PDF should contain link annotations");
    assert!(pdf_str.contains("ch1"), "PDF should contain destination reference");
}

#[test]
fn empty_outlines_and_named_dests() {
    let doc = Document::builder()
        .content(DocumentNode::text("Simple content"))
        .build();

    assert!(doc.outlines.is_empty(), "No outlines should be present");
    assert!(doc.named_dests.is_empty(), "No named dests should be present");

    let result = PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF without outlines/named dests should render");

    let pdf_bytes = result.unwrap();
    let pdf_str = String::from_utf8_lossy(&pdf_bytes);
    assert!(!pdf_str.contains("/Outlines"), "PDF should not contain outlines section");
}
