import pytest
import sys
import uuid
from pathlib import Path

# Add parent of db_core to path so we can import db_core as a package
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
from db_core.query import Query

pytestmark = pytest.mark.pg


@pytest.mark.asyncio
async def test_connection(db_client: PostgresClient):
    """Test database connection"""
    assert db_client.engine is not None
    assert db_client.session_maker is not None


@pytest.mark.asyncio
async def test_disconnect(db_client: PostgresClient):
    """Test database disconnect"""
    # Connection should still be active
    result = await db_client.table("users").limit(1).get()
    assert result.count() == 1


@pytest.mark.asyncio
async def test_raw_query(db_client: PostgresClient):
    """Test raw query generation"""
    query = Query(table="users", selects=["name", "email"])
    sql, params = db_client.raw_query(query)
    
    assert "SELECT" in sql.upper()
    assert "users" in sql
    assert "name" in sql
    assert "email" in sql


@pytest.mark.asyncio
async def test_raw_query_with_where(db_client: PostgresClient):
    """Test raw query generation with WHERE clause"""
    qb = db_client.table("users").where("age", ">", 25)
    sql, params = db_client.raw_query(qb.query)
    
    assert "WHERE" in sql.upper() or "where" in sql
    assert "age" in sql


@pytest.mark.asyncio
async def test_explain(db_client: PostgresClient):
    """Test EXPLAIN functionality"""
    query = Query(table="users")
    explain_output = await db_client.explain(query)
    
    assert explain_output is not None
    assert len(explain_output) > 0
    # Check for common EXPLAIN output patterns
    assert any(keyword in explain_output for keyword in ["Seq Scan", "Index", "Scan", "scan"])


@pytest.mark.asyncio
async def test_explain_analyze(db_client: PostgresClient):
    """Test EXPLAIN ANALYZE functionality"""
    query = Query(table="users")
    explain_output = await db_client.explain(query, analyze=True)
    
    assert explain_output is not None
    assert len(explain_output) > 0


@pytest.mark.asyncio
async def test_table_method(db_client: PostgresClient):
    """Test table method returns QueryBuilder"""
    qb = db_client.table("users")
    assert qb is not None
    assert qb.query.table == "users"
    assert hasattr(qb, "where")
    assert hasattr(qb, "select")
    assert hasattr(qb, "get")


@pytest.mark.asyncio
async def test_execute_simple_query(db_client: PostgresClient):
    """Test executing a simple query"""
    query = Query(table="users", selects=["*"])
    result = await db_client._execute(query)
    
    assert result is not None
    assert result.count() == 4


@pytest.mark.asyncio
async def test_execute_query_with_limit(db_client: PostgresClient):
    """Test executing a query with limit"""
    query = Query(table="users", selects=["*"], limit=2)
    result = await db_client._execute(query)
    
    assert result.count() == 2


@pytest.mark.asyncio
async def test_find_one_and_find_many(db_client: PostgresClient):
    """Test DBClient convenience methods find_one and find_many"""
    alice = await db_client.find_one("users", {"email": "alice@example.com"})
    assert alice is not None
    assert alice["name"] == "Alice Smith"
    assert alice["age"] == 30

    missing = await db_client.find_one("users", {"email": "missing@example.com"})
    assert missing is None

    users = await db_client.find_many("users", {"age": 25})
    assert isinstance(users, list)
    assert len(users) == 1
    assert users[0]["name"] == "Bob Jones"

    top_two = await db_client.find_many("users", limit=2, order_by=["-age"])
    assert len(top_two) == 2
    assert top_two[0]["age"] >= top_two[1]["age"]


@pytest.mark.asyncio
async def test_create_method_inserts_new_user(db_client: PostgresClient):
    unique_email = f"eve.save.{uuid.uuid4().hex}@example.com"
    new_user = {
        "name": "Eve Adams",
        "email": unique_email,
        "age": 22,
    }

    created = await db_client.create("users", new_user, returning=["id", "name", "email", "age"])
    assert created is not None
    assert created["id"] is not None
    assert created["email"] == unique_email
    assert created["name"] == "Eve Adams"
    assert created["age"] == 22

    fetched = await db_client.find_by_id("users", created["id"])
    assert fetched is not None
    assert fetched["email"] == unique_email

    deleted = await db_client.delete_by_id("users", created["id"])
    assert deleted == 1


@pytest.mark.asyncio
async def test_save_method_updates_existing_user(db_client: PostgresClient):
    unique_email = f"frank.save.{uuid.uuid4().hex}@example.com"
    created = await db_client.create(
        "users",
        {"name": "Frank Miller", "email": unique_email, "age": 29},
        returning=["id", "email", "age"]
    )
    assert created is not None

    updated = await db_client.save(
        "users",
        {"id": created["id"], "name": "Frank M.", "age": 30}
    )
    assert updated is not None
    assert updated["id"] == created["id"]
    assert updated["name"] == "Frank M."
    assert updated["age"] == 30

    deleted = await db_client.delete_by_id("users", created["id"])
    assert deleted == 1


@pytest.mark.asyncio
async def test_pool_settings(db_client: PostgresClient):
    """Test that connection pool is configured"""
    assert db_client.engine.pool.size() >= 0
