"""The HTTP layer: persona permissions, 404-vs-403 discipline, and the
routes' error translation. Engine logic itself is covered in test_engine."""

from app import bw_accounts as bwa
from tests.conftest import OWNER, as_user


def _code(resp):
    body = resp.json()
    d = body.get("detail")
    return d.get("error_code") if isinstance(d, dict) else body.get("error_code")


def _mk_team(kit, username="tina"):
    """A team-persona level carrying the three app permissions + kit basics."""
    kit.bwa.create_level(OWNER, "team", permissions=[
        "items.act_team", "items.compose", "workflows.author",
        "instances.create", "instances.grant"])
    kit.member(username, "team", all_instances=True)
    return username


def _mk_client(kit, punchlist_id, username="cleo"):
    kit.member(username, "member")
    kit.grant(username, punchlist_id, "member")
    return username


def _mk_punchlist(client, title="Brentwood onboarding"):
    as_user(client, OWNER)
    r = client.post("/api/punchlists", json={"title": title})
    assert r.status_code == 201, r.text
    return r.json()["id"]


def _add_ga4(client, pid):
    r = client.post(f"/api/punchlists/{pid}/items",
                    json={"template_key": "ga4_admin_access",
                          "variables": {"who_needs_access": ["rian@rian.ca"]}})
    assert r.status_code == 201, r.text
    return r.json()["id"]


def test_create_and_visibility_discipline(client, kit):
    pid = _mk_punchlist(client)
    assert pid == "brentwood-onboarding"          # slugified id

    cleo = _mk_client(kit, pid)
    as_user(client, cleo)
    assert client.get(f"/api/punchlists/{pid}").status_code == 200

    # another member with NO grant: 404, never 403
    kit.member("mallory", "member")
    as_user(client, "mallory")
    r = client.get(f"/api/punchlists/{pid}")
    assert r.status_code == 404 and _code(r) == "NO_SUCH_INSTANCE"
    # and an item id inside it is invisible the same way
    as_user(client, OWNER)
    iid = _add_ga4(client, pid)
    as_user(client, "mallory")
    assert client.post(f"/api/items/{iid}/actions", json={"action": "primary"}).status_code == 404


def test_compose_needs_permission(client, kit):
    pid = _mk_punchlist(client)
    cleo = _mk_client(kit, pid)
    as_user(client, cleo)
    r = client.post(f"/api/punchlists/{pid}/items",
                    json={"template_key": "ga4_admin_access",
                          "variables": {"who_needs_access": ["rian@rian.ca"]}})
    assert r.status_code == 403 and _code(r) == "FORBIDDEN"


def test_client_acts_and_perceives(client, kit):
    pid = _mk_punchlist(client)
    as_user(client, OWNER)
    iid = _add_ga4(client, pid)
    cleo = _mk_client(kit, pid)

    as_user(client, cleo)
    body = client.get(f"/api/punchlists/{pid}").json()
    assert body["is_team"] is False
    item = body["items"][0]
    assert item["client_state"] == "todo"
    assert item["step"]["targets"] == [{"value": "rian@rian.ca", "done": False}]
    assert "rian@rian.ca" in item["label"]                # goal label, interpolated

    r = client.post(f"/api/items/{iid}/actions",
                    json={"action": "primary", "target": "rian@rian.ca"})
    assert r.status_code == 200
    assert r.json()["item"]["client_state"] == "waiting_on_us"

    # the team step is not the client's to act on
    r = client.post(f"/api/items/{iid}/actions", json={"action": "primary"})
    assert r.status_code == 403 and _code(r) == "NOT_YOUR_STEP"


def test_team_queue_and_events_are_team_only(client, kit):
    pid = _mk_punchlist(client)
    as_user(client, OWNER)
    iid = _add_ga4(client, pid)
    cleo = _mk_client(kit, pid)
    tina = _mk_team(kit)

    as_user(client, cleo)
    client.post(f"/api/items/{iid}/actions",
                json={"action": "trouble", "message": "no admin tab visible"})
    assert client.get("/api/team/queue").status_code == 403
    assert client.get(f"/api/items/{iid}/events").status_code == 403

    as_user(client, tina)
    q = client.get("/api/team/queue").json()["queue"]
    assert [x["id"] for x in q] == [iid]
    assert q[0]["status"] == "needs_attention"
    assert q[0]["punchlist"]["id"] == pid

    r = client.post(f"/api/items/{iid}/actions",
                    json={"action": "resolve_flag", "message": "Try the gear icon top-left"})
    assert r.status_code == 200
    ev = client.get(f"/api/items/{iid}/events").json()["events"]
    assert ev[-1]["action"] == "resolve_flag" and "gear icon" in ev[-1]["message"]


def test_workflow_validate_and_publish_permissions(client, kit):
    tina = _mk_team(kit)
    as_user(client, tina)

    bad = {"key": "x", "title": "X", "start": "nope",
           "steps": [{"id": "a", "owner": "client", "headline": "h",
                      "primary": {"label": "Done", "to": "$done"}}]}
    r = client.post("/api/workflows/validate", json={"spec": bad})
    assert r.status_code == 200 and r.json()["ok"] is False
    assert any("start step 'nope'" in e for e in r.json()["errors"])

    good = dict(bad, start="a")
    r = client.post("/api/workflows/validate", json={"spec": good})
    assert r.json()["ok"] is True

    r = client.post("/api/workflows", json={"spec": good})
    assert r.status_code == 201 and r.json()["version"] == 1

    # a plain member may neither validate nor publish
    kit.member("bob", "member")
    as_user(client, "bob")
    assert client.post("/api/workflows/validate", json={"spec": good}).status_code == 403
    assert client.post("/api/workflows", json={"spec": good}).status_code == 403


def test_invalid_publish_returns_full_error_list(client, kit):
    tina = _mk_team(kit)
    as_user(client, tina)
    bad = {"key": "x", "title": "X", "start": "zz",
           "steps": [{"id": "a", "owner": "client", "headline": "{{whoo}}",
                      "primary": {"label": "Done", "to": "bb"}}]}
    r = client.post("/api/workflows", json={"spec": bad})
    assert r.status_code == 422 and _code(r) == "SPEC_INVALID"
    assert len(r.json()["detail"]["errors"]) >= 3


def test_seen_and_done_since_seen(client, kit):
    pid = _mk_punchlist(client)
    as_user(client, OWNER)
    r = client.post(f"/api/punchlists/{pid}/items", json={
        "spec": {"key": "quick", "title": "Quick", "start": "a",
                 "steps": [{"id": "a", "owner": "client", "headline": "Do it",
                            "primary": {"label": "Done", "to": "$done"}}]}})
    iid = r.json()["id"]
    cleo = _mk_client(kit, pid)

    as_user(client, cleo)
    client.post(f"/api/punchlists/{pid}/seen")
    client.post(f"/api/items/{iid}/actions", json={"action": "primary"})
    item = client.get(f"/api/punchlists/{pid}").json()["items"][0]
    assert item["client_state"] == "done" and item["done_since_seen"] is True
    client.post(f"/api/punchlists/{pid}/seen")
    item = client.get(f"/api/punchlists/{pid}").json()["items"][0]
    assert item["done_since_seen"] is False, "seen: the pop-out fires once"
