# -*- coding: utf-8 -*-
"""Auth API: register/login/me/change-password/logout/delete-account."""
from __future__ import annotations

from datetime import datetime

from fastapi import APIRouter, HTTPException, Request, Response
from pydantic import BaseModel, Field

from ...auth import (
    AuthError,
    AuthUser,
    SESSION_COOKIE_NAME,
    SESSION_COOKIE_SECURE,
    authenticate_session,
    change_password,
    create_session_for_user,
    delete_account,
    get_registered_user,
    is_user_registered,
    register_first_user,
    revoke_session,
    verify_user_credentials,
)

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


class AuthProfileResponse(BaseModel):
    username: str
    display_name: str
    avatar_data_url: str | None = None


class AuthStatusResponse(BaseModel):
    initialized: bool
    authenticated: bool
    profile: AuthProfileResponse | None = None


class RegisterRequest(BaseModel):
    username: str = Field(..., min_length=1, max_length=128)
    password: str = Field(..., min_length=1, max_length=256)
    display_name: str | None = Field(default=None, max_length=128)
    avatar_data_url: str | None = Field(default=None, max_length=2_000_000)


class LoginRequest(BaseModel):
    username: str = Field(..., min_length=1, max_length=128)
    password: str = Field(..., min_length=1, max_length=256)


class SessionResponse(BaseModel):
    profile: AuthProfileResponse
    expires_at: datetime


class ChangePasswordRequest(BaseModel):
    old_password: str = Field(..., min_length=1, max_length=256)
    new_password: str = Field(..., min_length=1, max_length=256)


class LogoutResponse(BaseModel):
    success: bool


class DeleteAccountRequest(BaseModel):
    password: str = Field(..., min_length=1, max_length=256)


def _profile_from_user(user: AuthUser) -> AuthProfileResponse:
    return AuthProfileResponse(
        username=user.username,
        display_name=user.display_name,
        avatar_data_url=user.avatar_data_url,
    )


def _set_session_cookie(response: Response, token: str, ttl_days: int) -> None:
    response.set_cookie(
        key=SESSION_COOKIE_NAME,
        value=token,
        httponly=True,
        samesite="lax",
        secure=SESSION_COOKIE_SECURE,
        max_age=ttl_days * 24 * 60 * 60,
        path="/",
    )


def _clear_session_cookie(response: Response) -> None:
    response.delete_cookie(
        key=SESSION_COOKIE_NAME,
        path="/",
        secure=SESSION_COOKIE_SECURE,
        httponly=True,
        samesite="lax",
    )


def _current_user(request: Request) -> AuthUser:
    state_user = getattr(request.state, "auth_user", None)
    if isinstance(state_user, AuthUser):
        return state_user
    token = request.cookies.get(SESSION_COOKIE_NAME)
    user = authenticate_session(token)
    if user is None:
        raise HTTPException(status_code=401, detail="Authentication required.")
    return user


def _map_auth_error(exc: AuthError) -> HTTPException:
    code_to_status = {
        "already_registered": 409,
        "invalid_username": 400,
        "weak_password": 400,
        "not_registered": 400,
        "invalid_credentials": 401,
        "invalid_ttl": 400,
        "invalid_display_name": 400,
        "invalid_avatar": 400,
    }
    status = code_to_status.get(exc.code, 400)
    return HTTPException(status_code=status, detail=str(exc))


@router.get("/status", response_model=AuthStatusResponse)
async def auth_status(request: Request) -> AuthStatusResponse:
    initialized = is_user_registered()
    token = request.cookies.get(SESSION_COOKIE_NAME)
    user = authenticate_session(token)
    return AuthStatusResponse(
        initialized=initialized,
        authenticated=user is not None,
        profile=_profile_from_user(user) if user else None,
    )


@router.post("/register", response_model=SessionResponse)
async def register(
    body: RegisterRequest,
    response: Response,
) -> SessionResponse:
    try:
        username = register_first_user(
            username=body.username,
            password=body.password,
            display_name=body.display_name,
            avatar_data_url=body.avatar_data_url,
        )
    except AuthError as exc:
        raise _map_auth_error(exc) from exc

    registered_user = get_registered_user()
    if registered_user is None:
        raise HTTPException(status_code=500, detail="Auth state is invalid.")
    ttl_days = 7
    token, expires_at = create_session_for_user(
        username=username,
        ttl_days=ttl_days,
    )
    _set_session_cookie(response, token, ttl_days=ttl_days)
    return SessionResponse(
        profile=_profile_from_user(registered_user),
        expires_at=expires_at,
    )


@router.post("/login", response_model=SessionResponse)
async def login(
    body: LoginRequest,
    response: Response,
) -> SessionResponse:
    if not is_user_registered():
        raise HTTPException(
            status_code=400,
            detail="No account found. Please register first.",
        )
    if not verify_user_credentials(body.username, body.password):
        raise HTTPException(status_code=401, detail="Invalid credentials.")

    registered_user = get_registered_user()
    if registered_user is None:
        raise HTTPException(status_code=500, detail="Auth state is invalid.")
    ttl_days = 7
    token, expires_at = create_session_for_user(
        username=registered_user.username,
        ttl_days=ttl_days,
    )
    _set_session_cookie(response, token, ttl_days=ttl_days)
    return SessionResponse(
        profile=_profile_from_user(registered_user),
        expires_at=expires_at,
    )


@router.get("/me", response_model=AuthProfileResponse)
async def me(request: Request) -> AuthProfileResponse:
    user = _current_user(request)
    return _profile_from_user(user)


@router.post("/change-password", response_model=SessionResponse)
async def update_password(
    body: ChangePasswordRequest,
    request: Request,
    response: Response,
) -> SessionResponse:
    user = _current_user(request)
    if not verify_user_credentials(user.username, body.old_password):
        raise HTTPException(
            status_code=401,
            detail="Current password is incorrect.",
        )
    try:
        change_password(
            username=user.username,
            old_password=body.old_password,
            new_password=body.new_password,
        )
    except AuthError as exc:
        raise _map_auth_error(exc) from exc

    refreshed_user = get_registered_user()
    if refreshed_user is None:
        raise HTTPException(status_code=500, detail="Auth state is invalid.")
    ttl_days = 7
    token, expires_at = create_session_for_user(
        username=refreshed_user.username,
        ttl_days=ttl_days,
    )
    _set_session_cookie(response, token, ttl_days=ttl_days)
    return SessionResponse(
        profile=_profile_from_user(refreshed_user),
        expires_at=expires_at,
    )


@router.post("/logout", response_model=LogoutResponse)
async def logout(request: Request, response: Response) -> LogoutResponse:
    session_token = request.cookies.get(SESSION_COOKIE_NAME)
    if session_token:
        revoke_session(session_token)
    _clear_session_cookie(response)
    return LogoutResponse(success=True)


@router.post("/delete-account", response_model=LogoutResponse)
async def delete_user_account(
    body: DeleteAccountRequest,
    request: Request,
    response: Response,
) -> LogoutResponse:
    user = _current_user(request)
    try:
        delete_account(
            username=user.username,
            password=body.password,
        )
    except AuthError as exc:
        raise _map_auth_error(exc) from exc

    _clear_session_cookie(response)
    return LogoutResponse(success=True)
