"""Ingesta una hoja de un archivo .xlsx a una tabla DuckDB.

Funcion impura: abre el archivo DuckDB destino en modo read-write
(`duckdb.connect(duckdb_path)`, que crea el archivo si no existe), carga la
extension `excel` de DuckDB y materializa la hoja del .xlsx en una tabla con
`read_xlsx`. La conexion se cierra siempre en un bloque try/finally. Devuelve un
dict sin lanzar excepciones, siguiendo el estilo del grupo duckdb del registry:
{status:'ok', ...} en exito y {status:'error', error:str} en fallo.

Camino activo (verificado en DuckDB 1.5.2): extension nativa `excel`. El path del
.xlsx y el nombre de la hoja se pasan como parametros posicionales (marcador `?`)
a `read_xlsx`, por lo que NO se interpolan en el SQL y no hay inyeccion por esa
via. El identificador de tabla destino SI se interpola (CREATE/INSERT no admiten
parametro para el nombre de tabla), asi que se valida contra un regex estricto.

mode='replace' (default) -> `CREATE OR REPLACE TABLE <table> AS SELECT * FROM
read_xlsx(?)`: reemplaza la tabla entera. mode='append' -> crea la tabla si no
existe (`CREATE TABLE IF NOT EXISTS ... AS SELECT ... LIMIT 0` para fijar el
schema) y luego `INSERT INTO <table> SELECT * FROM read_xlsx(?)`.
"""

import re

# Identificador de tabla valido: letras, digitos y guion bajo, sin empezar por
# digito. Rechaza cualquier cosa que pudiera inyectarse en el CREATE/INSERT.
_VALID_IDENT = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")


def excel_to_duckdb(
    xlsx_path: str,
    duckdb_path: str,
    table: str,
    sheet: str = None,
    mode: str = "replace",
) -> dict:
    """Ingesta una hoja de un .xlsx a una tabla DuckDB via la extension excel.

    Args:
        xlsx_path: ruta al archivo .xlsx de origen. Debe existir y ser legible.
            Se pasa como parametro posicional a read_xlsx (no se interpola).
        duckdb_path: ruta al archivo DuckDB destino. Se abre en modo escritura, que
            crea el archivo si no existe. DuckDB es single-writer: si otro proceso
            lo tiene abierto en escritura, falla con error de lock.
        table: nombre de la tabla destino. Se valida contra
            ^[A-Za-z_][A-Za-z0-9_]*$ antes de interpolarlo en el SQL (CREATE/INSERT
            no admiten parametro para el nombre de tabla). Identificador invalido
            devuelve {status:'error', ...} sin tocar la base.
        sheet: nombre de la hoja a leer. None (default) lee la primera hoja del
            libro. Se pasa como parametro posicional (sheet=?) a read_xlsx.
        mode: 'replace' (default) reemplaza la tabla entera con CREATE OR REPLACE
            TABLE AS SELECT. 'append' crea la tabla si no existe y luego inserta
            las filas con INSERT INTO ... SELECT. Cualquier otro valor devuelve
            {status:'error', ...}.

    Returns:
        dict. En exito: {status:'ok', table:str, row_count:int} donde row_count es
        el numero de filas que tiene la tabla tras la ingesta. En error (sin
        lanzar): {status:'error', error:str}.
    """
    if not isinstance(table, str) or not _VALID_IDENT.match(table):
        return {
            "status": "error",
            "error": f"invalid table identifier: {table!r}",
        }
    if mode not in ("replace", "append"):
        return {
            "status": "error",
            "error": f"invalid mode: {mode!r} (expected 'replace' or 'append')",
        }

    quoted = '"' + table.replace('"', '""') + '"'

    # Argumentos de read_xlsx: path siempre, sheet solo si se especifica. Todo
    # como parametros posicionales para evitar inyeccion via el .xlsx/hoja.
    if sheet is not None:
        read_call = "read_xlsx(?, sheet=?)"
        read_params = [xlsx_path, sheet]
    else:
        read_call = "read_xlsx(?)"
        read_params = [xlsx_path]

    conn = None
    try:
        conn = __import__("duckdb").connect(duckdb_path)
        # La extension excel se instala (red la 1a vez) y carga en la conexion.
        conn.execute("INSTALL excel; LOAD excel;")

        if mode == "replace":
            conn.execute(
                f"CREATE OR REPLACE TABLE {quoted} AS SELECT * FROM {read_call}",
                read_params,
            )
        else:  # append
            # Fijamos el schema de la tabla con un SELECT vacio si no existe, sin
            # cargar datos; luego insertamos todas las filas.
            conn.execute(
                f"CREATE TABLE IF NOT EXISTS {quoted} AS "
                f"SELECT * FROM {read_call} LIMIT 0",
                read_params,
            )
            conn.execute(
                f"INSERT INTO {quoted} SELECT * FROM {read_call}",
                read_params,
            )

        conn.commit()
        row_count = conn.execute(f"SELECT COUNT(*) FROM {quoted}").fetchone()[0]
        return {"status": "ok", "table": table, "row_count": int(row_count)}
    except Exception as e:  # noqa: BLE001
        return {"status": "error", "error": str(e)}
    finally:
        if conn is not None:
            conn.close()
