"""Portal user management (routers/portal_users.py): a portal role with the
"user_management" feature manages its own organization's portal users,
giving only the roles listed under "user_management_roles"."""

import uuid

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


async def _setup(tenant_id: uuid.UUID) -> dict:
    tag = uuid.uuid4().hex[:6]
    async with AsyncSessionLocal() as db:
        async with db.begin():
            org, other_org = Vendor(tenant_id=tenant_id, organization_name=f"KSRTC {tag}"), Vendor(tenant_id=tenant_id, organization_name=f"Other {tag}")
            db.add_all([org, other_org])
            await db.flush()
            clerk = Role(tenant_id=tenant_id, name=f"Clerk {tag}", scope="portal", action_matrix={})
            officer = Role(tenant_id=tenant_id, name=f"Officer {tag}", scope="portal", action_matrix={})
            db.add_all([clerk, officer])
            await db.flush()
            manager_role = Role(
                tenant_id=tenant_id, name=f"Nodal {tag}", scope="portal", action_matrix={"user_management": ["enabled"], "user_management_roles": [str(clerk.id)]}
            )
            plain_role = Role(tenant_id=tenant_id, name=f"Plain {tag}", scope="portal", action_matrix={})
            db.add_all([manager_role, plain_role])
            await db.flush()

            def user(name, vendor, *roles):
                u = User(tenant_id=tenant_id, vendor_id=vendor.id, name=name, email=f"{name.lower()}-{tag}@example.com", status="active")
                db.add(u)
                return u, roles

            pairs = [
                user("Manager", org, manager_role),
                user("ClerkUser", org, clerk),
                user("OfficerUser", org, officer),
                user("Outsider", other_org, clerk),
                user("Plain", org, plain_role),
            ]
            await db.flush()
            for u, roles in pairs:
                for r in roles:
                    db.add(UserRole(user_id=u.id, role_id=r.id))
            users = {u.name: u for u, _ in pairs}
            ids = {"clerk": clerk.id, "officer": officer.id, **{n: u.id for n, u in users.items()}, "org": org.id}
    token = lambda name: {"Authorization": f"Bearer {create_access_token(tenant_id=tenant_id, user_id=ids[name], vendor_id=ids['org'])}"}  # noqa: E731
    return {**ids, "as": token}


async def test_a_manager_manages_their_own_organization(client, test_tenant):
    s = await _setup(test_tenant["tenant_id"])
    me = s["as"]("Manager")

    roles = (await client.get("/portal/users/roles", headers=me)).json()
    assert [r["id"] for r in roles] == [str(s["clerk"])]

    listed = {u["name"]: u for u in (await client.get("/portal/users", headers=me)).json()}
    assert set(listed) == {"Manager", "ClerkUser", "OfficerUser", "Plain"}  # not the other organization's
    assert listed["ClerkUser"]["manageable"] is True
    assert listed["OfficerUser"]["manageable"] is False  # holds a role the manager can't give
    assert listed["Manager"]["manageable"] is False  # themselves

    created = await client.post("/portal/users", headers=me, json={"name": "New Clerk", "email": f"new-{uuid.uuid4().hex[:6]}@example.com", "password": "S3cure-pass", "role_ids": [str(s["clerk"])]})
    assert created.status_code == 201, created.text
    new_id = created.json()["id"]

    edited = await client.patch(f"/portal/users/{new_id}", headers=me, json={"status": "inactive", "role_ids": []})
    assert edited.status_code == 200, edited.text
    assert edited.json()["status"] == "inactive" and edited.json()["role_ids"] == []
    assert (await client.post(f"/portal/users/{new_id}/set-password", headers=me, json={"password": "An0ther-pass"})).status_code == 204


async def test_a_manager_cant_overreach(client, test_tenant):
    s = await _setup(test_tenant["tenant_id"])
    me = s["as"]("Manager")
    # A role they weren't allowed to give.
    give = await client.post("/portal/users", headers=me, json={"name": "X", "email": f"x-{uuid.uuid4().hex[:6]}@example.com", "password": "S3cure-pass", "role_ids": [str(s["officer"])]})
    assert give.status_code == 403
    # Someone holding such a role, themselves, someone in another organization.
    assert (await client.patch(f"/portal/users/{s['OfficerUser']}", headers=me, json={"status": "inactive"})).status_code == 403
    assert (await client.patch(f"/portal/users/{s['Manager']}", headers=me, json={"role_ids": [str(s["clerk"])]})).status_code == 403
    assert (await client.patch(f"/portal/users/{s['Outsider']}", headers=me, json={"status": "inactive"})).status_code == 404
    # A portal user without the feature, and an internal user.
    assert (await client.get("/portal/users", headers=s["as"]("Plain"))).status_code == 403


async def test_studio_users_use_users_instead(client, auth_headers):
    assert (await client.get("/portal/users", headers=auth_headers)).status_code == 403
