"""Auth router: register, login, refresh."""

from __future__ import annotations

import uuid

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

from legate.api.schemas import (
    LoginRequest,
    RefreshRequest,
    RegisterRequest,
    TokenResponse,
    UserOut,
)
from legate.db.session import get_db
from legate.repositories import users as users_repo
from legate.repositories import workspaces as workspaces_repo
from legate.security.passwords import hash_password, verify_password
from legate.security.tokens import (
    TokenError,
    create_access_token,
    create_refresh_token,
    decode_token,
)

router = APIRouter(prefix="/auth", tags=["auth"])


def _default_workspace_id(db: Session, user_id: uuid.UUID) -> uuid.UUID | None:
    workspaces = workspaces_repo.list_for_user(db, user_id)
    return workspaces[0].id if workspaces else None


@router.post("/register", response_model=UserOut, status_code=status.HTTP_201_CREATED)
def register(body: RegisterRequest, db: Session = Depends(get_db)) -> UserOut:
    existing = users_repo.get_by_email(db, body.email)
    if existing is not None and existing.password_hash is not None:
        raise HTTPException(status.HTTP_409_CONFLICT, detail="email already registered")

    if existing is not None:
        # An invited, password-less placeholder user is now claiming the account.
        existing.password_hash = hash_password(body.password)
        existing.name = body.name or existing.name
        existing.is_active = True
        db.commit()
        db.refresh(existing)
        return UserOut.model_validate(existing)

    user = users_repo.create(
        db,
        email=body.email,
        password_hash=hash_password(body.password),
        name=body.name,
    )
    db.commit()
    db.refresh(user)
    return UserOut.model_validate(user)


@router.post("/login", response_model=TokenResponse)
def login(body: LoginRequest, db: Session = Depends(get_db)) -> TokenResponse:
    user = users_repo.get_by_email(db, body.email)
    if user is None or not verify_password(body.password, user.password_hash):
        raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail="invalid credentials")
    if not user.is_active:
        raise HTTPException(status.HTTP_403_FORBIDDEN, detail="user is inactive")

    workspace_id = _default_workspace_id(db, user.id)
    return TokenResponse(
        access_token=create_access_token(user.id, workspace_id),
        refresh_token=create_refresh_token(user.id),
        workspace_id=workspace_id,
    )


@router.post("/refresh", response_model=TokenResponse)
def refresh(body: RefreshRequest, db: Session = Depends(get_db)) -> TokenResponse:
    try:
        payload = decode_token(body.refresh_token, expected_type="refresh")
    except TokenError as exc:
        raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc

    user = users_repo.get_by_id(db, payload.user_id)
    if user is None or not user.is_active:
        raise HTTPException(status.HTTP_401_UNAUTHORIZED, detail="invalid refresh token")

    workspace_id = _default_workspace_id(db, user.id)
    # Rotate: a fresh refresh token is issued alongside the new access token.
    return TokenResponse(
        access_token=create_access_token(user.id, workspace_id),
        refresh_token=create_refresh_token(user.id),
        workspace_id=workspace_id,
    )
