"""Tests para pg_create_table_from_rows.

Requieren un PostgreSQL real. Si PG_TEST_DSN no esta definida, los tests que tocan
la DB se saltan. Cada test crea una tabla con nombre aleatorio y la elimina.

    PG_TEST_DSN="postgresql://user:pass@localhost:5433/trends" \
        python/.venv/bin/python3 -m pytest \
        python/functions/infra/pg_create_table_from_rows_test.py
"""

import os
import sys
import uuid
from datetime import date, datetime

import pytest

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from infra.pg_create_table_from_rows import (  # noqa: E402
    _pg_type,
    pg_create_table_from_rows,
)

PG_TEST_DSN = os.environ.get("PG_TEST_DSN")
requires_pg = pytest.mark.skipif(
    not PG_TEST_DSN, reason="PG_TEST_DSN no definido: se omiten los tests de Postgres"
)


@pytest.fixture
def table_name():
    """Nombre de tabla aleatorio; la elimina al terminar (si existe)."""
    name = "pg_ctfr_t_" + uuid.uuid4().hex[:12]
    yield name
    if PG_TEST_DSN:
        import psycopg2

        conn = psycopg2.connect(PG_TEST_DSN)
        try:
            with conn.cursor() as cur:
                cur.execute(f"DROP TABLE IF EXISTS {name}")
            conn.commit()
        finally:
            conn.close()


def test_skip_sin_pg_test_dsn():
    """skip sin PG_TEST_DSN."""
    if not PG_TEST_DSN:
        pytest.skip("PG_TEST_DSN no definido")
    assert PG_TEST_DSN


def test_infiere_tipos_desde_valores():
    """infiere tipos desde valores: _pg_type es puro, sin DB."""
    assert _pg_type(True) == "BOOLEAN"
    assert _pg_type(3) == "BIGINT"
    assert _pg_type(1.5) == "DOUBLE PRECISION"
    assert _pg_type(datetime(2026, 1, 1, 12, 0)) == "TIMESTAMP"
    assert _pg_type(date(2026, 1, 1)) == "DATE"
    assert _pg_type("hola") == "TEXT"
    assert _pg_type(None) is None  # None no determina tipo


def test_identificador_invalido_devuelve_status_error():
    """identificador invalido devuelve status error sin tocar DB."""
    res = pg_create_table_from_rows(
        "postgresql://x/y", "mala tabla", [{"a": 1}]
    )
    assert res["status"] == "error"


@requires_pg
def test_columna_con_todo_none_queda_text(table_name):
    """columna con todo none queda text: tipos correctos y created True."""
    rows = [
        {"id": 1, "ratio": 0.5, "flag": True, "note": None},
        {"id": 2, "ratio": 0.9, "flag": False, "note": None},
    ]
    res = pg_create_table_from_rows(PG_TEST_DSN, table_name, rows)
    assert res["status"] == "ok"
    assert res["created"] is True
    assert res["columns"] == {
        "id": "BIGINT",
        "ratio": "DOUBLE PRECISION",
        "flag": "BOOLEAN",
        "note": "TEXT",  # toda la columna es None -> TEXT por defecto
    }


@requires_pg
def test_primary_key_se_anade(table_name):
    """primary key se anade: el upsert posterior puede usar ON CONFLICT."""
    res = pg_create_table_from_rows(
        PG_TEST_DSN, table_name,
        [{"email": "a@x.com", "name": "A"}], primary_key=["email"],
    )
    assert res["status"] == "ok"
    # Verifica que existe la PK consultando el catalogo.
    import psycopg2

    conn = psycopg2.connect(PG_TEST_DSN)
    try:
        with conn.cursor() as cur:
            cur.execute(
                "SELECT count(*) FROM information_schema.table_constraints "
                "WHERE table_name = %s AND constraint_type = 'PRIMARY KEY'",
                (table_name,),
            )
            assert cur.fetchone()[0] == 1
    finally:
        conn.close()


@requires_pg
def test_idempotente_created_false_la_segunda_vez(table_name):
    """idempotente created false la segunda vez."""
    rows = [{"id": 1, "name": "x"}]
    first = pg_create_table_from_rows(PG_TEST_DSN, table_name, rows)
    second = pg_create_table_from_rows(PG_TEST_DSN, table_name, rows)
    assert first["status"] == "ok" and first["created"] is True
    assert second["status"] == "ok" and second["created"] is False
