"""Tests para duckdb_upsert."""

import os
import sys
import tempfile

import duckdb

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from infra.duckdb_upsert import duckdb_upsert  # noqa: E402


def _fresh_db():
    """Crea un .duckdb temporal con tabla t(k PK, a, b) y devuelve su path."""
    fd, path = tempfile.mkstemp(suffix=".duckdb")
    os.close(fd)
    os.remove(path)  # DuckDB crea el archivo limpio.
    con = duckdb.connect(path)
    con.execute("CREATE TABLE t (k INTEGER PRIMARY KEY, a VARCHAR, b VARCHAR)")
    con.close()
    return path


def _select_row(path, k):
    con = duckdb.connect(path, read_only=True)
    try:
        return con.execute("SELECT k, a, b FROM t WHERE k = ?", [k]).fetchone()
    finally:
        con.close()


def test_upsert_fila_nueva_inserta():
    path = _fresh_db()
    try:
        res = duckdb_upsert(
            path, "t", [{"k": 1, "a": "a1", "b": "b1"}], key_cols=["k"]
        )
        assert res == {"status": "ok", "inserted": 1, "updated": 0}
        assert _select_row(path, 1) == (1, "a1", "b1")
    finally:
        os.remove(path)


def test_update_cols_selectivo_no_pisa_columnas_excluidas():
    path = _fresh_db()
    try:
        duckdb_upsert(path, "t", [{"k": 1, "a": "a1", "b": "b1"}], key_cols=["k"])
        # Re-upsert de la misma k cambiando a y b en el dict, pero solo
        # autorizando actualizar 'a'. 'b' debe conservar el valor viejo.
        res = duckdb_upsert(
            path,
            "t",
            [{"k": 1, "a": "a2", "b": "b2"}],
            key_cols=["k"],
            update_cols=["a"],
        )
        assert res == {"status": "ok", "inserted": 0, "updated": 1}
        assert _select_row(path, 1) == (1, "a2", "b1")  # a cambio, b NO
    finally:
        os.remove(path)


def test_update_cols_vacio_do_nothing_no_cambia_existente():
    path = _fresh_db()
    try:
        duckdb_upsert(path, "t", [{"k": 1, "a": "a1", "b": "b1"}], key_cols=["k"])
        res = duckdb_upsert(
            path,
            "t",
            [{"k": 1, "a": "X", "b": "Y"}],
            key_cols=["k"],
            update_cols=[],
        )
        assert res == {"status": "ok", "inserted": 0, "updated": 1}
        assert _select_row(path, 1) == (1, "a1", "b1")  # intacta
    finally:
        os.remove(path)


def test_varias_filas_a_la_vez_mezcla_insert_y_update():
    path = _fresh_db()
    try:
        duckdb_upsert(path, "t", [{"k": 1, "a": "a1", "b": "b1"}], key_cols=["k"])
        res = duckdb_upsert(
            path,
            "t",
            [
                {"k": 1, "a": "a1b", "b": "b1b"},  # update
                {"k": 2, "a": "a2", "b": "b2"},  # insert
                {"k": 3, "a": "a3", "b": "b3"},  # insert
            ],
            key_cols=["k"],
        )
        assert res == {"status": "ok", "inserted": 2, "updated": 1}
        assert _select_row(path, 1) == (1, "a1b", "b1b")
        assert _select_row(path, 2) == (2, "a2", "b2")
        assert _select_row(path, 3) == (3, "a3", "b3")
    finally:
        os.remove(path)


def test_rows_vacio_devuelve_cero():
    path = _fresh_db()
    try:
        res = duckdb_upsert(path, "t", [], key_cols=["k"])
        assert res == {"status": "ok", "inserted": 0, "updated": 0}
    finally:
        os.remove(path)


def test_columnas_inconsistentes_devuelve_error():
    path = _fresh_db()
    try:
        res = duckdb_upsert(
            path,
            "t",
            [{"k": 1, "a": "a1", "b": "b1"}, {"k": 2, "a": "a2"}],
            key_cols=["k"],
        )
        assert res["status"] == "error"
    finally:
        os.remove(path)


def test_identificador_invalido_devuelve_error():
    path = _fresh_db()
    try:
        res = duckdb_upsert(
            path, "t; DROP TABLE t", [{"k": 1, "a": "a1", "b": "b1"}], key_cols=["k"]
        )
        assert res["status"] == "error"
    finally:
        os.remove(path)
