"""Playwright test fixtures for Fabra UI testing.

Run with:
    uv run pytest tests/ui/ --headed  # See browser
    uv run pytest tests/ui/           # Headless

Note: UI tests use Playwright which has its own event loop management.
These tests are isolated from other async tests to prevent event loop pollution.
"""

import pytest
import subprocess
import time
import socket
import os
import signal
import asyncio
from typing import Generator


@pytest.fixture(scope="module", autouse=True)
def reset_event_loop():
    """Reset the event loop after UI tests to prevent pollution of subsequent tests."""
    yield
    # After UI tests complete, try to reset any loop state
    try:
        loop = asyncio.get_event_loop()
        if loop.is_running():
            loop.stop()
        if not loop.is_closed():
            loop.close()
    except Exception:
        pass
    # Force creation of a new event loop for subsequent tests
    asyncio.set_event_loop(asyncio.new_event_loop())


def is_port_in_use(port: int) -> bool:
    """Check if a port is in use."""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        return s.connect_ex(("localhost", port)) == 0


def wait_for_port(port: int, timeout: int = 30) -> bool:
    """Wait for a port to be available."""
    for _ in range(timeout):
        if is_port_in_use(port):
            return True
        time.sleep(1)
    return False


@pytest.fixture(scope="session")
def ui_server() -> Generator[str, None, None]:
    """Start the Fabra UI server for testing.

    This starts both:
    1. FastAPI backend on port 8502
    2. Next.js frontend on port 8501
    """
    frontend_port = 8501
    api_port = 8502
    base_url = f"http://localhost:{frontend_port}"

    # Check if already running
    if is_port_in_use(frontend_port):
        yield base_url
        return

    # Get paths
    project_root = os.path.dirname(
        os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    )
    ui_next_dir = os.path.join(project_root, "src", "fabra", "ui-next")
    features_file = os.path.join(project_root, "examples", "rag_chatbot.py")

    # Start FastAPI backend
    api_process = subprocess.Popen(
        [
            "uv",
            "run",
            "python",
            "-c",
            f"""
import sys
sys.path.insert(0, '{project_root}')
from fabra.ui_server import run_server
run_server('{features_file}', port={api_port}, host='127.0.0.1')
""",
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        cwd=project_root,
    )

    # Wait for API to start
    if not wait_for_port(api_port, timeout=15):
        api_process.kill()
        raise RuntimeError(f"API server failed to start on port {api_port}")

    # Start Next.js frontend
    frontend_process = subprocess.Popen(
        ["npm", "run", "dev", "--", "-p", str(frontend_port)],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        cwd=ui_next_dir,
        preexec_fn=os.setsid if hasattr(os, "setsid") else None,
    )

    # Wait for frontend to start
    if not wait_for_port(frontend_port, timeout=30):
        frontend_process.kill()
        api_process.kill()
        raise RuntimeError(f"Next.js server failed to start on port {frontend_port}")

    # Give extra time for Next.js to fully compile
    time.sleep(3)

    yield base_url

    # Cleanup
    try:
        if hasattr(os, "killpg"):
            os.killpg(os.getpgid(frontend_process.pid), signal.SIGTERM)
        else:
            frontend_process.terminate()
    except Exception:
        pass

    try:
        api_process.terminate()
        api_process.wait(timeout=5)
    except Exception:
        api_process.kill()


@pytest.fixture(scope="session")
def browser_context_args(browser_context_args: dict) -> dict:
    """Configure browser context for tests."""
    return {
        **browser_context_args,
        "viewport": {"width": 1280, "height": 720},
        "ignore_https_errors": True,
    }
