"""Shared test fixtures using testcontainers for real Postgres + Redis."""

from __future__ import annotations

import subprocess

import asyncpg
import pytest
import redis.asyncio as aioredis
from testcontainers.postgres import PostgresContainer
from testcontainers.redis import RedisContainer

from loom.db.migrations import run_migrations
from loom.graph.project import create_project

# Module-level containers — started once, shared across all tests
_pg_container: PostgresContainer | None = None
_redis_container: RedisContainer | None = None


def _cleanup_stale_reaper():
    """Remove stale Ryuk reaper containers from previous test runs.

    When a test session is interrupted, the Ryuk reaper container can be left
    in a bad state with unavailable port mappings, causing ConnectionError
    on the next run. This removes any such containers before starting fresh.
    """
    try:
        result = subprocess.run(
            ["docker", "ps", "-a", "--filter", "ancestor=testcontainers/ryuk",
             "--format", "{{.ID}}"],
            capture_output=True, text=True, timeout=10,
        )
        container_ids = result.stdout.strip().split("\n")
        for cid in container_ids:
            if cid:
                subprocess.run(
                    ["docker", "rm", "-f", cid],
                    capture_output=True, timeout=10,
                )
    except (subprocess.TimeoutExpired, FileNotFoundError):
        pass  # Docker not available or too slow — let testcontainers handle it


def pytest_configure(config):
    """Start containers once for the entire test session."""
    global _pg_container, _redis_container
    _cleanup_stale_reaper()
    _pg_container = PostgresContainer("postgres:16-alpine")
    _pg_container.start()
    _redis_container = RedisContainer("redis:7-alpine")
    _redis_container.start()


def pytest_unconfigure(config):
    """Stop containers at end of session."""
    global _pg_container, _redis_container
    if _pg_container:
        _pg_container.stop()
    if _redis_container:
        _redis_container.stop()


@pytest.fixture
async def pool():
    """Function-scoped asyncpg pool — migrations + clean slate each test."""
    dsn = _pg_container.get_connection_url().replace("+psycopg2", "")
    p = await asyncpg.create_pool(dsn, min_size=2, max_size=5)
    await run_migrations(p)
    # Clean all data (order respects foreign keys)
    await p.execute("DELETE FROM auth_events")
    await p.execute("DELETE FROM agents")
    await p.execute("DELETE FROM workflow_steps")
    await p.execute("DELETE FROM workflow_runs")
    await p.execute("DELETE FROM skill_runs")
    await p.execute("DELETE FROM escalations")
    await p.execute("DELETE FROM events")
    await p.execute("DELETE FROM task_deps")
    await p.execute("DELETE FROM tasks")
    await p.execute("DELETE FROM projects")
    yield p
    await p.close()


@pytest.fixture
async def redis_conn():
    """Function-scoped Redis connection — flushed each test."""
    host = _redis_container.get_container_host_ip()
    port = _redis_container.get_exposed_port(6379)
    r = aioredis.Redis(host=host, port=int(port), decode_responses=True)
    await r.flushdb()
    yield r
    await r.aclose()


@pytest.fixture
async def project(pool):
    """Create a test project, returns project_id string."""
    record = await create_project(pool, "test-project", "Test")
    return str(record["id"])
