from __future__ import annotations

from typing import Literal, Optional

from fastapi import Response
from pydantic import BaseModel, EmailStr
from slowapi import Limiter

from config import settings
from deps.utils import get_client_ip

# IP-level rate limiter (SEC-02 — 10 req/min on register/login/refresh)
limiter = Limiter(key_func=get_client_ip)


# ── Request models ────────────────────────────────────────────────────────────

class RegisterRequest(BaseModel):
    handle: str
    email: EmailStr
    password: str


class LoginRequest(BaseModel):
    email: EmailStr
    password: str
    totp_code: Optional[str] = None
    backup_code: Optional[str] = None
    remember_me: bool = False


class ChangePasswordRequest(BaseModel):
    current_password: str
    new_password: str


class TotpEnableRequest(BaseModel):
    code: str


class PasswordResetRequest(BaseModel):
    email: EmailStr


class PasswordResetConfirmRequest(BaseModel):
    token: str
    new_password: str


class PreferencesUpdate(BaseModel):
    """Request body for PATCH /api/auth/me/preferences.

    Validates pdf_open_mode strictly via Literal (T-04-05-05 — no mass assignment).
    """
    pdf_open_mode: Literal["in_app", "new_tab"]


# ── Helper: set httpOnly refresh cookie ──────────────────────────────────────

def _set_refresh_cookie(
    response: Response, raw_token: str, remember_me: bool = False
) -> None:
    """Set the httpOnly Secure SameSite=Strict refresh cookie (CLAUDE.md constraint).

    remember_me=False (default): Max-Age = refresh_token_expire_hours * 3600 (16h, D-11, RM-03)
    remember_me=True: Max-Age = refresh_token_expire_days * 86400 (30d, D-11, RM-03)
    """
    max_age = (
        settings.refresh_token_expire_days * 86400
        if remember_me
        else settings.refresh_token_expire_hours * 3600
    )
    response.set_cookie(
        key="refresh_token",
        value=raw_token,
        httponly=True,
        secure=True,
        samesite="strict",
        path="/api/auth/refresh",
        max_age=max_age,
    )


def _user_dict(user: object) -> dict:
    """Return serialisable user metadata (no password_hash, no credentials_enc)."""
    return {
        "id": str(user.id),
        "handle": user.handle,
        "email": user.email,
        "role": user.role,
        "totp_enabled": user.totp_enabled,
        "created_at": user.created_at.isoformat() if user.created_at else None,
    }
