import os
import time
from collections import defaultdict, deque

from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm
from sqlalchemy.orm import Session

from .. import models, schemas
from ..auth import create_access_token, get_current_user, hash_password, verify_password
from ..database import get_db

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

# Login lockout: per-email sliding window (audit M-1)
LOCKOUT_ATTEMPTS = 5
LOCKOUT_WINDOW_SECONDS = 15 * 60
_failures: dict[str, deque] = defaultdict(deque)


def _locked_out(email: str) -> bool:
    window = _failures[email]
    now = time.monotonic()
    while window and now - window[0] > LOCKOUT_WINDOW_SECONDS:
        window.popleft()
    return len(window) >= LOCKOUT_ATTEMPTS


def _registration_open() -> bool:
    return os.getenv("REGISTRATION_OPEN", "true").lower() == "true"


@router.post("/register", response_model=schemas.UserOut)
def register(body: schemas.UserCreate, db: Session = Depends(get_db)):
    is_first_user = db.query(models.User).count() == 0
    # The founding account can always be created; afterwards the policy applies.
    if not is_first_user and not _registration_open():
        raise HTTPException(403, "Registration is closed on this deployment")
    if db.query(models.User).filter(models.User.email == body.email).first():
        raise HTTPException(400, "Email already registered")
    if len(body.password) < 8:
        raise HTTPException(400, "Password must be at least 8 characters")
    user = models.User(
        email=body.email,
        password_hash=hash_password(body.password),
        role="admin" if is_first_user else "user",
    )
    db.add(user)
    db.commit()
    db.refresh(user)
    return user


@router.post("/login", response_model=schemas.Token)
def login(form: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
    email = form.username.lower().strip()
    if _locked_out(email):
        raise HTTPException(429, "Too many failed attempts — try again in 15 minutes")
    user = db.query(models.User).filter(models.User.email == email).first()
    if not user or not verify_password(form.password, user.password_hash):
        _failures[email].append(time.monotonic())
        raise HTTPException(401, "Invalid email or password")
    _failures.pop(email, None)  # success clears the counter
    return schemas.Token(access_token=create_access_token(user.id))


@router.get("/me", response_model=schemas.UserOut)
def me(user: models.User = Depends(get_current_user)):
    return user
