"""
Table column width optimization for pyWebLayout.

This module provides intelligent column width distribution for tables,
ensuring optimal space usage while respecting content constraints.
"""

from typing import List, Tuple, Optional, Dict
from pyWebLayout.abstract.block import Table, TableRow


def optimize_table_layout(table: Table,
                          available_width: int,
                          sample_size: int = 5,
                          style=None) -> List[int]:
    """
    Optimize column widths for a table.

    Strategy:
    1. Check for HTML width overrides (colspan, width attributes)
    2. Sample first ~5 rows to estimate column requirements (performance)
    3. Calculate minimum width for each column (longest unbreakable word)
    4. Calculate preferred width for each column (no wrapping)
    5. If total preferred fits: use preferred
    6. Otherwise: distribute available space proportionally
    7. Ensure no column < min_width

    Note: Hyphenation threshold is controlled by Font.min_hyphenation_width,
    not passed as a parameter here to avoid duplication.

    Args:
        table: The table to optimize
        available_width: Total width available
        sample_size: Number of rows to sample for measurement (default 5)
        style: Optional table style for border/padding calculations

    Returns:
        List of optimized column widths
    """
    from pyWebLayout.concrete.dynamic_page import DynamicPage

    n_cols = get_column_count(table)
    if n_cols == 0:
        return []

    # Account for table borders/padding overhead
    if style:
        overhead = calculate_table_overhead(n_cols, style)
        available_for_content = available_width - overhead
    else:
        # Default border overhead
        border_width = 1
        overhead = border_width * (n_cols + 1)
        available_for_content = available_width - overhead

    # Phase 0: Check for HTML width overrides
    html_widths = extract_html_column_widths(table)
    fixed_columns = {i: width for i, width in enumerate(html_widths) if width is not None}

    # Phase 1: Sample rows and measure constraints for each column
    min_widths = []  # Minimum without breaking words (Font handles hyphenation)
    pref_widths = []  # Preferred (no wrapping)

    # Sample first ~5 rows from each section (header, body, footer)
    sampled_rows = sample_table_rows(table, sample_size)

    for col_idx in range(n_cols):
        # Check if this column has HTML width override
        if col_idx in fixed_columns:
            fixed_width = fixed_columns[col_idx]
            min_widths.append(fixed_width)
            pref_widths.append(fixed_width)
            continue

        col_min = 50  # Absolute minimum
        col_pref = 50

        # Check sampled cells in this column
        for row in sampled_rows:
            cells = list(row.cells())
            if col_idx >= len(cells):
                continue

            cell = cells[col_idx]

            # Create a DynamicPage for this cell with no padding/borders
            # (we're just measuring content, not rendering a full page)
            from pyWebLayout.style.page_style import PageStyle
            measurement_style = PageStyle(padding=(0, 0, 0, 0), border_width=0)
            cell_page = DynamicPage(style=measurement_style)

            # Add cell content to page
            layout_cell_content(cell_page, cell)

            # Measure minimum width (Font's min_hyphenation_width controls breaking)
            # DynamicPage returns pure content width (no padding since we set it to 0)
            # TableRenderer will add cell padding later
            cell_min = cell_page.get_min_width()
            col_min = max(col_min, cell_min)

            # Measure preferred width (no wrapping)
            cell_pref = cell_page.get_preferred_width()
            col_pref = max(col_pref, cell_pref)

        min_widths.append(col_min)
        pref_widths.append(col_pref)

    # Phase 2: Distribute width (respecting fixed columns)
    return distribute_column_widths(
        min_widths,
        pref_widths,
        available_for_content,
        fixed_columns
    )


def layout_cell_content(page, cell):
    """
    Layout cell content onto a DynamicPage.

    This adds all blocks from the cell (paragraphs, images, etc.)
    as children of the page so they can be measured.

    Args:
        page: DynamicPage to add content to
        cell: TableCell containing blocks
    """
    from pyWebLayout.concrete.text import Line, Text
    from pyWebLayout.style.fonts import Font
    from pyWebLayout.style import FontWeight, Alignment
    from pyWebLayout.abstract.block import Paragraph, Heading
    from PIL import Image as PILImage, ImageDraw

    # Default font for measurement
    font_size = 12
    font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
    font = Font(font_path=font_path, font_size=font_size)

    # Create a minimal draw context for Text measurement
    # (Text needs this for width calculation)
    dummy_img = PILImage.new('RGB', (1, 1))
    dummy_draw = ImageDraw.Draw(dummy_img)

    # Get all blocks from the cell
    for block in cell.blocks():
        if isinstance(block, (Paragraph, Heading)):
            # Get words from the block
            word_items = block.words() if callable(block.words) else block.words
            words = list(word_items)

            if not words:
                continue

            # Create a line for measurement
            line = Line(
                spacing=(3, 6),  # word spacing
                origin=(0, 0),
                size=(1000, 20),  # Large size for measurement
                draw=dummy_draw,
                font=font,
                halign=Alignment.LEFT
            )

            # Add all words to estimate width
            for word_item in words:
                # Handle word tuples (index, word_obj)
                if isinstance(word_item, tuple) and len(word_item) >= 2:
                    word_obj = word_item[1]
                else:
                    word_obj = word_item

                # Extract text from the word
                word_text = word_obj.text if hasattr(word_obj, 'text') else str(word_obj)

                # Create Text object for the word
                # Text constructor: (text, style, draw)
                text_obj = Text(
                    text=word_text,
                    style=font,  # Font is the style
                    draw=dummy_draw
                )

                line._text_objects.append(text_obj)

            # Add line to page
            page.add_child(line)


def get_column_count(table: Table) -> int:
    """
    Get the number of columns in a table.

    Args:
        table: The table to analyze

    Returns:
        Number of columns
    """
    all_rows = list(table.all_rows())
    if not all_rows:
        return 0

    # Get from first row
    first_row = all_rows[0][1]
    return first_row.cell_count


def sample_table_rows(table: Table, sample_size: int) -> List[TableRow]:
    """
    Sample first ~sample_size rows from each table section.

    Args:
        table: The table to sample
        sample_size: Number of rows to sample per section

    Returns:
        List of sampled rows
    """
    sampled = []

    for section in ["header", "body", "footer"]:
        section_rows = [row for sec, row in table.all_rows() if sec == section]
        # Take first sample_size rows (or fewer if section is smaller)
        sampled.extend(section_rows[:sample_size])

    return sampled


def extract_html_column_widths(table: Table) -> List[Optional[int]]:
    """
    Extract column width overrides from HTML attributes.

    Checks for:
    - <col width="100px"> elements
    - <td width="100px"> in first row
    - <th width="100px"> in header

    Args:
        table: The table to check

    Returns:
        List of widths (None for auto-layout columns)
    """
    n_cols = get_column_count(table)
    widths = [None] * n_cols

    # Check for <col> elements with width
    if hasattr(table, 'col_widths'):
        for i, width in enumerate(table.col_widths):
            if width is not None:
                widths[i] = parse_html_width(width)

    # Check first row cells for width attributes
    all_rows = list(table.all_rows())
    if all_rows:
        first_row = all_rows[0][1]
        cells = list(first_row.cells())
        for i, cell in enumerate(cells):
            if i < len(widths) and hasattr(cell, 'width') and cell.width is not None:
                widths[i] = parse_html_width(cell.width)

    return widths


def parse_html_width(width_value) -> Optional[int]:
    """
    Parse HTML width value (e.g., "100px", "20%", "100").

    Args:
        width_value: HTML width attribute value

    Returns:
        Width in pixels, or None if percentage/invalid
    """
    if isinstance(width_value, int):
        return width_value

    if isinstance(width_value, str):
        # Remove whitespace
        width_value = width_value.strip()

        # Percentage widths not supported yet
        if '%' in width_value:
            return None

        # Parse pixel values
        if width_value.endswith('px'):
            try:
                return int(width_value[:-2])
            except ValueError:
                return None

        # Plain number
        try:
            return int(width_value)
        except ValueError:
            return None

    return None


def distribute_column_widths(min_widths: List[int],
                             pref_widths: List[int],
                             available_width: int,
                             fixed_columns: Dict[int, int]) -> List[int]:
    """
    Distribute width among columns, respecting fixed column widths.

    Args:
        min_widths: Minimum width for each column
        pref_widths: Preferred width for each column
        available_width: Total width available
        fixed_columns: Dict mapping column index to fixed width

    Returns:
        List of final column widths
    """
    n_cols = len(min_widths)
    if n_cols == 0:
        return []

    # Calculate available space for flexible columns
    fixed_total = sum(fixed_columns.values())
    flexible_available = available_width - fixed_total

    # Get indices of flexible columns
    flexible_cols = [i for i in range(n_cols) if i not in fixed_columns]

    if not flexible_cols:
        # All columns fixed - return as-is
        return [fixed_columns.get(i, min_widths[i]) for i in range(n_cols)]

    # Calculate totals for flexible columns only
    flex_min_total = sum(min_widths[i] for i in flexible_cols)
    flex_pref_total = sum(pref_widths[i] for i in flexible_cols)

    # Distribute space among flexible columns
    widths = [0] * n_cols

    # Set fixed columns
    for i, width in fixed_columns.items():
        widths[i] = width

    # Distribute to flexible columns
    if flex_pref_total <= flexible_available:
        # Preferred widths fit - distribute remaining space proportionally
        extra_space = flexible_available - flex_pref_total

        if extra_space > 0 and flex_pref_total > 0:
            # Distribute extra space proportionally based on preferred widths
            for i in flexible_cols:
                proportion = pref_widths[i] / flex_pref_total
                widths[i] = int(pref_widths[i] + (extra_space * proportion))
        else:
            # No extra space, just use preferred widths
            for i in flexible_cols:
                widths[i] = pref_widths[i]
    elif flex_min_total > flexible_available:
        # Can't satisfy minimum - force it anyway (graceful degradation)
        for i in flexible_cols:
            widths[i] = min_widths[i]
    else:
        # Proportional distribution between min and pref
        extra_space = flexible_available - flex_min_total
        flex_pref_over_min = flex_pref_total - flex_min_total

        for i in flexible_cols:
            if flex_pref_over_min > 0:
                pref_over_min = pref_widths[i] - min_widths[i]
                proportion = pref_over_min / flex_pref_over_min
                extra = extra_space * proportion
                widths[i] = int(min_widths[i] + extra)
            else:
                widths[i] = int(min_widths[i])

    return widths


def calculate_table_overhead(n_cols: int, style) -> int:
    """
    Calculate the pixel overhead for table borders and spacing.

    Args:
        n_cols: Number of columns
        style: TableStyle object

    Returns:
        Total pixel overhead
    """
    # Border on each side of each column + outer borders
    border_overhead = style.border_width * (n_cols + 1)

    # Cell spacing if any
    spacing_overhead = style.cell_spacing * (n_cols - 1) if n_cols > 1 else 0

    return border_overhead + spacing_overhead
