"""Tests für Busylight-Pull-Endpoint und Token-Verwaltung."""
import pytest
import pytest_asyncio
from datetime import date
from httpx import AsyncClient


# ── Fixtures: eigene Company für Busylight-Tests ─────────────────────────────

@pytest_asyncio.fixture(scope="session", loop_scope="session")
async def bl_company(client: AsyncClient):
    resp = await client.post("/api/v1/auth/register", json={
        "company_name": "Busylight GmbH",
        "first_name": "Light",
        "last_name": "Admin",
        "email": "admin@busylight.de",
        "password": "Secret123",
    })
    assert resp.status_code == 201
    tokens = resp.json()
    me = await client.get(
        "/api/v1/auth/me",
        headers={"Authorization": f"Bearer {tokens['access_token']}"},
    )
    return {"tokens": tokens, "user": me.json()}


@pytest_asyncio.fixture(scope="session", loop_scope="session")
async def bl_headers(bl_company):
    return {"Authorization": f"Bearer {bl_company['tokens']['access_token']}"}


@pytest_asyncio.fixture(scope="session", loop_scope="session")
async def bl_user_with_personnel_number(client: AsyncClient, bl_headers, bl_company):
    """Setzt personnel_number am Admin-User."""
    user_id = bl_company["user"]["id"]
    resp = await client.patch(
        f"/api/v1/users/{user_id}",
        json={"personnel_number": "0001"},
        headers=bl_headers,
    )
    assert resp.status_code == 200
    return resp.json()


@pytest_asyncio.fixture(scope="session", loop_scope="session")
async def bl_token(client: AsyncClient, bl_headers):
    """Generiert Token via Rotate-Endpoint und gibt Klartext zurück."""
    resp = await client.post("/api/v1/companies/me/busylight-token/rotate", headers=bl_headers)
    assert resp.status_code == 200
    return resp.json()["token"]


# ── Token-Verwaltung ─────────────────────────────────────────────────────────

@pytest.mark.asyncio
async def test_rotate_returns_plaintext_once(client: AsyncClient, bl_headers):
    resp = await client.post("/api/v1/companies/me/busylight-token/rotate", headers=bl_headers)
    assert resp.status_code == 200
    body = resp.json()
    assert "token" in body and len(body["token"]) >= 32
    assert "created_at" in body


@pytest.mark.asyncio
async def test_token_status_after_rotate(client: AsyncClient, bl_headers, bl_token):
    resp = await client.get("/api/v1/companies/me/busylight-token", headers=bl_headers)
    assert resp.status_code == 200
    body = resp.json()
    assert body["configured"] is True
    assert body["created_at"] is not None


# ── Pull-Endpoint Auth ───────────────────────────────────────────────────────

@pytest.mark.asyncio
async def test_pull_without_token_returns_401(client: AsyncClient):
    resp = await client.get("/api/v1/busylight/users")
    assert resp.status_code == 401


@pytest.mark.asyncio
async def test_pull_with_invalid_token_returns_401(client: AsyncClient):
    resp = await client.get(
        "/api/v1/busylight/users",
        headers={"Authorization": "Bearer not-a-valid-token-abc"},
    )
    assert resp.status_code == 401


@pytest.mark.asyncio
async def test_pull_with_valid_token_returns_200(
    client: AsyncClient, bl_token, bl_user_with_personnel_number
):
    resp = await client.get(
        "/api/v1/busylight/users",
        headers={"Authorization": f"Bearer {bl_token}"},
    )
    assert resp.status_code == 200
    body = resp.json()
    assert body["date"] == str(date.today())
    assert isinstance(body["users"], list)
    pn = {u["personnel_number"] for u in body["users"]}
    assert "0001" in pn


@pytest.mark.asyncio
async def test_pull_omits_users_without_personnel_number(
    client: AsyncClient, bl_token, bl_headers
):
    """User ohne personnel_number tauchen nicht in der Liste auf."""
    invite = await client.post(
        "/api/v1/users/invite",
        json={
            "email": "ohne_nr@busylight.de",
            "first_name": "Ohne",
            "last_name": "Nummer",
            "role": "EMPLOYEE",
        },
        headers=bl_headers,
    )
    assert invite.status_code in (200, 201)

    resp = await client.get(
        "/api/v1/busylight/users",
        headers={"Authorization": f"Bearer {bl_token}"},
    )
    assert resp.status_code == 200
    names = {u["full_name"] for u in resp.json()["users"]}
    assert "Ohne Nummer" not in names


@pytest.mark.asyncio
async def test_pull_includes_today_absence_with_category(
    client: AsyncClient, bl_token, bl_headers, bl_user_with_personnel_number
):
    """Quick-Sick erstellt approved-Krank-Absence für heute → muss in absences_today auftauchen."""
    today = date.today()
    sick_resp = await client.post(
        "/api/v1/absences/quick-sick",
        json={"start_date": str(today), "end_date": str(today)},
        headers=bl_headers,
    )
    assert sick_resp.status_code in (200, 201), sick_resp.text

    resp = await client.get(
        "/api/v1/busylight/users",
        headers={"Authorization": f"Bearer {bl_token}"},
    )
    body = resp.json()
    me_entry = next((u for u in body["users"] if u["personnel_number"] == "0001"), None)
    assert me_entry is not None
    cats = {a["category"] for a in me_entry["absences_today"]}
    assert "sick" in cats


# ── Tenant-Isolation ─────────────────────────────────────────────────────────

@pytest_asyncio.fixture(scope="session", loop_scope="session")
async def other_company_token(client: AsyncClient):
    """Zweite Firma mit eigenem Token + eigenem User mit personnel_number 9999."""
    reg = await client.post("/api/v1/auth/register", json={
        "company_name": "Other GmbH",
        "first_name": "Other",
        "last_name": "Boss",
        "email": "other@boss.de",
        "password": "Secret123",
    })
    assert reg.status_code == 201
    headers = {"Authorization": f"Bearer {reg.json()['access_token']}"}

    me = await client.get("/api/v1/auth/me", headers=headers)
    user_id = me.json()["id"]
    patch = await client.patch(
        f"/api/v1/users/{user_id}",
        json={"personnel_number": "9999"},
        headers=headers,
    )
    assert patch.status_code == 200

    rot = await client.post("/api/v1/companies/me/busylight-token/rotate", headers=headers)
    assert rot.status_code == 200
    return rot.json()["token"]


@pytest.mark.asyncio
async def test_tenant_isolation(client: AsyncClient, other_company_token, bl_token):
    """Token von Firma B liefert nur deren User – keine User von Firma A."""
    resp_b = await client.get(
        "/api/v1/busylight/users",
        headers={"Authorization": f"Bearer {other_company_token}"},
    )
    assert resp_b.status_code == 200
    pns_b = {u["personnel_number"] for u in resp_b.json()["users"]}
    assert "9999" in pns_b
    assert "0001" not in pns_b

    resp_a = await client.get(
        "/api/v1/busylight/users",
        headers={"Authorization": f"Bearer {bl_token}"},
    )
    pns_a = {u["personnel_number"] for u in resp_a.json()["users"]}
    assert "0001" in pns_a
    assert "9999" not in pns_a


# ── Token-Delete ─────────────────────────────────────────────────────────────

@pytest.mark.asyncio
async def test_delete_token_invalidates_pull(client: AsyncClient, bl_headers):
    rot = await client.post("/api/v1/companies/me/busylight-token/rotate", headers=bl_headers)
    fresh = rot.json()["token"]

    pull_ok = await client.get(
        "/api/v1/busylight/users",
        headers={"Authorization": f"Bearer {fresh}"},
    )
    assert pull_ok.status_code == 200

    dl = await client.delete("/api/v1/companies/me/busylight-token", headers=bl_headers)
    assert dl.status_code == 204

    pull_after = await client.get(
        "/api/v1/busylight/users",
        headers={"Authorization": f"Bearer {fresh}"},
    )
    assert pull_after.status_code == 401
