use pdfmake_rust::{
    Document, DocumentNode, FontRegistry, LayoutItem,
    NormalizedBlock, PdfMake, TableCell, TableLayoutConfig, TableNode,
    TableStyle, TableWidth,
};
use pdfmake_rust::{layout_document, measure_document, normalize_document};

// ============================================================================
// Per-cell border tests
// ============================================================================

#[test]
fn cell_border_true_keeps_all_sides() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![
                TableCell::text("cell").border(vec![true, true, true, true]),
            ]])
            .widths(vec![TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell = &table.rows[0].cells[0];
    assert_eq!(cell.borders.len(), 4, "should have all 4 borders");
}

#[test]
fn cell_border_none_uses_default() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![
                TableCell::text("cell"),
            ]])
            .widths(vec![TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell = &table.rows[0].cells[0];
    assert_eq!(cell.borders.len(), 4, "default should have all 4 borders");
}

#[test]
fn cell_border_mixed_across_cells() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![
                vec![
                    TableCell::text("A").border(vec![true, true, false, false]),
                    TableCell::text("B").border(vec![false, true, true, false]),
                ],
            ])
            .widths(vec![TableWidth::Fixed(100.0), TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell_a = &table.rows[0].cells[0];
    let cell_b = &table.rows[0].cells[1];

    assert_eq!(cell_a.borders.len(), 2, "cell A should have 2 borders");
    assert_eq!(cell_b.borders.len(), 2, "cell B should have 2 borders");
}

#[test]
fn cell_border_with_col_span() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![
                TableCell::text("wide").col_span(2).border(vec![true, true, false, true]),
                TableCell::text("tail"),
            ]])
            .widths(vec![
                TableWidth::Fixed(50.0),
                TableWidth::Fixed(60.0),
                TableWidth::Fixed(70.0),
            ]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell = &table.rows[0].cells[0];
    assert_eq!(cell.col_span, 2);
    assert_eq!(cell.borders.len(), 3, "col_span cell should have 3 borders");
}

#[test]
fn cell_border_with_row_span() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![
                vec![
                    TableCell::text("tall").row_span(2).border(vec![true, true, true, false]),
                    TableCell::text("B"),
                ],
                vec![TableCell::text("D")],
            ])
            .widths(vec![TableWidth::Fixed(50.0), TableWidth::Fixed(50.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell = &table.rows[0].cells[0];
    assert_eq!(cell.row_span, 2);
    assert_eq!(cell.borders.len(), 3, "row_span cell should have 3 borders");
}

#[test]
fn cell_border_color_single_color() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![
                TableCell::text("cell")
                    .border_color(vec![String::from("#ff0000")])
            ]])
            .widths(vec![TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell = &table.rows[0].cells[0];
    assert_eq!(cell.border_colors, Some(vec!["#ff0000".to_string()]));
}

#[test]
fn cell_border_color_four_colors() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![
                TableCell::text("cell")
                    .border_color(vec![
                        String::from("#ff0000"),
                        String::from("#00ff00"),
                        String::from("#0000ff"),
                        String::from("#ffff00"),
                    ])
            ]])
            .widths(vec![TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell = &table.rows[0].cells[0];
    assert_eq!(
        cell.border_colors,
        Some(vec![
            "#ff0000".to_string(),
            "#00ff00".to_string(),
            "#0000ff".to_string(),
            "#ffff00".to_string(),
        ])
    );
}

#[test]
fn cell_border_render_produces_valid_pdf() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![
                vec![
                    TableCell::text("A").border(vec![true, true, false, false]),
                    TableCell::text("B").border(vec![false, true, true, false]),
                ],
                vec![
                    TableCell::text("C").border(vec![true, false, false, true]),
                    TableCell::text("D").border(vec![false, false, true, true]),
                ],
            ])
            .widths(vec![TableWidth::Fixed(100.0), TableWidth::Fixed(100.0)]),
        ))
        .build();

    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(pdf.len() > 100);
}

// ============================================================================
// Cell fill color tests
// ============================================================================

#[test]
fn cell_fill_color_none_by_default() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![
                TableCell::text("cell"),
            ]])
            .widths(vec![TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell = &table.rows[0].cells[0];
    assert_eq!(cell.fill_color, None);
}

#[test]
fn cell_fill_opacity_none_by_default() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![
                TableCell::text("cell"),
            ]])
            .widths(vec![TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell = &table.rows[0].cells[0];
    assert_eq!(cell.fill_opacity, None);
}

#[test]
fn cell_fill_color_different_colors_per_cell() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![
                vec![
                    TableCell::text("Red").fill_color("#ff0000"),
                    TableCell::text("Green").fill_color("#00ff00"),
                ],
                vec![
                    TableCell::text("Blue").fill_color("#0000ff"),
                    TableCell::text("None"),
                ],
            ])
            .widths(vec![TableWidth::Fixed(100.0), TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let mut cells: Vec<&pdfmake_rust::LayoutTableCell> = Vec::new();
    for page in &layout.pages {
        for item in &page.items {
            if let LayoutItem::Table(t) = item {
                for row in &t.rows {
                    for cell in &row.cells {
                        cells.push(cell);
                    }
                }
            }
        }
    }

    assert!(cells.len() >= 4, "should have at least 4 cells");
    assert_eq!(cells[0].fill_color, Some("#ff0000".to_string()));
    assert_eq!(cells[1].fill_color, Some("#00ff00".to_string()));
    assert_eq!(cells[2].fill_color, Some("#0000ff".to_string()));
    assert_eq!(cells[3].fill_color, None);
}

#[test]
fn cell_fill_opacity_different_values() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![
                vec![
                    TableCell::text("25%").fill_color("#ff0000").fill_opacity(0.25),
                    TableCell::text("50%").fill_color("#00ff00").fill_opacity(0.5),
                ],
                vec![
                    TableCell::text("75%").fill_color("#0000ff").fill_opacity(0.75),
                    TableCell::text("100%").fill_color("#ffff00").fill_opacity(1.0),
                ],
            ])
            .widths(vec![TableWidth::Fixed(100.0), TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let mut cells: Vec<&pdfmake_rust::LayoutTableCell> = Vec::new();
    for page in &layout.pages {
        for item in &page.items {
            if let LayoutItem::Table(t) = item {
                for row in &t.rows {
                    for cell in &row.cells {
                        cells.push(cell);
                    }
                }
            }
        }
    }

    assert!(cells.len() >= 4, "should have at least 4 cells");
    assert_eq!(cells[0].fill_opacity, Some(0.25));
    assert_eq!(cells[1].fill_opacity, Some(0.5));
    assert_eq!(cells[2].fill_opacity, Some(0.75));
    assert_eq!(cells[3].fill_opacity, Some(1.0));
}

#[test]
fn cell_fill_color_with_col_span() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![
                TableCell::text("wide").col_span(2).fill_color("#ff0000"),
                TableCell::text("tail"),
            ]])
            .widths(vec![
                TableWidth::Fixed(50.0),
                TableWidth::Fixed(60.0),
                TableWidth::Fixed(70.0),
            ]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    assert_eq!(table.rows[0].cells[0].fill_color, Some("#ff0000".to_string()));
    assert_eq!(table.rows[0].cells[0].col_span, 2);
}

#[test]
fn cell_fill_color_with_row_span() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![
                vec![
                    TableCell::text("tall").row_span(2).fill_color("#00ff00"),
                    TableCell::text("B"),
                ],
                vec![TableCell::text("D")],
            ])
            .widths(vec![TableWidth::Fixed(50.0), TableWidth::Fixed(50.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    assert_eq!(table.rows[0].cells[0].fill_color, Some("#00ff00".to_string()));
    assert_eq!(table.rows[0].cells[0].row_span, 2);
}

// ============================================================================
// hLineWhenBroken tests
// ============================================================================

#[test]
fn h_line_when_broken_set_to_false() {
    let table = TableNode::new(vec![vec![TableCell::text("A")]])
        .h_line_when_broken(false);
    assert_eq!(table.h_line_when_broken, Some(false));
}

#[test]
fn h_line_when_broken_set_to_true() {
    let table = TableNode::new(vec![vec![TableCell::text("A")]])
        .h_line_when_broken(true);
    assert_eq!(table.h_line_when_broken, Some(true));
}

#[test]
fn h_line_when_broken_true_through_normalize() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![TableCell::text("A")]])
                .h_line_when_broken(true),
        ))
        .build();

    let normalized = normalize_document(&doc).unwrap();
    match &normalized.blocks[0] {
        NormalizedBlock::Table(table) => {
            assert_eq!(table.h_line_when_broken, Some(true));
        }
        other => panic!("expected table, got {other:?}"),
    }
}

#[test]
fn h_line_when_broken_with_header_rows() {
    let mut rows = vec![
        vec![TableCell::text("Header")],
    ];
    for i in 0..30 {
        rows.push(vec![TableCell::text(format!("Row {i}"))]);
    }

    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(rows)
                .header_rows(1)
                .h_line_when_broken(false),
        ))
        .build();

    let normalized = normalize_document(&doc).unwrap();
    match &normalized.blocks[0] {
        NormalizedBlock::Table(table) => {
            assert_eq!(table.header_rows, Some(1));
            assert_eq!(table.h_line_when_broken, Some(false));
        }
        other => panic!("expected table, got {other:?}"),
    }
}

#[test]
fn h_line_when_broken_render_produces_valid_pdf() {
    let rows: Vec<Vec<TableCell>> = (0..30)
        .map(|i| vec![TableCell::text(format!("Row {i}"))])
        .collect();
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(rows)
                .widths(vec![TableWidth::Star])
                .h_line_when_broken(false),
        ))
        .build();

    let pdf = PdfMake::new().render(&doc).unwrap();
    assert!(pdf.starts_with(b"%PDF-1.4"));
    assert!(pdf.len() > 100);
}

// ============================================================================
// Combined tests
// ============================================================================

#[test]
fn cell_all_enhancements_together() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![
                vec![
                    TableCell::text("Full")
                        .border(vec![true, true, false, false])
                        .border_color(vec![String::from("#ff0000"), String::from("#00ff00")])
                        .fill_color("#0000ff")
                        .fill_opacity(0.5),
                    TableCell::text("Default"),
                ],
            ])
            .widths(vec![TableWidth::Fixed(100.0), TableWidth::Fixed(100.0)]),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    let cell = &table.rows[0].cells[0];
    assert_eq!(cell.fill_color, Some("#0000ff".to_string()));
    assert_eq!(cell.fill_opacity, Some(0.5));
    assert_eq!(cell.border_colors, Some(vec!["#ff0000".to_string(), "#00ff00".to_string()]));
    assert_eq!(cell.borders.len(), 2);
}

#[test]
fn cell_enhancements_with_table_style() {
    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![
                vec![
                    TableCell::text("A").fill_color("#ff0000"),
                    TableCell::text("B"),
                ],
                vec![
                    TableCell::text("C"),
                    TableCell::text("D").fill_color("#00ff00"),
                ],
            ])
            .widths(vec![TableWidth::Fixed(100.0), TableWidth::Fixed(100.0)])
            .table_style(TableStyle::NoBorders),
        ))
        .build();

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

    let mut cells: Vec<&pdfmake_rust::LayoutTableCell> = Vec::new();
    let mut all_borders_empty = true;
    for page in &layout.pages {
        for item in &page.items {
            if let LayoutItem::Table(t) = item {
                if !t.borders.is_empty() {
                    all_borders_empty = false;
                }
                for row in &t.rows {
                    for cell in &row.cells {
                        cells.push(cell);
                    }
                }
            }
        }
    }

    assert!(all_borders_empty, "NoBorders should produce no table-level borders");
    assert!(cells.len() >= 4, "should have at least 4 cells");
    assert_eq!(cells[0].fill_color, Some("#ff0000".to_string()));
    assert_eq!(cells[3].fill_color, Some("#00ff00".to_string()));
}

#[test]
fn cell_enhancements_with_layout_config() {
    let config = TableLayoutConfig {
        h_line_width: Some(2.0),
        v_line_width: Some(1.0),
        ..Default::default()
    };

    let doc = Document::builder()
        .content(DocumentNode::Table(
            TableNode::new(vec![vec![
                TableCell::text("cell")
                    .fill_color("#ff0000")
                    .border_color(vec![String::from("#00ff00")])
            ]])
            .widths(vec![TableWidth::Fixed(100.0)])
            .layout_config(config),
        ))
        .build();

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

    let table = match &layout.pages[0].items[0] {
        LayoutItem::Table(t) => t,
        other => panic!("expected table, got {other:?}"),
    };

    assert!(table.layout_config.is_some());
    let cell = &table.rows[0].cells[0];
    assert_eq!(cell.fill_color, Some("#ff0000".to_string()));
    assert_eq!(cell.border_colors, Some(vec!["#00ff00".to_string()]));
}
