"""Log in (OAuth2 password flow) and "who am I"."""
from typing import Annotated

from fastapi import APIRouter, Depends, Request
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from ..db import get_session
from ..errors import ApiError
from ..models import User
from ..ratelimit import raise_if_limited
from ..schemas import Problem, TokenOut, UserOut
from ..security import CurrentUser, Principal, create_access_token, verify_password

router = APIRouter(prefix="/v1", tags=["auth"])


@router.post("/auth/token", responses={401: {"model": Problem}, 429: {"model": Problem}})
async def login(request: Request, form: Annotated[OAuth2PasswordRequestForm, Depends()],
                session: Annotated[AsyncSession, Depends(get_session)]) -> TokenOut:
    settings = request.state.settings
    limiter, limit = request.state.limiter, settings.login_attempts_per_minute
    key = f"login:{request.client.host if request.client else 'unknown'}:{form.username.lower()}"
    raise_if_limited(limiter.retry_after(key, limit))           # only failed attempts count
    user = await session.scalar(select(User).where(User.email == form.username.lower()))
    # unknown e-mails pay the same bcrypt cost: response times don't reveal which accounts exist
    valid = await verify_password(form.password, user.password_hash if user else request.state.dummy_hash)
    if user is None or not valid:
        limiter.hit(key, limit)
        raise ApiError(401, "incorrect e-mail or password", type_="unauthenticated")
    token, expires_in = create_access_token(Principal(user.id, user.email, user.role), settings)
    return TokenOut(access_token=token, expires_in=expires_in)


@router.get("/me")
async def me(principal: CurrentUser) -> UserOut:
    return UserOut(id=principal.id, email=principal.email, role=principal.role)
