#![cfg(all(feature = "image", feature = "svg", feature = "qr"))]

use flate2::read::ZlibDecoder;
use pdfmake_rust::{
    AbsoluteNode, Alignment, ColumnsNode, Document, DocumentNode, ImageNode, InlineFragment,
    ListNode, Margins, PageSize, PdfMake, QrNode, Style, SvgNode, TableCell, TableNode,
    TableWidth, TextDecoration, TextNode,
};
use std::io::Read;

/// 解压 PDF 内容流，提取纯文本用于断言
fn decompress_pdf_content(pdf: &[u8]) -> String {
    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() && !decompressed.is_empty() {
                result.push_str(&decompressed);
            }
        }
        pos = data_end + 10;
    }
    result
}

// ============================================================
// 1. 列表 marker_type 全覆盖
// ============================================================

#[test]
fn ordered_list_decimal_default() {
    let doc = Document::builder()
        .content(DocumentNode::list(ListNode::ordered(vec![
            DocumentNode::text("A"),
            DocumentNode::text("B"),
        ])))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(1. )"), "decimal: {}", content);
    assert!(content.contains("(2. )"), "decimal: {}", content);
}

#[test]
fn ordered_list_lower_alpha_with_start() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("char c"),
                DocumentNode::text("char d"),
                DocumentNode::text("char e"),
            ])
            .marker_type("lower-alpha")
            .start(3),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(c. )"), "lower-alpha start=3: {}", content);
    assert!(content.contains("(d. )"), "lower-alpha start=3: {}", content);
    assert!(content.contains("(e. )"), "lower-alpha start=3: {}", content);
}

#[test]
fn ordered_list_upper_alpha() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("ALPHA"),
                DocumentNode::text("BETA"),
            ])
            .marker_type("upper-alpha"),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(A. )"), "upper-alpha: {}", content);
    assert!(content.contains("(B. )"), "upper-alpha: {}", content);
}

#[test]
fn ordered_list_lower_roman() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("i item"),
                DocumentNode::text("ii item"),
                DocumentNode::text("iii item"),
                DocumentNode::text("iv item"),
                DocumentNode::text("v item"),
            ])
            .marker_type("lower-roman"),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(i. )"), "lower-roman: {}", content);
    assert!(content.contains("(ii. )"), "lower-roman: {}", content);
    assert!(content.contains("(iii. )"), "lower-roman: {}", content);
    assert!(content.contains("(iv. )"), "lower-roman: {}", content);
    assert!(content.contains("(v. )"), "lower-roman: {}", content);
}

#[test]
fn ordered_list_upper_roman_with_large_start() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("49 => XLIX"),
                DocumentNode::text("50 => L"),
            ])
            .marker_type("upper-roman")
            .start(49),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(XLIX. )"), "upper-roman start=49: {}", content);
    assert!(content.contains("(L. )"), "upper-roman start=49: {}", content);
}

#[test]
fn ordered_list_lower_latin_alias() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![DocumentNode::text("item")])
                .marker_type("lower-latin")
                .start(1),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(a. )"), "lower-latin: {}", content);
}

#[test]
fn ordered_list_upper_latin_alias() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![DocumentNode::text("item")])
                .marker_type("upper-latin")
                .start(2),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(B. )"), "upper-latin start=2: {}", content);
}

#[test]
fn ordered_list_unknown_marker_type_falls_back_to_decimal() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![DocumentNode::text("x")])
                .marker_type("unknown-type")
                .start(7),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(7. )"), "unknown marker_type fallback: {}", content);
}

#[test]
fn ordered_list_high_start_value() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("item 100"),
                DocumentNode::text("item 101"),
            ])
            .start(100),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(100. )"), "start=100: {}", content);
    assert!(content.contains("(101. )"), "start=100: {}", content);
}

// ============================================================
// 2. 无序列表
// ============================================================

#[test]
fn unordered_list_empty() {
    let doc = Document::builder()
        .content(DocumentNode::list(ListNode::unordered(vec![])))
        .content(DocumentNode::text("after"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 0);
}

#[test]
fn ordered_list_empty() {
    let doc = Document::builder()
        .content(DocumentNode::list(ListNode::ordered(vec![])))
        .content(DocumentNode::text("after"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 0);
}

// ============================================================
// 3. Style 边界值
// ============================================================

#[test]
fn style_all_none_new() {
    // Style::new() 所有字段为 None，不覆盖默认值
    let doc = Document::builder()
        .content(DocumentNode::Text(
            TextNode::new("default").inline_style(Style::new()),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("default"));
}

#[test]
fn style_font_size_zero_and_large() {
    let doc = Document::builder()
        .content(DocumentNode::stack(vec![
            DocumentNode::Text(TextNode::new("zero").inline_style(Style::new().font_size(0.0))),
            DocumentNode::Text(TextNode::new("huge").inline_style(Style::new().font_size(200.0))),
        ]))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("zero"));
    assert!(content.contains("huge"));
}

#[test]
fn style_line_height_zero_and_large() {
    let doc = Document::builder()
        .content(DocumentNode::stack(vec![
            DocumentNode::Text(TextNode::new("lh0").inline_style(Style::new().line_height(0.0))),
            DocumentNode::Text(TextNode::new("lh10").inline_style(Style::new().line_height(10.0))),
        ]))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("lh0"));
    assert!(content.contains("lh10"));
}

#[test]
fn style_all_decorations() {
    let doc = Document::builder()
        .content(DocumentNode::stack(vec![
            DocumentNode::Text(
                TextNode::new("u")
                    .inline_style(Style::new().decoration(TextDecoration::Underline)),
            ),
            DocumentNode::Text(
                TextNode::new("s")
                    .inline_style(Style::new().decoration(TextDecoration::Strikethrough)),
            ),
            DocumentNode::Text(
                TextNode::new("o")
                    .inline_style(Style::new().decoration(TextDecoration::Overline)),
            ),
        ]))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("u"));
    assert!(content.contains("s"));
    assert!(content.contains("o"));
}

#[test]
fn style_all_alignments() {
    let doc = Document::builder()
        .content(DocumentNode::stack(vec![
            DocumentNode::Text(
                TextNode::new("left")
                    .inline_style(Style::new().alignment(Alignment::Left)),
            ),
            DocumentNode::Text(
                TextNode::new("center")
                    .inline_style(Style::new().alignment(Alignment::Center)),
            ),
            DocumentNode::Text(
                TextNode::new("right")
                    .inline_style(Style::new().alignment(Alignment::Right)),
            ),
        ]))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("left"));
    assert!(content.contains("center"));
    assert!(content.contains("right"));
}

#[test]
fn style_bold_italics_combinations() {
    let doc = Document::builder()
        .content(DocumentNode::stack(vec![
            DocumentNode::Text(TextNode::new("normal").inline_style(Style::new())),
            DocumentNode::Text(TextNode::new("bold").inline_style(Style::new().bold(true))),
            DocumentNode::Text(TextNode::new("italics").inline_style(Style::new().italics(true))),
            DocumentNode::Text(
                TextNode::new("both")
                    .inline_style(Style::new().bold(true).italics(true)),
            ),
        ]))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("normal"));
    assert!(content.contains("bold"));
    assert!(content.contains("italics"));
    assert!(content.contains("both"));
}

// ============================================================
// 4. InlineFragment 边界
// ============================================================

#[test]
fn inline_fragment_empty_text() {
    let doc = Document::builder()
        .content(DocumentNode::Text(TextNode::with_inlines(vec![
            InlineFragment::new("before "),
            InlineFragment::new(""),
            InlineFragment::new("after"),
        ])))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("before"));
    assert!(content.contains("after"));
}

#[test]
fn inline_fragment_many_styles() {
    let doc = Document::builder()
        .content(DocumentNode::Text(TextNode::with_inlines(vec![
            InlineFragment::new("A").style(Style::new().bold(true)),
            InlineFragment::new("B").style(Style::new().italics(true)),
            InlineFragment::new("C").style(
                Style::new().decoration(TextDecoration::Underline),
            ),
            InlineFragment::new("D").style(
                Style::new().decoration(TextDecoration::Strikethrough),
            ),
            InlineFragment::new("E").style(
                Style::new().decoration(TextDecoration::Overline),
            ),
            InlineFragment::new("F").style(Style::new().font_size(20.0)),
        ])))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("A"));
    assert!(content.contains("B"));
    assert!(content.contains("C"));
    assert!(content.contains("D"));
    assert!(content.contains("E"));
    assert!(content.contains("F"));
}

// ============================================================
// 5. Table 边界
// ============================================================

#[test]
fn table_empty_body() {
    let table = TableNode::new(vec![]);
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .content(DocumentNode::text("after"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 0);
}

#[test]
fn table_empty_row() {
    let table = TableNode::new(vec![
        vec![TableCell::text("A")],
        vec![],
    ]);
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("A"));
}

#[test]
fn table_cell_empty_text() {
    let table = TableNode::new(vec![vec![TableCell::text("")]]);
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 0);
}

#[test]
fn table_cell_padding_zero_and_large() {
    let table = TableNode::new(vec![vec![
        TableCell::text("p0").padding(0.0),
        TableCell::text("p50").padding(50.0),
    ]]);
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("p0"));
    assert!(content.contains("p50"));
}

#[test]
fn table_col_span_zero_clamped_to_one() {
    let table = TableNode::new(vec![vec![
        TableCell::text("clamped").col_span(0),
        TableCell::text("next"),
    ]]);
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("clamped"));
    assert!(content.contains("next"));
}

#[test]
fn table_row_span_zero_clamped_to_one() {
    let table = TableNode::new(vec![
        vec![TableCell::text("clamped").row_span(0)],
        vec![TableCell::text("row2")],
    ]);
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("clamped"));
    assert!(content.contains("row2"));
}

#[test]
fn table_col_span_exceeds_column_count() {
    let table = TableNode::new(vec![vec![
        TableCell::text("overspan").col_span(10),
    ]]);
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("overspan"));
}

#[test]
fn table_all_width_variants() {
    let table = TableNode::new(vec![
        vec![
            TableCell::text("Auto"),
            TableCell::text("Star"),
            TableCell::text("Fixed"),
        ],
    ])
    .widths(vec![TableWidth::Auto, TableWidth::Star, TableWidth::Fixed(100.0)]);
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("Auto"));
    assert!(content.contains("Star"));
    assert!(content.contains("Fixed"));
}

#[test]
fn table_width_fixed_zero() {
    // Fixed(0) 导致列宽为0，文本可能不可见，但不应该崩溃
    let table = TableNode::new(vec![vec![TableCell::text("zero_width")]])
        .widths(vec![TableWidth::Fixed(0.0)]);
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 0);
}

// ============================================================
// 6. Image 边界
// ============================================================

fn minimal_jpeg_1x1() -> Vec<u8> {
    vec![
        0xFF, 0xD8, 0xFF, 0xC0, 0x00, 0x0B, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0xFF,
        0xD9,
    ]
}

#[test]
fn image_bytes_source() {
    let doc = Document::builder()
        .content(DocumentNode::image(ImageNode::bytes(minimal_jpeg_1x1()).width(50.0)))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(String::from_utf8_lossy(&pdf).contains("/XObject"));
}

#[test]
fn image_path_source() {
    let doc = Document::builder()
        .content(DocumentNode::image(
            ImageNode::path("docs/pdfmake/examples/images/sampleImage.jpg").width(50.0),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(String::from_utf8_lossy(&pdf).contains("/XObject"));
}

#[test]
fn image_with_fit() {
    let doc = Document::builder()
        .content(DocumentNode::image(
            ImageNode::bytes(minimal_jpeg_1x1()).fit(30.0, 20.0),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(String::from_utf8_lossy(&pdf).contains("/XObject"));
}

#[test]
fn image_all_alignments() {
    for align in &[Alignment::Left, Alignment::Center, Alignment::Right] {
        let doc = Document::builder()
            .content(DocumentNode::image(
                ImageNode::bytes(minimal_jpeg_1x1())
                    .width(30.0)
                    .alignment(*align),
            ))
            .build();
        let pdf = PdfMake::new().render(&doc).unwrap();
        assert!(
            String::from_utf8_lossy(&pdf).contains("/XObject"),
            "image alignment={:?}",
            align
        );
    }
}

#[test]
fn image_width_only_no_height() {
    let doc = Document::builder()
        .content(DocumentNode::image(ImageNode::bytes(minimal_jpeg_1x1()).width(40.0)))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(String::from_utf8_lossy(&pdf).contains("/XObject"));
}

#[test]
fn image_height_only_no_width() {
    let doc = Document::builder()
        .content(DocumentNode::image(
            ImageNode::bytes(minimal_jpeg_1x1()).height(40.0),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(String::from_utf8_lossy(&pdf).contains("/XObject"));
}

// ============================================================
// 7. SVG 边界
// ============================================================

#[test]
fn svg_with_dimensions() {
    let doc = Document::builder()
        .content(DocumentNode::svg(
            SvgNode::new(
                r#"<svg width="20" height="10"><rect width="20" height="10"/></svg>"#,
            )
            .width(50.0)
            .height(30.0),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 0);
}

#[test]
fn svg_with_fit() {
    let doc = Document::builder()
        .content(DocumentNode::svg(
            SvgNode::new(
                r#"<svg width="20" height="10"><line x1="0" y1="0" x2="20" y2="10" stroke="red"/></svg>"#,
            )
            .fit(100.0, 50.0),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 0);
}

#[test]
fn svg_all_alignments() {
    for align in &[Alignment::Left, Alignment::Center, Alignment::Right] {
        let doc = Document::builder()
            .content(DocumentNode::svg(
                SvgNode::new(
                    r#"<svg width="20" height="10"><rect width="20" height="10"/></svg>"#,
                )
                .alignment(*align),
            ))
            .build();
        let pdf = PdfMake::new().render(&doc).unwrap();
        assert!(pdf.len() > 0, "svg alignment={:?}", align);
    }
}

// ============================================================
// 8. QR 边界
// ============================================================

#[test]
fn qr_with_fit() {
    let doc = Document::builder()
        .content(DocumentNode::qr(QrNode::new("hello").fit(40.0)))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 0);
}

#[test]
fn qr_all_alignments() {
    for align in &[Alignment::Left, Alignment::Center, Alignment::Right] {
        let doc = Document::builder()
            .content(DocumentNode::qr(QrNode::new("test").fit(50.0).alignment(*align)))
            .build();
        let pdf = PdfMake::new().render(&doc).unwrap();
        assert!(pdf.len() > 0, "qr alignment={:?}", align);
    }
}

// ============================================================
// 9. Columns 边界
// ============================================================

#[test]
fn columns_single_column() {
    let doc = Document::builder()
        .content(DocumentNode::columns(ColumnsNode::new(vec![vec![
            DocumentNode::text("only"),
        ]])))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("only"));
}

#[test]
fn columns_empty_column() {
    let doc = Document::builder()
        .content(DocumentNode::columns(ColumnsNode::new(vec![
            vec![DocumentNode::text("has_content")],
            vec![],
        ])))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("has_content"));
}

#[test]
fn columns_gap_zero() {
    let doc = Document::builder()
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::text("left")],
                vec![DocumentNode::text("right")],
            ])
            .column_gap(0.0),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("left"));
    assert!(content.contains("right"));
}

// ============================================================
// 10. Absolute 定位边界
// ============================================================

#[test]
fn absolute_with_position_properties() {
    let doc = Document::builder()
        .content(DocumentNode::absolute(
            AbsoluteNode::new(DocumentNode::text("positioned"))
                .left(10.0)
                .top(20.0),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("positioned"));
}

#[test]
fn absolute_with_center_horizontal() {
    let doc = Document::builder()
        .content(DocumentNode::absolute(
            AbsoluteNode::new(DocumentNode::text("centered")).center_horizontal(),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("centered"));
}

#[test]
fn absolute_with_center_vertical() {
    let doc = Document::builder()
        .content(DocumentNode::absolute(
            AbsoluteNode::new(DocumentNode::text("vcentered")).center_vertical(),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("vcentered"));
}

#[test]
fn absolute_right_bottom() {
    let doc = Document::builder()
        .content(DocumentNode::absolute(
            AbsoluteNode::new(DocumentNode::text("corner"))
                .right(10.0)
                .bottom(10.0),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("corner"));
}

// ============================================================
// 11. Document 级别边界
// ============================================================

#[test]
fn document_empty_content_errors() {
    let doc = Document::builder().build();
    let result = PdfMake::new().render(&doc);
    assert!(result.is_err());
}

#[test]
fn document_custom_page_size() {
    let doc = Document::builder()
        .page_size(PageSize::new(300.0, 400.0))
        .content(DocumentNode::text("small page"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF"));
}

#[test]
fn document_a4_page_size() {
    let doc = Document::builder()
        .page_size(PageSize::a4())
        .content(DocumentNode::text("A4"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF"));
}

#[test]
fn document_custom_margins() {
    let doc = Document::builder()
        .page_margins(Margins::new(10.0, 20.0, 30.0, 40.0))
        .content(DocumentNode::text("margins"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("margins"));
}

#[test]
fn document_uniform_margins() {
    let doc = Document::builder()
        .page_margins(Margins::all(60.0))
        .content(DocumentNode::text("uniform"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("uniform"));
}

#[test]
fn document_named_styles() {
    let doc = Document::builder()
        .style("my-heading", Style::new().bold(true).font_size(20.0))
        .style("my-note", Style::new().italics(true).font_size(9.0))
        .content(DocumentNode::Text(TextNode::new("heading").style("my-heading")))
        .content(DocumentNode::Text(TextNode::new("note").style("my-note")))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("heading"));
    assert!(content.contains("note"));
}

#[test]
fn document_default_style_override() {
    let doc = Document::builder()
        .default_style(Style::new().font_size(18.0).bold(true))
        .content(DocumentNode::text("default styled"))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("default styled"));
}

// ============================================================
// 12. 嵌套/组合场景
// ============================================================

#[test]
fn stack_nests_all_node_types() {
    let doc = Document::builder()
        .content(DocumentNode::stack(vec![
            DocumentNode::text("text in stack"),
            DocumentNode::list(ListNode::unordered(vec![
                DocumentNode::text("list in stack"),
            ])),
            DocumentNode::columns(ColumnsNode::new(vec![vec![DocumentNode::text(
                "col in stack",
            )]])),
        ]))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("text in stack"));
    assert!(content.contains("list in stack"));
    assert!(content.contains("col in stack"));
}

#[test]
fn list_nests_stack() {
    let doc = Document::builder()
        .content(DocumentNode::list(ListNode::ordered(vec![
            DocumentNode::stack(vec![
                DocumentNode::text("nested line 1"),
                DocumentNode::text("nested line 2"),
            ]),
        ])))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("nested line 1"));
    assert!(content.contains("nested line 2"));
}

#[test]
fn table_inline_styles_with_row_span_and_col_span() {
    let table = TableNode::new(vec![
        vec![
            TableCell::text("R1C1").style(Style::new().bold(true)),
            TableCell::text("R1C2").col_span(2),
        ],
        vec![
            TableCell::text("R2C1").row_span(2),
            TableCell::text("R2C2"),
            TableCell::text("R2C3"),
        ],
        vec![
            TableCell::text("R3C2"),
            TableCell::text("R3C3"),
        ],
    ])
    .inline_style(Style::new().font_size(10.0));
    let doc = Document::builder()
        .content(DocumentNode::table(table))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("R1C1"));
    assert!(content.contains("R3C2"));
}

#[test]
fn columns_with_mixed_content() {
    let doc = Document::builder()
        .content(DocumentNode::columns(ColumnsNode::new(vec![
            vec![DocumentNode::text("col0")],
            vec![DocumentNode::text("col1")],
            vec![DocumentNode::text("col2")],
        ])))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("col0"));
    assert!(content.contains("col1"));
    assert!(content.contains("col2"));
}

#[test]
fn absolute_contains_nested_nodes() {
    let doc = Document::builder()
        .content(DocumentNode::absolute(
            AbsoluteNode::new(DocumentNode::stack(vec![
                DocumentNode::text("abs stack 1"),
                DocumentNode::text("abs stack 2"),
            ]))
            .left(5.0)
            .top(5.0),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("abs stack 1"));
    assert!(content.contains("abs stack 2"));
}

// ============================================================
// 13. 长文本 & 多内容
// ============================================================

#[test]
fn long_text_renders() {
    let long = "A".repeat(5000);
    let doc = Document::builder()
        .content(DocumentNode::text(long.clone()))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.len() > 100);
}

#[test]
fn many_content_nodes() {
    let mut builder = Document::builder();
    for i in 0..50 {
        builder = builder.content(DocumentNode::Text(TextNode::new(format!(
            "item {}",
            i
        ))));
    }
    let doc = builder.build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("item 0"));
    assert!(content.contains("item 49"));
}

// ============================================================
// 14. 多页渲染
// ============================================================

#[test]
fn multi_page_content() {
    let mut items = Vec::new();
    for i in 0..100 {
        items.push(DocumentNode::text(format!("line {}", i)));
    }
    let doc = Document::builder()
        .content(DocumentNode::stack(items))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("line 0"));
    assert!(content.contains("line 99"));
}

// ============================================================
// 15. alpha 标记 overflow（>26 项）
// ============================================================

#[test]
fn lower_alpha_with_overflow_above_26() {
    let items: Vec<DocumentNode> = (0..30)
        .map(|i| DocumentNode::text(format!("char {}", i + 1)))
        .collect();
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(items)
                .marker_type("lower-alpha")
                .start(1),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    // z 之后回绕到 a
    assert!(content.contains("(z. )"), "lower-alpha overflow: {}", content);
    assert!(content.contains("(a. )"), "lower-alpha overflow: {}", content);
}

// ============================================================
// 16. Roman 数字特殊值
// ============================================================

#[test]
fn upper_roman_special_values() {
    // 验证罗马数字的减法规则：IV(4), IX(9), XL(40), XC(90) 等
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("one"),
                DocumentNode::text("four"),       // marker: II
                DocumentNode::text("five"),       // marker: III
                DocumentNode::text("nine"),       // marker: IV
                DocumentNode::text("forty nine"), // marker: V
            ])
            .marker_type("upper-roman"),
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(I. )"), "roman I: {}", content);
    assert!(content.contains("(II. )"), "roman II: {}", content);
    assert!(content.contains("(III. )"), "roman III: {}", content);
    assert!(content.contains("(IV. )"), "roman IV: {}", content);
    assert!(content.contains("(V. )"), "roman V: {}", content);
}

// ============================================================
// 17. 列表 type:none / separator / reversed
// ============================================================

#[test]
fn ordered_list_type_none() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("first"),
                DocumentNode::text("second"),
            ]).marker_type("none")
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("first"), "should contain 'first', got: {}", content);
    assert!(content.contains("second"), "should contain 'second', got: {}", content);
    // 不应包含编号文本
    assert!(!content.contains("(1)"), "should not contain '(1)', got: {}", content);
    assert!(!content.contains("(2)"), "should not contain '(2)', got: {}", content);
}

#[test]
fn unordered_list_type_none() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::unordered(vec![
                DocumentNode::text("alpha"),
                DocumentNode::text("beta"),
            ]).marker_type("none")
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("alpha"), "should contain 'alpha', got: {}", content);
    assert!(content.contains("beta"), "should contain 'beta', got: {}", content);
    // 不应该有 bullet 圆点渲染（circle 命令 "m ... c ... f"）
    // 无标记类型不应产生任何 marker 绘制
}

#[test]
fn ordered_list_with_separator_paren() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("first"),
                DocumentNode::text("second"),
            ]).separator(")")
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(1\\) ") || content.contains("(1) "), "Expected '(1\\) ' or '(1) ', got: {}", content);
    assert!(content.contains("(2\\) ") || content.contains("(2) "), "Expected '(2\\) ' or '(2) ', got: {}", content);
}

#[test]
fn ordered_list_reversed() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("first"),
                DocumentNode::text("second"),
                DocumentNode::text("third"),
            ]).reversed()
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(3. )"), "Expected '(3. )', got: {}", content);
    assert!(content.contains("(2. )"), "Expected '(2. )', got: {}", content);
    assert!(content.contains("(1. )"), "Expected '(1. )', got: {}", content);
}

#[test]
fn ordered_list_reversed_with_start() {
    let doc = Document::builder()
        .content(DocumentNode::list(
            ListNode::ordered(vec![
                DocumentNode::text("a"),
                DocumentNode::text("b"),
            ]).reversed().start(10)
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    let content = decompress_pdf_content(&pdf);
    assert!(content.contains("(10. )"), "Expected '(10. )', got: {}", content);
    assert!(content.contains("(9. )"), "Expected '(9. )', got: {}", content);
}

// ============================================================
// 18. Unbreakable blocks
// ============================================================

#[test]
fn unbreakable_block_moves_to_new_page() {
    let doc = Document::builder()
        .page_size(PageSize::new(612.0, 300.0))
        .content(DocumentNode::text("Page 1 content"))
        .content(DocumentNode::Text(
            TextNode::new("This unbreakable block should move to a new page entirely because it is long enough to need its own page when the first page is already partially filled")
                .inline_style(Style::new().unbreakable(true))
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 100);
    assert!(pdf.starts_with(b"%PDF"));
}

#[test]
fn unbreakable_false_behaves_normally() {
    let doc = Document::builder()
        .page_size(PageSize::new(612.0, 300.0))
        .content(DocumentNode::text("filler"))
        .content(DocumentNode::Text(
            TextNode::new("This block is not unbreakable so it can be split across pages if needed without any special handling")
                .inline_style(Style::new().unbreakable(false))
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.len() > 100);
}

#[test]
fn unbreakable_block_on_empty_page_stays() {
    let doc = Document::builder()
        .page_size(PageSize::new(612.0, 842.0))
        .content(DocumentNode::Text(
            TextNode::new("short unbreakable")
                .inline_style(Style::new().unbreakable(true))
        ))
        .build();
    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF"));
}

#[test]
fn style_builder_unbreakable() {
    let style = Style::new().unbreakable(true);
    assert_eq!(style.unbreakable, Some(true));

    let style = Style::new().unbreakable(false);
    assert_eq!(style.unbreakable, Some(false));

    let style = Style::new();
    assert_eq!(style.unbreakable, None);
}

#[test]
fn style_has_any_includes_unbreakable() {
    let style = Style::new().unbreakable(true);
    assert!(style.has_any());

    let style = Style::new();
    assert!(!style.has_any());
}