from fastapi import Depends

from bluefox_auth.dependencies import current_active_user, current_superuser
from bluefox_auth.models import BluefoxUser
from bluefox_auth.tokens import create_access_token

TEST_SECRET = "test-secret-key-that-is-long-enough-for-validation"


def _register_routes(app):
    """Register test routes that use auth dependencies."""

    @app.get("/protected")
    async def protected(user=Depends(current_active_user)):
        return {"id": user.id, "email": user.email}

    @app.get("/admin")
    async def admin(user=Depends(current_superuser)):
        return {"id": user.id, "email": user.email}


async def _create_user(db, email="test@example.com", is_active=True, is_superuser=False):
    user = BluefoxUser(
        email=email,
        password_hash="fake-hash",
        is_active=is_active,
        is_superuser=is_superuser,
    )
    db.add(user)
    await db.flush()
    await db.refresh(user)
    return user


# --- Bearer token tests ---


async def test_bearer__returns_user(app, client, db, auth_settings):
    _register_routes(app)
    user = await _create_user(db)
    token = create_access_token(user.id, auth_settings)

    resp = await client.get("/protected", headers={"Authorization": f"Bearer {token}"})
    assert resp.status_code == 200
    assert resp.json()["email"] == "test@example.com"


async def test_bearer__rejects_unauthenticated(app, client):
    _register_routes(app)
    resp = await client.get("/protected")
    assert resp.status_code == 401


async def test_bearer__rejects_invalid_token(app, client):
    _register_routes(app)
    resp = await client.get("/protected", headers={"Authorization": "Bearer bad-token"})
    assert resp.status_code == 401


async def test_bearer__rejects_inactive_user(app, client, db, auth_settings):
    _register_routes(app)
    user = await _create_user(db, is_active=False)
    token = create_access_token(user.id, auth_settings)

    resp = await client.get("/protected", headers={"Authorization": f"Bearer {token}"})
    assert resp.status_code == 401


# --- Cookie transport tests ---


async def test_cookie__returns_user(app, client, db, auth_settings):
    _register_routes(app)
    user = await _create_user(db)
    token = create_access_token(user.id, auth_settings)

    # GET — no CSRF needed
    resp = await client.get(
        "/protected",
        cookies={auth_settings.cookie_name_access: token},
    )
    assert resp.status_code == 200
    assert resp.json()["email"] == "test@example.com"


async def test_cookie__post_requires_csrf(app, client, db, auth_settings):
    """POST with cookie transport must include CSRF token."""

    @app.post("/mutating")
    async def mutating(user=Depends(current_active_user)):
        return {"id": user.id}

    user = await _create_user(db)
    token = create_access_token(user.id, auth_settings)

    # POST without CSRF — should fail
    resp = await client.post(
        "/mutating",
        cookies={auth_settings.cookie_name_access: token},
    )
    assert resp.status_code == 403


async def test_cookie__post_with_csrf_succeeds(app, client, db, auth_settings):
    """POST with cookie transport + matching CSRF tokens succeeds."""

    @app.post("/mutating")
    async def mutating(user=Depends(current_active_user)):
        return {"id": user.id}

    user = await _create_user(db)
    token = create_access_token(user.id, auth_settings)
    csrf = "test-csrf-token"

    resp = await client.post(
        "/mutating",
        cookies={
            auth_settings.cookie_name_access: token,
            auth_settings.cookie_name_csrf: csrf,
        },
        headers={"X-CSRF-Token": csrf},
    )
    assert resp.status_code == 200


async def test_bearer__post_skips_csrf(app, client, db, auth_settings):
    """POST with Bearer token does not require CSRF."""

    @app.post("/mutating")
    async def mutating(user=Depends(current_active_user)):
        return {"id": user.id}

    user = await _create_user(db)
    token = create_access_token(user.id, auth_settings)

    resp = await client.post(
        "/mutating",
        headers={"Authorization": f"Bearer {token}"},
    )
    assert resp.status_code == 200


# --- Superuser tests ---


async def test_superuser__allows_superuser(app, client, db, auth_settings):
    _register_routes(app)
    user = await _create_user(db, is_superuser=True)
    token = create_access_token(user.id, auth_settings)

    resp = await client.get("/admin", headers={"Authorization": f"Bearer {token}"})
    assert resp.status_code == 200


async def test_superuser__rejects_non_superuser(app, client, db, auth_settings):
    _register_routes(app)
    user = await _create_user(db, is_superuser=False)
    token = create_access_token(user.id, auth_settings)

    resp = await client.get("/admin", headers={"Authorization": f"Bearer {token}"})
    assert resp.status_code == 403
