"""Sessions (app/crud/sessions.py): each sign-in is recorded, listed, and
can be signed out — by you, by an administrator, or by a password change."""

from tests.conftest import TEST_ADMIN_PASSWORD


async def _sign_in(client, tenant, password=TEST_ADMIN_PASSWORD, agent="Mozilla/5.0 (Windows NT 10.0) Chrome/130.0") -> dict:
    resp = await client.post(
        "/auth/login", headers={"User-Agent": agent},
        json={"tenant_id": str(tenant["tenant_id"]), "email": tenant["admin_email"], "password": password},
    )
    assert resp.status_code == 200, resp.text
    return {"Authorization": f"Bearer {resp.json()['access_token']}"}


async def test_your_sessions_are_listed_and_can_be_signed_out(client, test_tenant):
    laptop = await _sign_in(client, test_tenant)
    phone = await _sign_in(client, test_tenant, agent="Mozilla/5.0 (iPhone; CPU iPhone OS 18_0) Safari/604.1")

    mine = (await client.get("/auth/sessions", headers=laptop)).json()
    assert len(mine) == 2
    current = next(s for s in mine if s["current"])
    assert "Chrome" in current["user_agent"] and current["amr"] == ["pwd"]
    other = next(s for s in mine if not s["current"])

    # Signing out the other one ends it at once.
    assert (await client.post(f"/auth/sessions/{other['id']}/revoke", headers=laptop)).status_code == 204
    assert (await client.get("/auth/sessions", headers=phone)).status_code == 401
    assert (await client.get("/auth/sessions", headers=laptop)).status_code == 200

    # And signing out yourself.
    assert (await client.post("/auth/logout", headers=laptop)).status_code == 204
    resp = await client.get("/auth/sessions", headers=laptop)
    assert resp.status_code == 401 and "signed out" in resp.json()["detail"]


async def test_changing_your_password_signs_out_your_other_sessions(client, test_tenant):
    here = await _sign_in(client, test_tenant)
    elsewhere = await _sign_in(client, test_tenant)
    assert (await client.post("/auth/password", headers=here, json={"current_password": TEST_ADMIN_PASSWORD, "new_password": "fresh-pass-123"})).status_code == 204
    assert (await client.get("/auth/sessions", headers=here)).status_code == 200
    assert (await client.get("/auth/sessions", headers=elsewhere)).status_code == 401


async def test_administrators_see_and_end_anyones_sessions(client, test_tenant, auth_headers):
    session = await _sign_in(client, test_tenant)
    everyone = (await client.get("/sessions", headers=auth_headers)).json()
    assert [s["user_email"] for s in everyone] == [test_tenant["admin_email"]]
    assert (await client.post(f"/sessions/{everyone[0]['id']}/revoke", headers=auth_headers)).status_code == 204
    assert (await client.get("/auth/sessions", headers=session)).status_code == 401

    # Everywhere: a person's every session, and tokens from before sessions existed.
    other = await client.post("/users", headers=auth_headers, json={"name": "Other", "email": "other@example.com", "password": "other-pass-123"})
    other_id = other.json()["id"]
    other_login = await client.post("/auth/login", json={"tenant_id": str(test_tenant["tenant_id"]), "email": "other@example.com", "password": "other-pass-123"})
    other_headers = {"Authorization": f"Bearer {other_login.json()['access_token']}"}
    assert (await client.get("/auth/sessions", headers=other_headers)).status_code == 200
    assert (await client.post(f"/sessions/users/{other_id}/revoke", headers=auth_headers)).status_code == 204
    assert (await client.get("/auth/sessions", headers=other_headers)).status_code == 401


async def test_signing_everyone_out_keeps_you_signed_in(client, test_tenant, auth_headers):
    mine = await _sign_in(client, test_tenant)
    await client.post("/users", headers=auth_headers, json={"name": "Other", "email": "other2@example.com", "password": "other-pass-123"})
    login = await client.post("/auth/login", json={"tenant_id": str(test_tenant["tenant_id"]), "email": "other2@example.com", "password": "other-pass-123"})
    theirs = {"Authorization": f"Bearer {login.json()['access_token']}"}

    resp = await client.post("/sessions/revoke-everyone", headers=mine)
    assert resp.status_code == 200 and resp.json()["people"] == 1
    assert (await client.get("/auth/sessions", headers=theirs)).status_code == 401
    assert (await client.get("/auth/sessions", headers=mine)).status_code == 200


async def test_deactivating_someone_signs_them_out(client, test_tenant, auth_headers):
    created = await client.post("/users", headers=auth_headers, json={"name": "Leaver", "email": "leaver@example.com", "password": "leaver-pass-1"})
    login = await client.post("/auth/login", json={"tenant_id": str(test_tenant["tenant_id"]), "email": "leaver@example.com", "password": "leaver-pass-1"})
    theirs = {"Authorization": f"Bearer {login.json()['access_token']}"}
    assert (await client.patch(f"/users/{created.json()['id']}", headers=auth_headers, json={"status": "inactive"})).status_code == 200
    assert (await client.get("/auth/sessions", headers=theirs)).status_code == 401
