"""
Viewer server tests: run_id validation and path-traversal hardening.

Uses tmp dir via AGENTDBG_DATA_DIR; no real home directory touched.
"""

import pytest
from fastapi.testclient import TestClient

from agentdbg.config import load_config
from agentdbg.server import create_app
from agentdbg import storage


# ---------------------------------------------------------------------------
# Validator: reject path traversal and invalid format
# ---------------------------------------------------------------------------


def test_run_id_rejects_path_traversal_strings():
    """validate_run_id_format rejects path traversal and unsafe strings."""
    invalid = [
        "../x",
        "..",
        "/etc/passwd",
        "a/../b",
        "%2f",
        "..%2F",
        ".\\..",
        "run_id/../other",
        "..\\",
        "",
        "   ",
        "x" * 64,
        "00000000-0000-0000-0000-000000000001",  # UUID but v1, not v4
        "A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11",  # valid v4 but not canonical (uppercase)
    ]
    for run_id in invalid:
        with pytest.raises(ValueError, match="invalid run_id"):
            storage.validate_run_id_format(run_id)


def test_run_id_accepts_valid_ids_used_by_repo():
    """validate_run_id_format accepts canonical UUIDv4 strings as generated by the repo."""
    # Same format as storage.create_run: str(uuid.uuid4()) -> lowercase with hyphens
    valid = [
        "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
        "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    ]
    for run_id in valid:
        got = storage.validate_run_id_format(run_id)
        assert got == run_id


def test_run_id_accepts_valid_uuid_from_fixture(temp_data_dir):
    """Validator accepts a run_id produced by create_run (real repo format)."""
    config = load_config()
    meta = storage.create_run(run_name="test", config=config)
    run_id = meta["run_id"]
    assert storage.validate_run_id_format(run_id) == run_id


# ---------------------------------------------------------------------------
# Server: invalid run_id returns 400
# ---------------------------------------------------------------------------


def test_server_returns_400_for_invalid_run_id(temp_data_dir):
    """GET /api/runs/{run_id} with invalid run_id returns 400 (path traversal rejected by validator)."""
    client = TestClient(create_app())
    r = client.get("/api/runs/not-a-uuid")
    assert r.status_code == 400
    assert "invalid run_id" in (r.json().get("detail") or "")


def test_server_returns_400_for_invalid_run_id_events(temp_data_dir):
    """GET /api/runs/{run_id}/events with invalid run_id returns 400."""
    client = TestClient(create_app())
    r = client.get("/api/runs/not-a-uuid/events")
    assert r.status_code == 400, r.json()
    assert "invalid run_id" in (r.json().get("detail") or "")


def test_server_returns_404_for_valid_but_missing_run_id(temp_data_dir):
    """Valid UUID but missing run directory returns 404."""
    client = TestClient(create_app())
    r = client.get("/api/runs/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
    assert r.status_code == 404
    assert "run not found" in (r.json().get("detail") or "")


def test_server_returns_200_for_valid_run_id(temp_data_dir):
    """Valid run_id with existing run returns 200 and metadata."""
    config = load_config()
    meta = storage.create_run(run_name="server_test", config=config)
    run_id = meta["run_id"]
    client = TestClient(create_app())
    r = client.get(f"/api/runs/{run_id}")
    assert r.status_code == 200
    assert r.json().get("run_id") == run_id


def test_server_paths_endpoint_returns_run_json_path(temp_data_dir):
    """GET /api/runs/{run_id}/paths returns local run.json path."""
    config = load_config()
    meta = storage.create_run(run_name="paths_test", config=config)
    run_id = meta["run_id"]
    client = TestClient(create_app())

    r = client.get(f"/api/runs/{run_id}/paths")
    assert r.status_code == 200, r.text
    data = r.json()
    assert data.get("run_id") == run_id
    paths = data.get("paths") or {}
    assert paths.get("run_json") == meta["paths"]["run_json"]


def test_server_paths_endpoint_invalid_and_missing_run_id(temp_data_dir):
    """Paths endpoint mirrors run meta semantics for invalid/missing IDs."""
    client = TestClient(create_app())

    r = client.get("/api/runs/not-a-uuid/paths")
    assert r.status_code == 400
    assert "invalid run_id" in (r.json().get("detail") or "")

    r2 = client.get("/api/runs/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11/paths")
    assert r2.status_code == 404
    assert "run not found" in (r2.json().get("detail") or "")


def test_server_can_rename_run(temp_data_dir):
    """POST /api/runs/{run_id}/rename updates run.json run_name on disk."""
    config = load_config()
    meta = storage.create_run(run_name="before", config=config)
    run_id = meta["run_id"]
    client = TestClient(create_app())

    r = client.post(f"/api/runs/{run_id}/rename", json={"run_name": "after"})
    assert r.status_code == 200, r.text
    data = r.json()
    assert data.get("run_id") == run_id
    assert data.get("run_name") == "after"

    # Disk contents updated
    reloaded = storage.load_run_meta(run_id, config)
    assert reloaded["run_name"] == "after"


def test_server_rename_invalid_and_missing_run_id(temp_data_dir):
    """Rename endpoint validates run_id and missing runs."""
    client = TestClient(create_app())

    r = client.post("/api/runs/not-a-uuid/rename", json={"run_name": "x"})
    assert r.status_code == 400
    assert "invalid run_id" in (r.json().get("detail") or "")

    r2 = client.post(
        "/api/runs/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11/rename",
        json={"run_name": "x"},
    )
    assert r2.status_code == 404
    assert "run not found" in (r2.json().get("detail") or "")


def test_server_can_delete_run(temp_data_dir):
    """DELETE /api/runs/{run_id} removes the run directory."""
    config = load_config()
    meta = storage.create_run(run_name="to_delete", config=config)
    run_id = meta["run_id"]
    run_dir = meta["paths"]["run_dir"]
    assert run_dir, "create_run should return run_dir path"

    client = TestClient(create_app())

    r = client.delete(f"/api/runs/{run_id}")
    assert r.status_code == 204, r.text

    # Run directory should be gone
    from pathlib import Path

    assert not Path(run_dir).exists()


def test_server_delete_invalid_and_missing_run_id(temp_data_dir):
    """DELETE endpoint validates run_id and missing runs."""
    client = TestClient(create_app())

    r = client.delete("/api/runs/not-a-uuid")
    assert r.status_code == 400
    assert "invalid run_id" in (r.json().get("detail") or "")

    r2 = client.delete("/api/runs/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
    assert r2.status_code == 404
    assert "run not found" in (r2.json().get("detail") or "")
