"""Smoke tests for review queue API endpoints on the Production Console.

Tests the GET /api/review-queue and POST /api/review-queue/resolve routes
added to review_server.py as part of Phase 3.
"""

import json
import threading
import urllib.request
import urllib.error
import pytest
from unittest.mock import patch


def _find_free_port():
    """Find an available port."""
    import socket
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind(("127.0.0.1", 0))
        return s.getsockname()[1]


@pytest.fixture
def review_server(tmp_path):
    """Start review_server on a random port with a tmp project dir."""
    # Create the minimal project structure the server expects
    project_name = "test-rq-project"
    projects_root_dir = tmp_path / "projects"
    project_dir = projects_root_dir / project_name
    (project_dir / "state" / "visual" / "plans").mkdir(parents=True, exist_ok=True)
    (project_dir / "output" / "frames").mkdir(parents=True, exist_ok=True)
    (project_dir / "output" / "video").mkdir(parents=True, exist_ok=True)
    (project_dir / "episodes").mkdir(parents=True, exist_ok=True)

    # paths.projects_root() requires a .recoil-data-root sentinel at the
    # projects-root dir (recoil.core.paths._assert_data_root). review_server
    # resolves projects_root() at import time (module-level FsWatcher), so the
    # sentinel must exist before the fresh import below.
    (projects_root_dir / ".recoil-data-root").write_text("recoil-data-root\n")

    port = _find_free_port()

    # Patch projects_root() resolution via env var, plus DEFAULT_PROJECT on the
    # imported module. project_output_dir is patched inline so it routes to tmp.
    with patch.dict("os.environ", {"RECOIL_PROJECTS_ROOT": str(tmp_path / "projects")}, clear=False):

        with patch("recoil.core.paths.project_output_dir", lambda p: tmp_path / "projects" / p / "output"):

            # Import the module fresh (or use existing)
            from editors.review_server import ReviewHandler
            import http.server

            # Patch the module globals
            import editors.review_server as rs
            orig_default_project = rs.DEFAULT_PROJECT
            rs.DEFAULT_PROJECT = project_name

            # Also patch _paths_for_project to use our tmp dir
            orig_paths = rs._paths_for_project

            def patched_paths(project):
                proj = project.lower() if project else project_name
                out = tmp_path / "projects" / proj / "output"
                state_dir = tmp_path / "projects" / proj / "state" / "visual"
                return {
                    "project": proj,
                    "project_dir": tmp_path / "projects" / proj,
                    "output_dir": out,
                    "frames_dir": out / "frames",
                    "video_dir": out / "video",
                    "state_dir": state_dir,
                    "plans_dir": state_dir / "plans",
                    "bible_path": state_dir / "global_bible.json",
                }

            rs._paths_for_project = patched_paths

            server = http.server.HTTPServer(("127.0.0.1", port), ReviewHandler)
            t = threading.Thread(target=server.serve_forever, daemon=True)
            t.start()

            yield {
                "port": port,
                "base_url": f"http://127.0.0.1:{port}",
                "project": project_name,
                "project_dir": project_dir,
                "queue_path": project_dir / "state" / "visual" / "review_queue.jsonl",
            }

            server.shutdown()
            rs.DEFAULT_PROJECT = orig_default_project
            rs._paths_for_project = orig_paths


def test_get_review_queue_empty(review_server):
    """GET /api/review-queue returns empty list when no entries exist."""
    url = f"{review_server['base_url']}/api/review-queue?project={review_server['project']}"
    req = urllib.request.Request(url)
    resp = urllib.request.urlopen(req, timeout=5)
    data = json.loads(resp.read())
    assert data["count"] == 0
    assert data["entries"] == []


def test_get_review_queue_with_entries(review_server):
    """GET /api/review-queue returns pending entries."""
    from recoil.pipeline._lib.review_queue import enqueue
    queue_path = review_server["queue_path"]

    # Enqueue 2 entries
    enqueue(
        queue_path=queue_path, project=review_server["project"],
        episode_id="EP001", shot_id="SH01", run_id="run_test",
        reason="attempts_exhausted", failure_mode="anatomy_face_merge",
        attempts=[], total_cost_usd=0.30,
    )
    enqueue(
        queue_path=queue_path, project=review_server["project"],
        episode_id="EP001", shot_id="SH02", run_id="run_test",
        reason="icu_escalated", failure_mode="unknown",
        attempts=[], total_cost_usd=0.15,
    )

    url = f"{review_server['base_url']}/api/review-queue?project={review_server['project']}"
    resp = urllib.request.urlopen(url, timeout=5)
    data = json.loads(resp.read())
    assert data["count"] == 2
    assert len(data["entries"]) == 2
    shot_ids = {e["shot_id"] for e in data["entries"]}
    assert shot_ids == {"SH01", "SH02"}


def test_post_resolve_approve(review_server):
    """POST /api/review-queue/resolve approves an entry."""
    from recoil.pipeline._lib.review_queue import enqueue, list_pending
    queue_path = review_server["queue_path"]

    entry = enqueue(
        queue_path=queue_path, project=review_server["project"],
        episode_id="EP001", shot_id="SH01", run_id="run_test",
        reason="attempts_exhausted", failure_mode="unknown",
        attempts=[], total_cost_usd=0.10,
    )

    url = f"{review_server['base_url']}/api/review-queue/resolve"
    body = json.dumps({
        "rq_id": entry["rq_id"],
        "resolution": "approved",
        "project": review_server["project"],
    }).encode()
    req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
    resp = urllib.request.urlopen(req, timeout=5)
    data = json.loads(resp.read())
    assert data["ok"] is True

    # Verify entry is no longer pending
    pending = list_pending(queue_path=queue_path)
    assert len(pending) == 0


def test_post_resolve_missing_fields(review_server):
    """POST /api/review-queue/resolve with missing fields returns 400."""
    url = f"{review_server['base_url']}/api/review-queue/resolve"
    body = json.dumps({"rq_id": "rq_missing"}).encode()
    req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
    try:
        urllib.request.urlopen(req, timeout=5)
        assert False, "Expected 400 error"
    except urllib.error.HTTPError as e:
        assert e.code == 400


def test_post_resolve_invalid_resolution(review_server):
    """POST /api/review-queue/resolve with invalid resolution returns 400."""
    url = f"{review_server['base_url']}/api/review-queue/resolve"
    body = json.dumps({
        "rq_id": "rq_test",
        "resolution": "invalid_status",
        "project": review_server["project"],
    }).encode()
    req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"})
    try:
        urllib.request.urlopen(req, timeout=5)
        assert False, "Expected 400 error"
    except urllib.error.HTTPError as e:
        assert e.code == 400
