"""The authorization boundary: default-deny, membership, admin-only, and the
404-not-403 asymmetry for a project the caller cannot see."""

import pytest

from tests.conftest import admin_import_and_publish, as_user, import_package, publish_option


# --- anonymous: default-deny at the middleware layer ------------------------


def test_anonymous_list_projects_401(client):
    resp = client.get("/api/projects")
    assert resp.status_code == 401
    assert resp.json()["error_code"] == "NOT_AUTHENTICATED"


def test_anonymous_review_bundle_401(client, seed):
    resp = client.get(f"/api/projects/{seed.project.id}/review")
    assert resp.status_code == 401
    assert resp.json()["error_code"] == "NOT_AUTHENTICATED"


def test_anonymous_screenshot_401(client, seed):
    resp = client.get(f"/api/projects/{seed.project.id}/options/1/screenshot/desktop")
    assert resp.status_code == 401
    assert resp.json()["error_code"] == "NOT_AUTHENTICATED"


# --- a signed-in caller who is not a member: 404, never 403 -----------------


def test_outsider_get_project_404_not_403(client, seed):
    as_user(client, "t_outsider")
    resp = client.get(f"/api/projects/{seed.project.id}")
    assert resp.status_code == 404
    assert resp.status_code != 403
    assert resp.json()["detail"]["error_code"] == "PROJECT_NOT_FOUND"


def test_outsider_review_bundle_404(client, seed):
    as_user(client, "t_outsider")
    resp = client.get(f"/api/projects/{seed.project.id}/review")
    assert resp.status_code == 404
    assert resp.status_code != 403
    assert resp.json()["detail"]["error_code"] == "PROJECT_NOT_FOUND"


def test_outsider_screenshot_404(client, seed):
    as_user(client, "t_outsider")
    resp = client.get(f"/api/projects/{seed.project.id}/options/1/screenshot/desktop")
    assert resp.status_code == 404
    assert resp.status_code != 403
    assert resp.json()["detail"]["error_code"] == "PROJECT_NOT_FOUND"


# --- a member (reviewer level, no scout.project.manage): admin routes rejected

_ADMIN_ROUTE_CASES = [
    ("get", "/options", None, None),
    ("post", "/import", None, "package"),
    ("get", "/results", None, None),
    ("get", "/report", None, None),
    ("get", "/materials", None, None),
    ("post", "/materials/note", {"title": "t", "body": "hello"}, None),
    ("get", "/export", None, None),
    ("patch", "/options/1", {"descriptor": "x"}, None),
    ("post", "/options/reorder", {"ordered_ids": []}, None),
]


@pytest.mark.parametrize("method, suffix, json_body, files_marker", _ADMIN_ROUTE_CASES)
def test_member_cannot_access_admin_routes(client, seed, method, suffix, json_body, files_marker):
    as_user(client, "t_member")
    url = f"/api/projects/{seed.project.id}{suffix}"
    if files_marker == "package":
        files = {"package": ("package.zip", b"not-a-real-zip", "application/zip")}
        resp = client.post(url, files=files)
    else:
        # client.request(), not the get()/post() shortcuts: httpx's get() does
        # not accept a `json=` kwarg at all (GET conventionally carries no body).
        resp = client.request(method.upper(), url, json=json_body)
    assert resp.status_code == 403
    assert resp.json()["detail"]["error_code"] == "PERMISSION_REQUIRED"


# --- a member CAN read the project and the review bundle ---------------------


def test_member_can_get_project(client, seed):
    as_user(client, "t_member")
    resp = client.get(f"/api/projects/{seed.project.id}")
    assert resp.status_code == 200
    assert resp.json()["id"] == seed.project.id


def test_member_can_get_review_bundle(client, seed):
    as_user(client, "t_member")
    resp = client.get(f"/api/projects/{seed.project.id}/review")
    assert resp.status_code == 200
    assert resp.json()["options"] == []


# --- an admin can reach the admin-only surface -------------------------------


def test_admin_can_list_options(client, seed):
    as_user(client, "t_admin")
    resp = client.get(f"/api/projects/{seed.project.id}/options")
    assert resp.status_code == 200
    assert resp.json() == []


# --- screenshot visibility: draft is admin-only, publish opens it to members -


def test_screenshot_visibility_draft_then_published(client, seed):
    as_user(client, "t_admin")
    import_resp = import_package(client, seed.project, [{"slug": "alpha"}])
    assert import_resp.status_code == 200, import_resp.text
    listing = client.get(f"/api/projects/{seed.project.id}/options").json()
    option_id = next(o["id"] for o in listing if o["slug"] == "alpha")

    # Draft: member is refused, admin can preview.
    as_user(client, "t_member")
    member_resp = client.get(
        f"/api/projects/{seed.project.id}/options/{option_id}/screenshot/desktop"
    )
    assert member_resp.status_code == 404

    as_user(client, "t_admin")
    admin_resp = client.get(
        f"/api/projects/{seed.project.id}/options/{option_id}/screenshot/desktop"
    )
    assert admin_resp.status_code == 200
    assert admin_resp.headers["content-type"] == "image/png"

    # Publish, then a member can see it too.
    publish_resp = publish_option(client, seed.project, option_id)
    assert publish_resp.status_code == 200

    as_user(client, "t_member")
    member_resp_2 = client.get(
        f"/api/projects/{seed.project.id}/options/{option_id}/screenshot/desktop"
    )
    assert member_resp_2.status_code == 200
    assert member_resp_2.headers["content-type"] == "image/png"


def test_admin_import_and_publish_helper_smoke(client, seed):
    """Sanity check on the shared helper other test files rely on."""
    as_user(client, "t_admin")
    ids = admin_import_and_publish(client, seed.project, [{"slug": "smoke"}])
    assert set(ids) == {"smoke"}
    listing = client.get(f"/api/projects/{seed.project.id}/options").json()
    row = next(o for o in listing if o["slug"] == "smoke")
    assert row["status"] == "published"
