"""A portal user's sidebar (GET /menus) follows Menus › role visibility for a
role that uses it; a role that has never shown any menu lists what its
grants open. A grant needed for data (e.g. a form's dropdown reading a
datasource) no longer puts that datasource's menu in the sidebar of a
role that hid it."""

import uuid

from app.core.auth import create_access_token
from app.models.identity import Role, User, UserRole, Vendor
from app.models.navigation import Menu, MenuRole
from tests.conftest import AsyncSessionLocal


async def _setup(tenant_id: uuid.UUID) -> dict:
    tag = uuid.uuid4().hex[:6]
    office, page, other = uuid.uuid4(), uuid.uuid4(), uuid.uuid4()
    async with AsyncSessionLocal() as db:
        async with db.begin():
            org = Vendor(tenant_id=tenant_id, organization_name=f"Public {tag}")
            db.add(org)
            grants = {"masters": [str(office)], "pages": [str(page)]}
            public = Role(tenant_id=tenant_id, name=f"Public {tag}", scope="portal", action_matrix=grants)
            clerk = Role(tenant_id=tenant_id, name=f"Clerk {tag}", scope="portal", action_matrix=grants)
            db.add_all([public, clerk])
            await db.flush()

            def menu(label, target_type, target_id):
                m = Menu(tenant_id=tenant_id, label=f"{label} {tag}", icon="Home", link_type="existing_feature", audience="vendor", status="active", target_type=target_type, target_id=target_id)
                db.add(m)
                return m

            offices, apply, elsewhere = menu("Offices", "master", office), menu("Apply", "page", page), menu("Other", "page", other)
            await db.flush()
            # Public uses visibility: only Apply is ticked. Clerk never ticked anything.
            db.add_all([MenuRole(menu_id=m.id, role_id=public.id, visible=m is apply) for m in (offices, apply, elsewhere)])
            db.add_all([MenuRole(menu_id=m.id, role_id=clerk.id, visible=False) for m in (offices, apply, elsewhere)])

            users = {}
            for name, roles in (("public", [public]), ("clerk", [clerk]), ("both", [public, clerk])):
                u = User(tenant_id=tenant_id, vendor_id=org.id, name=name, email=f"{name}-{tag}@example.com", status="active")
                db.add(u)
                await db.flush()
                db.add_all([UserRole(user_id=u.id, role_id=r.id) for r in roles])
                users[name] = u.id
    as_ = lambda n: {"Authorization": f"Bearer {create_access_token(tenant_id=tenant_id, user_id=users[n], vendor_id=org.id)}"}  # noqa: E731
    return {"as": as_, "tag": tag}


async def _labels(client, headers, tag) -> set[str]:
    resp = await client.get("/menus", headers=headers)
    assert resp.status_code == 200
    return {m["label"].removesuffix(f" {tag}") for m in resp.json() if m["label"].endswith(tag)}


async def test_a_role_that_hides_a_granted_menu_doesnt_list_it(client, test_tenant):
    s = await _setup(test_tenant["tenant_id"])
    assert await _labels(client, s["as"]("public"), s["tag"]) == {"Apply"}


async def test_a_role_without_visibility_settings_lists_what_it_can_open(client, test_tenant):
    s = await _setup(test_tenant["tenant_id"])
    assert await _labels(client, s["as"]("clerk"), s["tag"]) == {"Offices", "Apply"}


async def test_several_roles_list_what_any_of_them_lists(client, test_tenant):
    s = await _setup(test_tenant["tenant_id"])
    assert await _labels(client, s["as"]("both"), s["tag"]) == {"Offices", "Apply"}


async def test_studio_users_still_see_every_menu(client, test_tenant, auth_headers):
    s = await _setup(test_tenant["tenant_id"])
    assert await _labels(client, auth_headers, s["tag"]) == {"Offices", "Apply", "Other"}
