"""Ejecuta una sentencia de escritura (INSERT/UPDATE/DELETE/DDL) contra DuckDB.

Funcion impura: abre un archivo DuckDB con `duckdb.connect(db_path)` en modo
read-write (crea el archivo si no existe, cosa que el modo escritura de DuckDB
permite). Ejecuta UNA sentencia con parametros posicionales (DuckDB usa el
marcador `?`), hace commit y cierra la conexion siempre en un bloque try/finally.

Es el primitivo de escritura del grupo `duckdb` del registry; complementa a
`duckdb_query_readonly_py_infra`, que es solo lectura.

Devuelve un dict sin lanzar excepciones, siguiendo el estilo del grupo
(`{status:'ok', ...}` en exito, `{status:'error', error:str}` en fallo). En exito
incluye `rowcount`: el numero de filas afectadas por la sentencia. DuckDB no expone
un `rowcount` fiable en su cursor (siempre devuelve -1), pero tras un
INSERT/UPDATE/DELETE el `fetchall()` del cursor devuelve `[(n,)]` con el conteo;
de ahi se extrae. Para DDL u operaciones que no reportan filas, `rowcount` queda
en -1 y eso NUNCA hace fallar la funcion.
"""


def _affected_rowcount(cursor) -> int:
    """Extrae el numero de filas afectadas de un cursor DuckDB de escritura.

    Estrategia robusta para DuckDB:
      1. Si `cursor.rowcount` esta disponible y es >= 0, usarlo.
      2. Si no, intentar `cursor.fetchall()`: tras INSERT/UPDATE/DELETE DuckDB
         devuelve `[(n,)]` con el conteo. Se extrae el primer entero.
      3. Si nada aplica (DDL, sin filas), devolver -1.

    Nunca lanza: cualquier problema al leer el conteo cae a -1.
    """
    try:
        rc = getattr(cursor, "rowcount", -1)
        if isinstance(rc, int) and rc >= 0:
            return rc
    except Exception:  # noqa: BLE001
        pass

    try:
        fetched = cursor.fetchall()
    except Exception:  # noqa: BLE001
        return -1

    if fetched and fetched[0]:
        candidate = fetched[0][0]
        if isinstance(candidate, int):
            return candidate

    return -1


def duckdb_execute(db_path: str, sql: str, params: list = None) -> dict:
    """Ejecuta una sentencia de escritura DuckDB en conexion read-write.

    Args:
        db_path: ruta al archivo DuckDB. En modo escritura DuckDB crea el archivo
            si no existe. Un directorio inexistente o un lock de otro proceso
            devuelve {status:'error', ...}.
        sql: sentencia SQL de escritura (INSERT/UPDATE/DELETE/DDL). Usa el
            marcador `?` para parametros posicionales.
        params: lista de parametros posicionales para el SQL en orden. None
            (default) significa sin parametros.

    Returns:
        dict. En exito: {status:'ok', rowcount:int} donde rowcount es el numero
        de filas afectadas (o -1 cuando la sentencia no reporta filas, p.ej. DDL).
        En error (sin lanzar): {status:'error', error:str}.
    """
    conn = None
    try:
        conn = __import__("duckdb").connect(db_path)
        cursor = conn.execute(sql, params if params is not None else [])
        rowcount = _affected_rowcount(cursor)
        # DuckDB autocommitea por defecto, pero llamar a commit es seguro e
        # idempotente: garantiza la durabilidad de la escritura.
        conn.commit()
        return {"status": "ok", "rowcount": rowcount}
    except Exception as e:  # noqa: BLE001
        return {"status": "error", "error": str(e)}
    finally:
        if conn is not None:
            conn.close()
