"""
Unit tests for table column width optimization.
"""

import pytest
from pyWebLayout.layout.table_optimizer import (
    optimize_table_layout,
    sample_table_rows,
    extract_html_column_widths,
    parse_html_width,
    distribute_column_widths,
    get_column_count,
    calculate_table_overhead
)
from pyWebLayout.abstract.block import Table
from pyWebLayout.concrete.table import TableStyle


class TestParseHtmlWidth:
    """Test HTML width parsing."""

    def test_parse_int(self):
        assert parse_html_width(100) == 100

    def test_parse_px_string(self):
        assert parse_html_width("150px") == 150

    def test_parse_plain_number_string(self):
        assert parse_html_width("200") == 200

    def test_parse_percentage_returns_none(self):
        assert parse_html_width("50%") is None

    def test_parse_invalid_string(self):
        assert parse_html_width("invalid") is None

    def test_parse_with_whitespace(self):
        assert parse_html_width("  120px  ") == 120


class TestDistributeColumnWidths:
    """Test column width distribution."""

    def test_distribute_with_no_fixed_columns(self):
        min_widths = [50, 60, 70]
        pref_widths = [100, 120, 140]
        available = 360
        fixed = {}

        result = distribute_column_widths(min_widths, pref_widths, available, fixed)

        # Should use preferred widths (they fit)
        assert result == [100, 120, 140]

    def test_distribute_when_preferred_fits(self):
        min_widths = [50, 50]
        pref_widths = [100, 100]
        available = 250
        fixed = {}

        result = distribute_column_widths(min_widths, pref_widths, available, fixed)

        # Preferred widths fit, extra 50px distributed proportionally (25px each)
        assert result == [125, 125]

    def test_distribute_when_must_use_minimum(self):
        min_widths = [100, 100]
        pref_widths = [200, 200]
        available = 150
        fixed = {}

        result = distribute_column_widths(min_widths, pref_widths, available, fixed)

        # Can't even fit minimum, but force it anyway
        assert result == [100, 100]

    def test_distribute_proportional(self):
        min_widths = [50, 50]
        pref_widths = [200, 100]
        available = 200  # Between min and pref totals
        fixed = {}

        result = distribute_column_widths(min_widths, pref_widths, available, fixed)

        # Should distribute proportionally
        assert len(result) == 2
        assert result[0] + result[1] == 200
        # First column should get more (higher pref)
        assert result[0] > result[1]

    def test_distribute_with_fixed_columns(self):
        min_widths = [50, 50, 50]
        pref_widths = [100, 100, 100]
        available = 300
        fixed = {1: 80}  # Second column fixed at 80

        result = distribute_column_widths(min_widths, pref_widths, available, fixed)

        # Second column should be 80
        assert result[1] == 80
        # Other columns share remaining space
        assert result[0] + result[2] == 220

    def test_distribute_all_fixed(self):
        min_widths = [50, 50]
        pref_widths = [100, 100]
        available = 300
        fixed = {0: 120, 1: 150}

        result = distribute_column_widths(min_widths, pref_widths, available, fixed)

        assert result == [120, 150]

    def test_distribute_empty(self):
        result = distribute_column_widths([], [], 100, {})
        assert result == []


class TestGetColumnCount:
    """Test column counting."""

    def test_empty_table(self):
        table = Table()
        assert get_column_count(table) == 0

    def test_table_with_header(self):
        from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph, Word
        from pyWebLayout.style import Font

        table = Table()
        font = Font(font_size=12)

        row = TableRow()
        for text in ["A", "B", "C"]:
            cell = TableCell(is_header=True)
            para = Paragraph(font)
            para.add_word(Word(text, font))
            cell.add_block(para)
            row.add_cell(cell)

        table.add_row(row, section="header")

        assert get_column_count(table) == 3

    def test_table_with_body(self):
        from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph, Word
        from pyWebLayout.style import Font

        table = Table()
        font = Font(font_size=12)

        row = TableRow()
        for text in ["1", "2"]:
            cell = TableCell()
            para = Paragraph(font)
            para.add_word(Word(text, font))
            cell.add_block(para)
            row.add_cell(cell)

        table.add_row(row, section="body")

        assert get_column_count(table) == 2


class TestSampleTableRows:
    """Test row sampling."""

    def test_sample_small_table(self):
        from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
        from pyWebLayout.abstract.inline import Word
        from pyWebLayout.style import Font

        table = Table()
        font = Font(font_size=12)

        for text in ["1", "2"]:
            row = TableRow()
            cell = TableCell()
            para = Paragraph(font)
            para.add_word(Word(text, font))
            cell.add_block(para)
            row.add_cell(cell)
            table.add_row(row, section="body")

        sampled = sample_table_rows(table, sample_size=5)

        # Should get all rows (only 2)
        assert len(sampled) == 2

    def test_sample_large_table(self):
        from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
        from pyWebLayout.abstract.inline import Word
        from pyWebLayout.style import Font

        table = Table()
        font = Font(font_size=12)

        for i in range(20):
            row = TableRow()
            cell = TableCell()
            para = Paragraph(font)
            para.add_word(Word(str(i), font))
            cell.add_block(para)
            row.add_cell(cell)
            table.add_row(row, section="body")

        sampled = sample_table_rows(table, sample_size=5)

        # Should get only 5 body rows
        assert len(sampled) == 5

    def test_sample_with_header_body_footer(self):
        from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
        from pyWebLayout.abstract.inline import Word
        from pyWebLayout.style import Font

        table = Table()
        font = Font(font_size=12)

        # 3 header rows
        for i in range(3):
            row = TableRow()
            cell = TableCell(is_header=True)
            para = Paragraph(font)
            para.add_word(Word(f"H{i}", font))
            cell.add_block(para)
            row.add_cell(cell)
            table.add_row(row, section="header")

        # 10 body rows
        for i in range(10):
            row = TableRow()
            cell = TableCell()
            para = Paragraph(font)
            para.add_word(Word(f"B{i}", font))
            cell.add_block(para)
            row.add_cell(cell)
            table.add_row(row, section="body")

        # 2 footer rows
        for i in range(2):
            row = TableRow()
            cell = TableCell()
            para = Paragraph(font)
            para.add_word(Word(f"F{i}", font))
            cell.add_block(para)
            row.add_cell(cell)
            table.add_row(row, section="footer")

        sampled = sample_table_rows(table, sample_size=2)

        # Should get 2 from each section = 6 total
        assert len(sampled) == 6


class TestExtractHtmlColumnWidths:
    """Test HTML width extraction."""

    def test_no_widths(self):
        from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
        from pyWebLayout.abstract.inline import Word
        from pyWebLayout.style import Font

        table = Table()
        font = Font(font_size=12)

        row = TableRow()
        for text in ["A", "B"]:
            cell = TableCell()
            para = Paragraph(font)
            para.add_word(Word(text, font))
            cell.add_block(para)
            row.add_cell(cell)
        table.add_row(row, section="body")

        widths = extract_html_column_widths(table)

        assert widths == [None, None]

    def test_cell_width_attributes(self):
        from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
        from pyWebLayout.abstract.inline import Word
        from pyWebLayout.style import Font

        table = Table()
        font = Font(font_size=12)

        row = TableRow()

        cell1 = TableCell()
        cell1.width = "100px"
        para1 = Paragraph(font)
        para1.add_word(Word("A", font))
        cell1.add_block(para1)
        row.add_cell(cell1)

        cell2 = TableCell()
        cell2.width = "150"
        para2 = Paragraph(font)
        para2.add_word(Word("B", font))
        cell2.add_block(para2)
        row.add_cell(cell2)

        table.add_row(row, section="body")

        widths = extract_html_column_widths(table)

        assert widths == [100, 150]


class TestCalculateTableOverhead:
    """Test table overhead calculation."""

    def test_basic_overhead(self):
        style = TableStyle(border_width=1, cell_spacing=0)
        overhead = calculate_table_overhead(3, style)

        # 3 columns = 4 borders (n+1)
        assert overhead == 4

    def test_with_cell_spacing(self):
        style = TableStyle(border_width=1, cell_spacing=5)
        overhead = calculate_table_overhead(3, style)

        # 4 borders + 2 spacings (n-1)
        assert overhead == 4 + 10

    def test_thicker_borders(self):
        style = TableStyle(border_width=3, cell_spacing=0)
        overhead = calculate_table_overhead(2, style)

        # 2 columns = 3 borders * 3px
        assert overhead == 9


class TestOptimizeTableLayout:
    """Test full table optimization."""

    def test_optimize_simple_table(self):
        from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
        from pyWebLayout.abstract.inline import Word
        from pyWebLayout.style import Font

        table = Table()
        font = Font(font_size=12)

        row = TableRow()
        for text in ["Short", "A bit longer text"]:
            cell = TableCell()
            para = Paragraph(font)
            for word in text.split():
                para.add_word(Word(word, font))
            cell.add_block(para)
            row.add_cell(cell)
        table.add_row(row, section="body")

        style = TableStyle()
        widths = optimize_table_layout(table, available_width=400, style=style)

        # Should return 2 column widths
        assert len(widths) == 2
        # Second column should be wider
        assert widths[1] > widths[0]

    def test_optimize_empty_table(self):
        table = Table()

        widths = optimize_table_layout(table, available_width=400)

        assert widths == []

    def test_optimize_respects_sample_size(self):
        from pyWebLayout.abstract.block import TableRow, TableCell, Paragraph
        from pyWebLayout.abstract.inline import Word
        from pyWebLayout.style import Font

        table = Table()
        font = Font(font_size=12)

        # Create 20 rows but only first 5 should be sampled
        for i in range(20):
            row = TableRow()
            cell = TableCell()
            para = Paragraph(font)
            para.add_word(Word(f"Data {i}", font))
            cell.add_block(para)
            row.add_cell(cell)
            table.add_row(row, section="body")

        widths = optimize_table_layout(table, available_width=400, sample_size=5)

        # Should return width for 1 column
        assert len(widths) == 1


if __name__ == '__main__':
    pytest.main([__file__, '-v'])
