"""HTTP-level tests for the built-in auth router."""

from __future__ import annotations

import asyncio

import httpx
import pytest
from fastapi import FastAPI

from aura_auth.app import AuraAuth


@pytest.fixture
def short_lived_app(engine) -> FastAPI:
    auth = AuraAuth(
        database_url="sqlite+aiosqlite://",
        secret="z" * 32,
        engine=engine,
        token_lifetime_seconds=1,
    )
    application = FastAPI()
    auth.init_app(application)
    return application


@pytest.mark.asyncio
async def test_register_created(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        response = await client.post(
            "/auth/register",
            json={"email": "newuser@example.com", "password": "password123"},
        )
    assert response.status_code == 201
    body = response.json()
    assert body["email"] == "newuser@example.com"


@pytest.mark.asyncio
async def test_register_invalid_email_422(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        response = await client.post(
            "/auth/register",
            json={"email": "not-an-email", "password": "password123"},
        )
    assert response.status_code == 422


@pytest.mark.asyncio
async def test_register_duplicate_409(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        payload = {"email": "dupuser@example.com", "password": "password123"}
        first = await client.post("/auth/register", json=payload)
        second = await client.post("/auth/register", json=payload)
    assert first.status_code == 201
    assert second.status_code == 409


@pytest.mark.asyncio
async def test_login_success_returns_token(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        await client.post(
            "/auth/register",
            json={"email": "loginok@example.com", "password": "password123"},
        )
        response = await client.post(
            "/auth/login",
            json={"email": "loginok@example.com", "password": "password123"},
        )
    assert response.status_code == 200
    body = response.json()
    assert "access_token" in body
    assert body["token_type"] == "bearer"


@pytest.mark.asyncio
async def test_login_wrong_password_401(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        await client.post(
            "/auth/register",
            json={"email": "badlogin@example.com", "password": "password123"},
        )
        response = await client.post(
            "/auth/login",
            json={"email": "badlogin@example.com", "password": "wrong-password"},
        )
    assert response.status_code == 401


@pytest.mark.asyncio
async def test_login_missing_user_401(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        response = await client.post(
            "/auth/login",
            json={"email": "nobody@example.com", "password": "password123"},
        )
    assert response.status_code == 401


@pytest.mark.asyncio
async def test_me_requires_token(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        response = await client.get("/auth/me")
    assert response.status_code == 401


@pytest.mark.asyncio
async def test_me_with_token_200(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        await client.post(
            "/auth/register",
            json={"email": "meuser@example.com", "password": "password123"},
        )
        login = await client.post(
            "/auth/login",
            json={"email": "meuser@example.com", "password": "password123"},
        )
        token = login.json()["access_token"]
        response = await client.get("/auth/me", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 200
    assert response.json()["email"] == "meuser@example.com"


@pytest.mark.asyncio
async def test_me_expired_token_401(short_lived_app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=short_lived_app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        await client.post(
            "/auth/register",
            json={"email": "expireme@example.com", "password": "password123"},
        )
        login = await client.post(
            "/auth/login",
            json={"email": "expireme@example.com", "password": "password123"},
        )
        token = login.json()["access_token"]
        await asyncio.sleep(1.1)
        response = await client.get("/auth/me", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 401


@pytest.mark.asyncio
async def test_logout_ok(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        response = await client.post("/auth/logout")
    assert response.status_code == 200


@pytest.mark.asyncio
async def test_protected_route_requires_auth(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        response = await client.get("/protected")
    assert response.status_code == 401


@pytest.mark.asyncio
async def test_protected_route_with_token(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        await client.post(
            "/auth/register",
            json={"email": "prot@example.com", "password": "password123"},
        )
        login = await client.post(
            "/auth/login",
            json={"email": "prot@example.com", "password": "password123"},
        )
        token = login.json()["access_token"]
        response = await client.get("/protected", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 200
    assert response.json()["email"] == "prot@example.com"


@pytest.mark.asyncio
async def test_active_user_dependency_forbids_inactive(app: FastAPI, aura_auth: AuraAuth) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        await client.post(
            "/auth/register",
            json={"email": "inactive@example.com", "password": "password123"},
        )
        login = await client.post(
            "/auth/login",
            json={"email": "inactive@example.com", "password": "password123"},
        )
        token = login.json()["access_token"]
        user = await aura_auth.manager.verify_token(token)
        assert user is not None
        await aura_auth.manager._backend.update(user, {"is_active": False})
        response = await client.get("/active-only", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 403


@pytest.mark.asyncio
async def test_verified_user_dependency_requires_verified(app: FastAPI) -> None:
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        await client.post(
            "/auth/register",
            json={"email": "unverified@example.com", "password": "password123"},
        )
        login = await client.post(
            "/auth/login",
            json={"email": "unverified@example.com", "password": "password123"},
        )
        token = login.json()["access_token"]
        response = await client.get("/verified-only", headers={"Authorization": f"Bearer {token}"})
    assert response.status_code == 403
