"""Tests para ingest_gsc_search_analytics.

Sin red ni credenciales: se mockean `gsc_auth`, `pull_gsc_search_analytics` y
`duckdb_to_postgres` sobre los símbolos ya importados en el módulo del pipeline.
DuckDB es embebido, así que `duckdb_execute` + `duckdb_upsert` se ejercitan de
verdad sobre un archivo temporal (tmp_path) y se verifican leyendo con
`duckdb_query_readonly`.
"""

import os
from datetime import date, timedelta
from unittest.mock import patch

import pytest

from pipelines import ingest_gsc_search_analytics as mod
from pipelines.ingest_gsc_search_analytics import ingest_gsc_search_analytics
from infra.duckdb_query_readonly import duckdb_query_readonly

# Filas fake tal como las devuelve pull_gsc_search_analytics con
# dimensions=["date","query","page"]: clave "date" (a renombrar), no "data_date".
_FAKE_RAW = [
    {
        "date": "2026-06-10",
        "query": "zapatillas running",
        "page": "https://ejemplo.com/running",
        "clicks": 12,
        "impressions": 340,
        "ctr": 0.0353,
        "position": 4.2,
    },
    {
        "date": "2026-06-11",
        "query": "ofertas verano",
        "page": "https://ejemplo.com/ofertas",
        "clicks": 5,
        "impressions": 120,
        "ctr": 0.0417,
        "position": 7.1,
    },
]


def _run(db_path, **kwargs):
    """Invoca el pipeline con todos los externos mockeados."""
    with patch.object(mod, "gsc_auth", return_value=object()) as m_auth, patch.object(
        mod, "pull_gsc_search_analytics", return_value=list(_FAKE_RAW)
    ) as m_pull, patch.object(
        mod, "duckdb_to_postgres", return_value={"status": "ok", "rows_synced": 2}
    ) as m_pg:
        res = ingest_gsc_search_analytics(
            site_url="sc-domain:ejemplo.com",
            duckdb_path=db_path,
            pg_dsn="postgresql://u:p@localhost:5432/seo",
            **kwargs,
        )
    return res, m_auth, m_pull, m_pg


def test_renombra_date_a_data_date_y_persiste_en_duckdb(tmp_path):
    db = os.path.join(str(tmp_path), "seo.duckdb")
    res, _, _, m_pg = _run(db)

    assert res["status"] == "ok"
    assert res["rows_pulled"] == 2
    # El espejo a Postgres se invocó en modo replace.
    m_pg.assert_called_once()
    assert m_pg.call_args.kwargs.get("mode") == "replace"

    # (1)+(2) El renombrado date->data_date y la persistencia: leemos la tabla.
    q = duckdb_query_readonly(
        db,
        "SELECT data_date, query, page, clicks, search_type, country "
        "FROM gsc_search_analytics ORDER BY data_date",
    )
    assert q["status"] == "ok"
    assert q["row_count"] == 2
    first = q["rows"][0]
    # data_date existe y vale la fecha de la fila raw "date" renombrada.
    assert str(first["data_date"]) == "2026-06-10"
    assert first["query"] == "zapatillas running"
    assert first["clicks"] == 12
    # Defaults estables para dims no pedidas.
    assert first["search_type"] == "web"
    assert first["country"] == ""


def test_resolucion_fechas_por_defecto(tmp_path):
    db = os.path.join(str(tmp_path), "seo.duckdb")
    res, _, m_pull, _ = _run(db, lookback_days=5)

    today = date.today()
    expected_end = (today - timedelta(days=3)).isoformat()
    expected_start = (today - timedelta(days=3 + 5)).isoformat()

    assert res["end_date"] == expected_end
    assert res["start_date"] == expected_start
    # Y se pasaron a pull en ese orden (service, site_url, start, end).
    args = m_pull.call_args.args
    assert args[2] == expected_start
    assert args[3] == expected_end


def test_upsert_idempotente_no_duplica(tmp_path):
    db = os.path.join(str(tmp_path), "seo.duckdb")
    # Dos corridas con las mismas filas fake: la clave UNIQUE evita duplicados.
    _run(db)
    _run(db)

    q = duckdb_query_readonly(db, "SELECT COUNT(*) AS n FROM gsc_search_analytics")
    assert q["status"] == "ok"
    assert q["rows"][0]["n"] == 2


def test_falta_site_url_da_value_error(tmp_path, monkeypatch):
    monkeypatch.delenv("GSC_SITE_URL", raising=False)
    db = os.path.join(str(tmp_path), "seo.duckdb")
    res = ingest_gsc_search_analytics(
        site_url="",
        duckdb_path=db,
        pg_dsn="postgresql://u:p@localhost:5432/seo",
    )
    assert res["status"] == "error"
    assert "site_url" in res["error"]


def test_falta_pg_dsn_da_value_error(tmp_path, monkeypatch):
    monkeypatch.delenv("SEO_DSN", raising=False)
    db = os.path.join(str(tmp_path), "seo.duckdb")
    res = ingest_gsc_search_analytics(
        site_url="sc-domain:ejemplo.com",
        duckdb_path=db,
        pg_dsn="",
    )
    assert res["status"] == "error"
    assert "pg_dsn" in res["error"]


if __name__ == "__main__":
    raise SystemExit(pytest.main([__file__, "-v"]))
