"""pdf_add_table — añade una tabla con headers y filas al documento PDF."""

import sys
import os

_types_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", "python", "types", "infra")
sys.path.insert(0, _types_dir)
from pdf_doc import PDFDoc
from pdf_style import PDFStyle


def _parse_hex_color(hex_color: str) -> tuple[int, int, int]:
    h = hex_color.lstrip("#")
    if len(h) == 3:
        h = "".join(c * 2 for c in h)
    return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)


def pdf_add_table(
    doc: PDFDoc,
    headers: list[str],
    rows: list[list[str]],
    col_widths: list[float] | None = None,
    header_style: PDFStyle | None = None,
    row_style: PDFStyle | None = None,
    border: int = 1,
    row_height: float = 8.0,
    alternate_bg: bool = True,
) -> PDFDoc:
    """Añade una tabla con headers y filas de datos al documento PDF.

    Las columnas se distribuyen uniformemente si no se especifican anchos.
    Soporta colores alternados en filas, bordes configurables y estilos
    diferenciados para headers y filas de datos.

    Args:
        doc: PDFDoc con pagina activa.
        headers: lista de strings con los nombres de columna.
        rows: lista de listas de strings con los datos de cada fila.
        col_widths: lista de anchos de columna en mm. None = distribucion uniforme.
        header_style: PDFStyle para la fila de headers. None usa bold 10pt.
        row_style: PDFStyle para filas de datos. None usa 10pt normal.
        border: grosor/tipo de borde (0=sin borde, 1=borde completo).
        row_height: altura de cada celda en mm. Por defecto 8.
        alternate_bg: si True, alterna fondo gris claro en filas pares.

    Returns:
        PDFDoc con la tabla añadida.
    """
    fpdf = doc.fpdf

    if not headers:
        return doc

    # Anchos de columna
    available_width = fpdf.epw
    n_cols = len(headers)
    if col_widths is None:
        col_widths = [available_width / n_cols] * n_cols
    else:
        # Rellenar si faltan columnas
        while len(col_widths) < n_cols:
            col_widths.append(available_width / n_cols)

    # Estilos
    if header_style is None:
        header_style = PDFStyle(
            font_family="Helvetica-Bold",
            font_size=10.0,
            color="#000000",
            alignment="left",
        )
    if row_style is None:
        row_style = PDFStyle(font_size=10.0, color="#000000", alignment="left")

    def set_font_from_style(s: PDFStyle):
        family = s.font_family
        bold = "-Bold" in family or "-BoldOblique" in family
        italic = "-Oblique" in family or "-BoldOblique" in family
        base = family.split("-")[0]
        st = ("B" if bold else "") + ("I" if italic else "")
        fpdf.set_font(base, style=st, size=s.font_size)
        r, g, b = _parse_hex_color(s.color)
        fpdf.set_text_color(r, g, b)

    def align_char(a: str) -> str:
        return {"left": "L", "center": "C", "right": "R", "justify": "J"}.get(a, "L")

    # Header row
    set_font_from_style(header_style)
    fpdf.set_fill_color(230, 230, 230)
    fpdf.set_draw_color(180, 180, 180)

    for i, (header, width) in enumerate(zip(headers, col_widths)):
        fpdf.cell(
            width,
            row_height,
            str(header),
            border=border,
            align=align_char(header_style.alignment),
            fill=True,
        )
    fpdf.ln()

    # Data rows
    set_font_from_style(row_style)
    fpdf.set_text_color(*_parse_hex_color(row_style.color))

    for row_idx, row in enumerate(rows):
        fill = alternate_bg and (row_idx % 2 == 1)
        if fill:
            fpdf.set_fill_color(245, 245, 245)
        else:
            fpdf.set_fill_color(255, 255, 255)

        for col_idx, (cell, width) in enumerate(zip(row, col_widths)):
            fpdf.cell(
                width,
                row_height,
                str(cell) if cell is not None else "",
                border=border,
                align=align_char(row_style.alignment),
                fill=True,
            )
        fpdf.ln()

    fpdf.ln(2)
    return doc
