"""Tests for PostgresClient.execute_raw()."""
from __future__ import annotations

import sys
from pathlib import Path

import pytest

parent_dir = Path(__file__).parent.parent.parent
if str(parent_dir) not in sys.path:
    sys.path.insert(0, str(parent_dir))

from db_core.clients.postgresql import PostgresClient

pytestmark = pytest.mark.pg


@pytest.mark.asyncio
async def test_execute_raw_select(db_client: PostgresClient):
    """Execute a raw SELECT and receive rows as dicts."""
    rows = await db_client.execute_raw("SELECT 1 AS num")
    assert rows == [{"num": 1}]


@pytest.mark.asyncio
async def test_execute_raw_select_existing_table(db_client: PostgresClient):
    """Execute a raw SELECT against the seeded users table."""
    rows = await db_client.execute_raw(
        "SELECT name, email FROM users WHERE age > :min_age",
        {"min_age": 25},
    )
    assert len(rows) >= 3  # Alice(30), Bob(25), Charlie(35), Diana(28)
    alice = [r for r in rows if r["name"] == "Alice Smith"]
    assert len(alice) == 1


@pytest.mark.asyncio
async def test_execute_raw_insert(db_client: PostgresClient):
    """Execute a raw INSERT and verify the result via SELECT."""
    rowcount = await db_client.execute_raw(
        "INSERT INTO users (name, email, age) VALUES (:name, :email, :age)",
        {"name": "Temp User", "email": "temp@example.com", "age": 99},
    )
    assert rowcount == 1

    # Verify the row was inserted
    rows = await db_client.execute_raw(
        "SELECT * FROM users WHERE email = :email",
        {"email": "temp@example.com"},
    )
    assert len(rows) == 1
    assert rows[0]["name"] == "Temp User"


@pytest.mark.asyncio
async def test_execute_raw_update(db_client: PostgresClient):
    """Execute a raw UPDATE and verify rowcount."""
    rowcount = await db_client.execute_raw(
        "UPDATE users SET age = :new_age WHERE email = :email",
        {"new_age": 31, "email": "alice@example.com"},
    )
    assert rowcount == 1


@pytest.mark.asyncio
async def test_execute_raw_delete(db_client: PostgresClient):
    """Execute a raw DELETE and verify the row is gone."""
    # Insert a sacrificial row
    await db_client.execute_raw(
        "INSERT INTO users (name, email, age) VALUES (:name, :email, :age)",
        {"name": "Delete Me", "email": "delete.me@example.com", "age": 0},
    )

    rowcount = await db_client.execute_raw(
        "DELETE FROM users WHERE email = :email",
        {"email": "delete.me@example.com"},
    )
    assert rowcount == 1


@pytest.mark.asyncio
async def test_execute_raw_ddl_create_drop_table(db_client: PostgresClient):
    """Execute CREATE TABLE and DROP TABLE via raw SQL."""
    # Create
    result = await db_client.execute_raw(
        "CREATE TABLE IF NOT EXISTS _test_exec_raw (id SERIAL PRIMARY KEY, val TEXT)"
    )
    # DDL may return None (no rows, rowcount is -1 or 0 depending on IF NOT EXISTS)
    assert result is not None or result == -1 or result == 0

    # Insert a row to verify the table works
    rowcount = await db_client.execute_raw(
        "INSERT INTO _test_exec_raw (val) VALUES (:val)",
        {"val": "hello"},
    )
    assert rowcount == 1

    # Drop
    result = await db_client.execute_raw("DROP TABLE IF EXISTS _test_exec_raw")
    assert result is not None or result == -1 or result == 0


@pytest.mark.asyncio
async def test_execute_raw_multi_statement(db_client: PostgresClient):
    """Execute multiple statements in a single call."""
    sql = """
    CREATE TABLE IF NOT EXISTS _test_multi_a (id SERIAL PRIMARY KEY, x INT);
    CREATE TABLE IF NOT EXISTS _test_multi_b (id SERIAL PRIMARY KEY, y TEXT);
    """
    result = await db_client.execute_raw(sql, multi_statement=True)
    # Multi-statement DDL always returns None (raw asyncpg connection)
    assert result is None

    # Verify both tables exist
    await db_client.execute_raw("INSERT INTO _test_multi_a (x) VALUES (1)")
    await db_client.execute_raw("INSERT INTO _test_multi_b (y) VALUES ('ok')")

    rows_a = await db_client.execute_raw("SELECT * FROM _test_multi_a")
    rows_b = await db_client.execute_raw("SELECT * FROM _test_multi_b")
    assert len(rows_a) == 1
    assert len(rows_b) == 1

    # Cleanup — drop individually
    await db_client.execute_raw("DROP TABLE IF EXISTS _test_multi_a")
    await db_client.execute_raw("DROP TABLE IF EXISTS _test_multi_b")


@pytest.mark.asyncio
async def test_execute_raw_no_params_none(db_client: PostgresClient):
    """Passing None as params should work (treated as empty dict)."""
    rows = await db_client.execute_raw("SELECT 1 AS ok", params=None)
    assert rows == [{"ok": 1}]
