"""Convierte archivos Excel a Markdown con cada sheet como seccion H2."""

import os
from pathlib import Path


# Codigos de error Excel para xlrd
_XL_ERROR_CODES = {
    0: "#NULL!",
    7: "#DIV/0!",
    15: "#VALUE!",
    23: "#REF!",
    29: "#NAME?",
    36: "#NUM!",
    42: "#N/A",
}


def _rows_to_markdown_table(rows: list[list[str]]) -> str:
    """Convierte filas de strings a tabla markdown."""
    if not rows:
        return ""

    header = rows[0]
    col_count = len(header)

    # Normalizar todas las filas al mismo numero de columnas
    normalized = []
    for row in rows:
        if len(row) < col_count:
            row = row + [""] * (col_count - len(row))
        normalized.append(row[:col_count])

    # Escapar pipes en celdas
    def escape(cell: str) -> str:
        return cell.replace("|", "\\|").replace("\n", " ")

    lines = []
    # Header
    lines.append("| " + " | ".join(escape(c) for c in normalized[0]) + " |")
    # Separator
    lines.append("| " + " | ".join("---" for _ in range(col_count)) + " |")
    # Data rows
    for row in normalized[1:]:
        lines.append("| " + " | ".join(escape(c) for c in row) + " |")

    return "\n".join(lines)


def _cell_value_xlrd(cell, workbook) -> str:
    """Convierte una celda xlrd a string segun su tipo."""
    import xlrd

    ctype = cell.ctype

    if ctype in (xlrd.XL_CELL_EMPTY, xlrd.XL_CELL_BLANK):
        return ""
    elif ctype == xlrd.XL_CELL_DATE:
        try:
            dt = xlrd.xldate_as_datetime(cell.value, workbook.datemode)
            if dt.hour == 0 and dt.minute == 0 and dt.second == 0:
                return dt.date().isoformat()
            return dt.isoformat()
        except Exception:
            return str(cell.value)
    elif ctype == xlrd.XL_CELL_BOOLEAN:
        return "TRUE" if cell.value else "FALSE"
    elif ctype == xlrd.XL_CELL_ERROR:
        return _XL_ERROR_CODES.get(int(cell.value), "#ERROR!")
    elif ctype == xlrd.XL_CELL_NUMBER:
        v = cell.value
        if v == int(v):
            return str(int(v))
        return str(v)
    elif ctype == xlrd.XL_CELL_TEXT:
        return str(cell.value)
    else:
        return str(cell.value)


def _sheet_xlrd(sheet, workbook, max_rows: int) -> str:
    """Convierte un sheet xlrd a markdown."""
    nrows = sheet.nrows
    ncols = sheet.ncols

    lines = []
    lines.append(f"## Sheet: {sheet.name}")
    lines.append("")
    lines.append(f"**Dimensions:** {nrows} x {ncols}")
    lines.append("")

    if nrows == 0 or ncols == 0:
        lines.append("*(empty sheet)*")
        return "\n".join(lines)

    display_rows = min(nrows, max_rows)
    rows = []
    for r in range(display_rows):
        row_data = [_cell_value_xlrd(sheet.cell(r, c), workbook) for c in range(ncols)]
        rows.append(row_data)

    lines.append(_rows_to_markdown_table(rows))

    if nrows > max_rows:
        omitted = nrows - max_rows
        lines.append("")
        lines.append(f"*{omitted} rows omitted (max_rows_per_sheet={max_rows})*")

    return "\n".join(lines)


def _cell_value_openpyxl(cell) -> str:
    """Convierte una celda openpyxl a string."""
    v = cell.value
    if v is None:
        return ""
    if isinstance(v, bool):
        return "TRUE" if v else "FALSE"
    if isinstance(v, float):
        if v == int(v):
            return str(int(v))
        return str(v)
    if isinstance(v, int):
        return str(v)
    # Fechas y datetimes
    import datetime
    if isinstance(v, datetime.datetime):
        if v.hour == 0 and v.minute == 0 and v.second == 0:
            return v.date().isoformat()
        return v.isoformat()
    if isinstance(v, datetime.date):
        return v.isoformat()
    return str(v)


def _sheet_openpyxl(ws, max_rows: int) -> str:
    """Convierte un worksheet openpyxl a markdown."""
    all_rows = list(ws.iter_rows())
    nrows = len(all_rows)
    ncols = ws.max_column or 0

    lines = []
    lines.append(f"## Sheet: {ws.title}")
    lines.append("")
    lines.append(f"**Dimensions:** {nrows} x {ncols}")
    lines.append("")

    if nrows == 0 or ncols == 0:
        lines.append("*(empty sheet)*")
        return "\n".join(lines)

    display_rows = min(nrows, max_rows)
    rows = []
    for row in all_rows[:display_rows]:
        row_data = [_cell_value_openpyxl(cell) for cell in row]
        rows.append(row_data)

    lines.append(_rows_to_markdown_table(rows))

    if nrows > max_rows:
        omitted = nrows - max_rows
        lines.append("")
        lines.append(f"*{omitted} rows omitted (max_rows_per_sheet={max_rows})*")

    return "\n".join(lines)


def excel_to_markdown(path: str, max_rows_per_sheet: int = 1000) -> str:
    """Convierte un archivo Excel (.xlsx, .xls, .xlsm) a markdown.

    Cada sheet se convierte en una seccion H2. Las filas se representan
    como tablas markdown. Si el numero de filas supera max_rows_per_sheet,
    el sheet se trunca y se añade una nota.

    Args:
        path: Ruta al archivo Excel (.xlsx, .xls, .xlsm).
        max_rows_per_sheet: Maximo de filas a incluir por sheet (default 1000).

    Returns:
        String markdown con todos los sheets del archivo.

    Raises:
        ValueError: Si la extension no es soportada.
        FileNotFoundError: Si el archivo no existe.
        Exception: Si hay errores leyendo el archivo.
    """
    p = Path(path)
    if not p.exists():
        raise FileNotFoundError(f"File not found: {path}")

    ext = p.suffix.lower()

    if ext == ".xls":
        import xlrd
        wb = xlrd.open_workbook(path)
        sections = []
        for sheet_name in wb.sheet_names():
            sheet = wb.sheet_by_name(sheet_name)
            sections.append(_sheet_xlrd(sheet, wb, max_rows_per_sheet))
        return "\n\n".join(sections)

    elif ext in (".xlsx", ".xlsm"):
        import openpyxl
        wb = openpyxl.load_workbook(path, data_only=True)
        sections = []
        for ws in wb.worksheets:
            sections.append(_sheet_openpyxl(ws, max_rows_per_sheet))
        return "\n\n".join(sections)

    else:
        raise ValueError(f"Unsupported extension '{ext}'. Use .xlsx, .xls, or .xlsm.")
