use pdfmake_rust::{Document, DocumentInfo, DocumentNode, PageSize, PdfMake, PdfEncryption, Style, TextNode, WatermarkConfig};

fn decompress_pdf_content(pdf: &[u8]) -> String {
    use flate2::read::ZlibDecoder;
    use std::io::Read;
    let mut result = String::new();
    let mut pos = 0usize;
    while let Some(stream_start) = pdf[pos..]
        .windows(8)
        .position(|w| w == b"\nstream\n" || w == b"\rstream\n")
    {
        let abs_start = pos + stream_start;
        let data_start = abs_start + 8;
        let remaining = &pdf[data_start..];
        let end_marker_pos = remaining
            .windows(10)
            .position(|w| w == b"\nendstream" || w == b"\rendstream");
        let data_end = match end_marker_pos {
            Some(p) => data_start + p,
            None => data_start + remaining.len(),
        };
        if data_end > data_start {
            let compressed = &pdf[data_start..data_end];
            let mut decoder = ZlibDecoder::new(compressed);
            let mut decompressed = String::new();
            if decoder.read_to_string(&mut decompressed).is_ok() {
                if !decompressed.is_empty() {
                    result.push_str(&decompressed);
                }
            }
        }
        pos = data_end + 10;
    }
    result
}

fn extract_watermark_font_size(content: &str) -> Option<f32> {
    for line in content.lines() {
        let trimmed = line.trim();
        if trimmed.starts_with("/F1") && trimmed.ends_with("Tf") {
            let parts: Vec<&str> = trimmed.split_whitespace().collect();
            if parts.len() >= 2 {
                if let Ok(size) = parts[1].parse::<f32>() {
                    return Some(size);
                }
            }
        }
    }
    None
}

#[test]
fn empty_document_returns_error() {
    let document = Document::default();
    let result = PdfMake::new().render(&document);
    assert!(result.is_err());
}

#[test]
fn render_text_document_returns_pdf_bytes() {
    let document = Document::builder()
        .content(DocumentNode::text("Hello PDF"))
        .build();

    let bytes = PdfMake::new().render(&document).unwrap();
    assert!(bytes.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&bytes).contains("Hello PDF"));
    assert!(String::from_utf8_lossy(&bytes).contains("%%EOF"));
}

#[test]
fn document_accepts_background() {
    let doc = Document::builder()
        .background(DocumentNode::Text(
            TextNode::new("BACKGROUND").inline_style(Style::new().font_size(60.0).color("#EEEEEE"))
        ))
        .content(DocumentNode::Text(TextNode::new("Foreground content")))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 100);
}

#[test]
fn watermark_appears_in_pdf() {
    let doc = Document::builder()
        .watermark(WatermarkConfig::new("CONFIDENTIAL"))
        .content(DocumentNode::text("Some content"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let text = String::from_utf8_lossy(&pdf);
    assert!(text.contains("/Filter /FlateDecode"), "PDF should have compressed streams");
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(text.contains("%%EOF"));
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("CONFIDENTIAL"), "Watermark text should appear in decompressed stream");
}

#[test]
fn watermark_with_custom_options() {
    let doc = Document::builder()
        .watermark(
            WatermarkConfig::new("DRAFT")
                .color("red")
                .font_size(60.0)
                .opacity(0.3)
                .angle(-45.0)
        )
        .content(DocumentNode::text("Some content"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&pdf).contains("%%EOF"));
    let content = decompress_pdf_content(&pdf);
    let font_size = extract_watermark_font_size(&content)
        .expect("Custom watermark should have font size in stream");
    assert_eq!(font_size, 60.0, "Custom font size should be 60.0");
    assert!(content.contains("DRAFT"), "Watermark text should appear in stream");
}

#[test]
fn watermark_with_auto_font_size() {
    let doc = Document::builder()
        .watermark(WatermarkConfig::new("CONFIDENTIAL").auto_font_size())
        .content(DocumentNode::text("Some content"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&pdf).contains("%%EOF"));
}

#[test]
fn watermark_auto_font_size_fits_page_width() {
    let doc = Document::builder()
        .watermark(WatermarkConfig::new("WATERMARK").auto_font_size())
        .content(DocumentNode::text("Content"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let text = String::from_utf8_lossy(&pdf);
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(text.contains("%%EOF"));
}

#[test]
fn watermark_default_font_size_is_auto() {
    let doc = Document::builder()
        .watermark(WatermarkConfig::new("DEFAULT"))
        .content(DocumentNode::text("Content"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&pdf).contains("%%EOF"));
}

#[test]
fn text_with_link_creates_annotation() {
    let doc = Document::builder()
        .content(DocumentNode::Text(TextNode::new("Click here").inline_style(
            Style::new().link("https://example.com")
        )))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let text = String::from_utf8_lossy(&pdf);
    assert!(text.contains("/Subtype /Link"), "PDF should contain Link annotation");
    assert!(text.contains("/URI (https://example.com)"), "PDF should contain the URL");
}

#[test]
fn header_appears_in_pdf() {
    let doc = Document::builder()
        .header(DocumentNode::text("Page Header"))
        .content(DocumentNode::text("Main content"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&pdf).contains("%%EOF"));
}

#[test]
fn footer_appears_in_pdf() {
    let doc = Document::builder()
        .footer(DocumentNode::text("Page Footer"))
        .content(DocumentNode::text("Main content"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&pdf).contains("%%EOF"));
}

#[test]
fn header_and_footer_together() {
    let doc = Document::builder()
        .header(DocumentNode::text("Header Text"))
        .footer(DocumentNode::text("Footer Text"))
        .content(DocumentNode::text("Body content"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&pdf).contains("%%EOF"));
}

#[test]
fn header_footer_with_multipage_content() {
    let long_text = "Line of content. ".repeat(200);
    let doc = Document::builder()
        .header(DocumentNode::text("Multi-page Header"))
        .footer(DocumentNode::text("Multi-page Footer"))
        .content(DocumentNode::text(&long_text))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&pdf).contains("%%EOF"));
}

#[test]
fn header_footer_with_stack_content() {
    let doc = Document::builder()
        .header(DocumentNode::stack(vec![
            DocumentNode::text("Company Name"),
            DocumentNode::text("Confidential"),
        ]))
        .footer(DocumentNode::stack(vec![
            DocumentNode::text("Page number"),
            DocumentNode::text("Date: 2025"),
        ]))
        .content(DocumentNode::text("Main body"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&pdf).contains("%%EOF"));
}

#[test]
fn document_accepts_info_metadata() {
    let doc = Document::builder()
        .info(DocumentInfo {
            title: Some("My Document".into()),
            author: Some("Test Author".into()),
            subject: Some("Test Subject".into()),
            keywords: Some("pdf,rust,test".into()),
            creator: Some("pdfmake_rust".into()),
            producer: Some("pdfmake_rust".into()),
        })
        .content(DocumentNode::Text(TextNode::new("hello")))
        .build();

    let pdf = PdfMake::new().render(&doc).unwrap();
    let text = String::from_utf8_lossy(&pdf);
    assert!(text.contains("My Document"));
    assert!(text.contains("Test Author"));
    assert!(text.contains("Test Subject"));
}

#[test]
fn document_without_info_renders() {
    let doc = Document::builder()
        .content(DocumentNode::Text(TextNode::new("no metadata")))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 100);
}

#[test]
fn document_accepts_encryption() {
    let doc = Document::builder()
        .encryption(PdfEncryption {
            user_password: Some("user".into()),
            owner_password: Some("owner".into()),
            allow_print: true,
            allow_copy: false,
            allow_modify: false,
        })
        .content(DocumentNode::Text(TextNode::new("Encrypted content")))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 100);
    let text = String::from_utf8_lossy(&pdf);
    assert!(text.contains("/Encrypt"));
}

#[test]
fn watermark_auto_font_size_renders_valid_pdf() {
    let doc = Document::builder()
        .watermark(WatermarkConfig::new("CONFIDENTIAL").auto_font_size())
        .content(DocumentNode::text("Some content"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(String::from_utf8_lossy(&pdf).contains("%%EOF"));
    let content = decompress_pdf_content(&pdf);
    let font_size = extract_watermark_font_size(&content)
        .expect("Watermark font size should be present in stream");
    assert!(font_size > 0.0, "Auto font size must be positive");
    assert!(content.contains("CONFIDENTIAL"), "Watermark text should appear in stream");
}

#[test]
fn watermark_default_font_size_matches_explicit_auto() {
    let doc_default = Document::builder()
        .watermark(WatermarkConfig::new("TEST"))
        .content(DocumentNode::text("Content"))
        .build();
    let doc_explicit = Document::builder()
        .watermark(WatermarkConfig::new("TEST").auto_font_size())
        .content(DocumentNode::text("Content"))
        .build();
    let pdf_default = PdfMake::new().render(&doc_default).unwrap();
    let pdf_explicit = PdfMake::new().render(&doc_explicit).unwrap();
    let content_default = decompress_pdf_content(&pdf_default);
    let content_explicit = decompress_pdf_content(&pdf_explicit);
    let size_default = extract_watermark_font_size(&content_default)
        .expect("Default watermark should have font size");
    let size_explicit = extract_watermark_font_size(&content_explicit)
        .expect("Explicit auto watermark should have font size");
    assert_eq!(size_default, size_explicit, "Default font_size should behave the same as explicit auto_font_size");
}

#[test]
fn watermark_auto_size_with_different_page_sizes() {
    let doc_small = Document::builder()
        .page_size(PageSize::new(400.0, 600.0))
        .watermark(WatermarkConfig::new("WIDE").auto_font_size())
        .content(DocumentNode::text("Content"))
        .build();
    let doc_large = Document::builder()
        .page_size(PageSize::new(1200.0, 1600.0))
        .watermark(WatermarkConfig::new("WIDE").auto_font_size())
        .content(DocumentNode::text("Content"))
        .build();
    let pdf_small = PdfMake::new().render(&doc_small).unwrap();
    let pdf_large = PdfMake::new().render(&doc_large).unwrap();
    let content_small = decompress_pdf_content(&pdf_small);
    let content_large = decompress_pdf_content(&pdf_large);
    let size_small = extract_watermark_font_size(&content_small)
        .expect("Small page watermark should have font size");
    let size_large = extract_watermark_font_size(&content_large)
        .expect("Large page watermark should have font size");
    assert_ne!(size_small, size_large,
        "Different page sizes should produce different auto font sizes (small={}, large={})",
        size_small, size_large);
    assert!(size_large > size_small,
        "Larger page should get a larger auto font size (small={}, large={})",
        size_small, size_large);
}
