#[cfg(feature = "image")]
use pdfmake_rust::ImageNode;
use pdfmake_rust::{
    ColumnsNode, Document, DocumentNode, FontRegistry, ListNode, Margins,
    PageSize, TableNode, TableCell,
    layout_document, measure_document, normalize_document, LayoutItem,
};

#[test]
fn snaking_columns_column_gap_builder() {
    let cols = ColumnsNode::new(vec![vec![DocumentNode::text("test")]])
        .column_gap(20.0);
    assert_eq!(cols.column_gap, 20.0);
}

#[test]
fn snaking_columns_default_column_gap() {
    let cols = ColumnsNode::new(vec![vec![DocumentNode::text("test")]]);
    assert_eq!(cols.column_gap, 10.0, "Default column gap should be 10.0");
}

#[test]
fn non_snaking_columns_only_first_column_gets_content() {
    let long_text = "word ".repeat(200);
    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::text(&long_text)],
                vec![DocumentNode::text("Column 2 content")],
            ])
        ))
        .build();

    let layout = layout_document(
        &measure_document(
            &normalize_document(&doc).unwrap(),
            &FontRegistry::empty(),
        )
        .unwrap(),
    )
    .unwrap();

    let first_page = &layout.pages[0];
    let columns_item = first_page.items.iter().find_map(|item| {
        if let LayoutItem::Columns(c) = item {
            Some(c)
        } else {
            None
        }
    });
    assert!(columns_item.is_some(), "Should have a Columns layout item");
    let cols = columns_item.unwrap();

    // In non-snaking mode, column 1 should only have its own content
    // Column 0 gets the overflow from long text, column 1 gets its own content
    let col0_has_content = !cols.columns[0].is_empty();
    let col1_has_content = !cols.columns[1].is_empty();
    assert!(col0_has_content, "Column 0 should have content");
    assert!(col1_has_content, "Column 1 should have its own content");
}

#[test]
fn snaking_columns_three_columns() {
    let long_text = "word ".repeat(300);
    let doc = Document::builder()
        .page_size(PageSize::new(600.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::text(&long_text)],
                vec![DocumentNode::text("Col 2")],
                vec![DocumentNode::text("Col 3")],
            ])
            .snaking_columns(true),
        ))
        .build();

    let layout = layout_document(
        &measure_document(
            &normalize_document(&doc).unwrap(),
            &FontRegistry::empty(),
        )
        .unwrap(),
    )
    .unwrap();

    assert!(!layout.pages.is_empty());
    let first_page = &layout.pages[0];
    let columns_item = first_page.items.iter().find_map(|item| {
        if let LayoutItem::Columns(c) = item {
            Some(c)
        } else {
            None
        }
    });
    assert!(columns_item.is_some(), "Should have Columns layout item");
    let cols = columns_item.unwrap();
    assert_eq!(cols.columns.len(), 3, "Should have 3 columns");
}

#[test]
fn snaking_columns_with_custom_gap() {
    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::text("Column 1")],
                vec![DocumentNode::text("Column 2")],
            ])
            .column_gap(30.0)
            .snaking_columns(true),
        ))
        .build();

    let layout = layout_document(
        &measure_document(
            &normalize_document(&doc).unwrap(),
            &FontRegistry::empty(),
        )
        .unwrap(),
    )
    .unwrap();

    let first_page = &layout.pages[0];
    let columns_item = first_page.items.iter().find_map(|item| {
        if let LayoutItem::Columns(c) = item {
            Some(c)
        } else {
            None
        }
    });
    assert!(columns_item.is_some(), "Should have Columns layout item");
    // The gap is used internally during layout; verify the node stores it
    // We verify by checking the document model
    if let DocumentNode::Columns(ref cn) = doc.content[0] {
        assert_eq!(cn.column_gap, 30.0);
    }
}

#[test]
fn snaking_columns_with_different_content_lengths() {
    let short_text = "Short";
    let long_text = "word ".repeat(100);
    let medium_text = "word ".repeat(30);

    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 400.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::text(short_text)],
                vec![DocumentNode::text(long_text)],
                vec![DocumentNode::text(medium_text)],
            ])
            .snaking_columns(true),
        ))
        .build();

    let layout = layout_document(
        &measure_document(
            &normalize_document(&doc).unwrap(),
            &FontRegistry::empty(),
        )
        .unwrap(),
    )
    .unwrap();

    assert!(!layout.pages.is_empty(), "Should produce at least one page");
}

#[test]
fn snaking_columns_renders_to_pdf() {
    let long_text = "word ".repeat(200);
    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::text(&long_text)],
                vec![DocumentNode::text("Column 2")],
            ])
            .snaking_columns(true),
        ))
        .build();

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

    let pdf_bytes = result.unwrap();
    assert!(!pdf_bytes.is_empty(), "PDF should not be empty");
}

#[test]
fn snaking_columns_with_table_content() {
    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::Table(
                    TableNode::new(vec![
                        vec![TableCell::text("Row 1 Col 1"), TableCell::text("Row 1 Col 2")],
                        vec![TableCell::text("Row 2 Col 1"), TableCell::text("Row 2 Col 2")],
                        vec![TableCell::text("Row 3 Col 1"), TableCell::text("Row 3 Col 2")],
                    ])
                )],
                vec![DocumentNode::text("Column 2 text content")],
            ])
            .snaking_columns(true),
        ))
        .build();

    let layout = layout_document(
        &measure_document(
            &normalize_document(&doc).unwrap(),
            &FontRegistry::empty(),
        )
        .unwrap(),
    )
    .unwrap();

    assert!(!layout.pages.is_empty(), "Should produce at least one page");
    let first_page = &layout.pages[0];
    let columns_item = first_page.items.iter().find_map(|item| {
        if let LayoutItem::Columns(c) = item {
            Some(c)
        } else {
            None
        }
    });
    assert!(columns_item.is_some(), "Should have Columns layout item");
}

#[test]
fn snaking_columns_with_list_content() {
    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::List(ListNode::unordered(vec![
                    DocumentNode::text("Item 1"),
                    DocumentNode::text("Item 2"),
                    DocumentNode::text("Item 3"),
                    DocumentNode::text("Item 4"),
                    DocumentNode::text("Item 5"),
                ]))],
                vec![DocumentNode::text("Column 2 content")],
            ])
            .snaking_columns(true),
        ))
        .build();

    let layout = layout_document(
        &measure_document(
            &normalize_document(&doc).unwrap(),
            &FontRegistry::empty(),
        )
        .unwrap(),
    )
    .unwrap();

    assert!(!layout.pages.is_empty(), "Should produce at least one page");
}

#[cfg(feature = "image")]
#[test]
fn snaking_columns_with_image_content() {
    // Use a minimal valid 1x1 PNG as image bytes
    let png_bytes: Vec<u8> = vec![
        0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature
        0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, // IHDR chunk
        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, // 1x1
        0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE, // 8-bit RGB
        0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, // IDAT chunk
        0x08, 0xD7, 0x63, 0xF8, 0xCF, 0xC0, 0x00, 0x00,
        0x00, 0x02, 0x00, 0x01, 0xE2, 0x21, 0xBC, 0x33,
        0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, // IEND chunk
        0xAE, 0x42, 0x60, 0x82,
    ];

    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::Image(ImageNode::bytes(png_bytes).width(50.0).height(50.0))],
                vec![DocumentNode::text("Column 2 with text")],
            ])
            .snaking_columns(true),
        ))
        .build();

    let result = pdfmake_rust::PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with snaking columns containing image should render");
}

#[test]
fn snaking_columns_builder_sets_true() {
    let cols = ColumnsNode::new(vec![vec![DocumentNode::text("a")], vec![DocumentNode::text("b")]])
        .snaking_columns(true);
    assert!(cols.snaking_columns, "snaking_columns(true) should set flag to true");
    assert_eq!(cols.columns.len(), 2);
}

#[test]
fn snaking_columns_single_column() {
    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![vec![DocumentNode::text("Single column content")]])
                .snaking_columns(true),
        ))
        .build();

    let layout = layout_document(
        &measure_document(
            &normalize_document(&doc).unwrap(),
            &FontRegistry::empty(),
        )
        .unwrap(),
    )
    .unwrap();

    let first_page = &layout.pages[0];
    let columns_item = first_page.items.iter().find_map(|item| {
        if let LayoutItem::Columns(c) = item {
            Some(c)
        } else {
            None
        }
    });
    assert!(columns_item.is_some(), "Should have Columns layout item");
    assert_eq!(columns_item.unwrap().columns.len(), 1, "Should have 1 column");
}

#[test]
fn snaking_columns_with_text_styles() {
    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::Text(
                    pdfmake_rust::TextNode::new("Bold text")
                        .inline_style(pdfmake_rust::Style::new().bold(true)),
                )],
                vec![DocumentNode::Text(
                    pdfmake_rust::TextNode::new("Italic text")
                        .inline_style(pdfmake_rust::Style::new().italics(true)),
                )],
            ])
            .snaking_columns(true),
        ))
        .build();

    let result = pdfmake_rust::PdfMake::new().render(&doc);
    assert!(result.is_ok(), "PDF with styled text in snaking columns should render");
}

#[test]
fn snaking_columns_multipage_content() {
    let long_text = "word ".repeat(500);
    let doc = Document::builder()
        .page_size(PageSize::new(400.0, 300.0))
        .page_margins(Margins::all(20.0))
        .content(DocumentNode::columns(
            ColumnsNode::new(vec![
                vec![DocumentNode::text(&long_text)],
                vec![DocumentNode::text("Column 2")],
            ])
            .snaking_columns(true),
        ))
        .build();

    let layout = layout_document(
        &measure_document(
            &normalize_document(&doc).unwrap(),
            &FontRegistry::empty(),
        )
        .unwrap(),
    )
    .unwrap();

    assert!(layout.pages.len() >= 2, "Should produce multiple pages, got {}", layout.pages.len());
}
