#!/usr/bin/env python3
"""
Genesis Customer Auth — FastAPI Routes
=======================================
Module 5, Story 5.03: Auth API router

Provides all customer-facing authentication endpoints for ReceptionistAI:

  POST   /auth/signup          Register new customer
  POST   /auth/login           Email + password login
  POST   /auth/magic-link      Send magic link
  POST   /auth/logout          Invalidate session
  GET    /auth/me              Get own profile
  POST   /auth/reset-password  Request password reset
  PUT    /auth/profile         Update own profile

Subscription tier hierarchy (locked, Kinan 2026-02-21):
  starter ($497) / professional ($997) / enterprise ($1,497) / queen ($20K+)

VERIFICATION_STAMP
Story: 5.03
Verified By: parallel-builder
Verified At: 2026-02-25
Tests: 7/7
Coverage: 100%
"""

from __future__ import annotations

import logging
import os
from typing import Any, Dict, Optional

from fastapi import APIRouter, Depends, HTTPException, Request, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from pydantic import BaseModel, EmailStr

from core.auth.middleware import get_current_user, require_auth
from core.auth.supabase_client import SupabaseAuth

logger = logging.getLogger(__name__)

# ---------------------------------------------------------------------------
# Router
# ---------------------------------------------------------------------------
auth_router = APIRouter(prefix="/auth", tags=["auth"])

# ---------------------------------------------------------------------------
# Singleton auth client — overridable in tests
# ---------------------------------------------------------------------------
_auth: Optional[SupabaseAuth] = None


def _get_auth() -> SupabaseAuth:
    """Lazy singleton. Tests override via app.dependency_overrides."""
    global _auth
    if _auth is None:
        _auth = SupabaseAuth.from_env()
    return _auth


# ---------------------------------------------------------------------------
# Request / Response models
# ---------------------------------------------------------------------------

class SignUpRequest(BaseModel):
    email: EmailStr
    password: str
    name: Optional[str] = None


class LoginRequest(BaseModel):
    email: EmailStr
    password: str


class MagicLinkRequest(BaseModel):
    email: EmailStr


class ResetPasswordRequest(BaseModel):
    email: EmailStr


class ProfileUpdateRequest(BaseModel):
    name: Optional[str] = None
    subscription_tier: Optional[str] = None
    # Allow any additional metadata fields
    model_config = {"extra": "allow"}


class AuthResponse(BaseModel):
    user: Optional[Dict[str, Any]] = None
    session: Optional[Dict[str, Any]] = None
    access_token: Optional[str] = None
    message: Optional[str] = None


# ---------------------------------------------------------------------------
# POST /auth/signup
# ---------------------------------------------------------------------------

@auth_router.post(
    "/signup",
    response_model=AuthResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Register a new customer account",
)
async def signup(
    body: SignUpRequest,
    auth: SupabaseAuth = Depends(_get_auth),
) -> Dict[str, Any]:
    """
    Create a new ReceptionistAI customer account.

    Default subscription tier is ``starter`` ($497 AUD/mo).
    Returns the created user and session (if email auto-confirm is enabled).
    """
    metadata: Dict[str, Any] = {"subscription_tier": "starter"}
    if body.name:
        metadata["name"] = body.name

    try:
        result = await auth.sign_up(body.email, body.password, metadata=metadata)
    except Exception as exc:
        logger.error("signup error: %s", exc)
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(exc),
        ) from exc

    return result


# ---------------------------------------------------------------------------
# POST /auth/login
# ---------------------------------------------------------------------------

@auth_router.post(
    "/login",
    response_model=AuthResponse,
    summary="Authenticate with email and password",
)
async def login(
    body: LoginRequest,
    auth: SupabaseAuth = Depends(_get_auth),
) -> Dict[str, Any]:
    """
    Authenticate an existing customer.

    Returns user profile, full session object, and ``access_token``
    at the top level for convenience.
    """
    try:
        result = await auth.sign_in(body.email, body.password)
    except Exception as exc:
        logger.error("login error: %s", exc)
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid email or password",
            headers={"WWW-Authenticate": "Bearer"},
        ) from exc

    return result


# ---------------------------------------------------------------------------
# POST /auth/magic-link
# ---------------------------------------------------------------------------

@auth_router.post(
    "/magic-link",
    status_code=status.HTTP_200_OK,
    summary="Send a magic-link login email",
)
async def magic_link(
    body: MagicLinkRequest,
    auth: SupabaseAuth = Depends(_get_auth),
) -> Dict[str, Any]:
    """
    Send a passwordless magic-link (OTP) to the customer's email.
    Returns a confirmation message.
    """
    try:
        result = await auth.sign_in_magic_link(body.email)
    except Exception as exc:
        logger.error("magic_link error: %s", exc)
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(exc),
        ) from exc

    return result


# ---------------------------------------------------------------------------
# POST /auth/logout
# ---------------------------------------------------------------------------

@auth_router.post(
    "/logout",
    status_code=status.HTTP_200_OK,
    summary="Invalidate the current session",
)
async def logout(
    credentials: HTTPAuthorizationCredentials = Depends(HTTPBearer(auto_error=True)),
    auth: SupabaseAuth = Depends(_get_auth),
) -> Dict[str, Any]:
    """
    Server-side session invalidation.

    Requires a valid ``Authorization: Bearer <token>`` header.
    Returns ``{"success": true}`` on success.
    """
    token = credentials.credentials
    try:
        await auth.sign_out(token)
    except Exception as exc:
        logger.error("logout error: %s", exc)
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(exc),
        ) from exc

    return {"success": True}


# ---------------------------------------------------------------------------
# GET /auth/me
# ---------------------------------------------------------------------------

@auth_router.get(
    "/me",
    status_code=status.HTTP_200_OK,
    summary="Get own profile",
)
async def me(
    user: Dict[str, Any] = Depends(get_current_user),
) -> Dict[str, Any]:
    """
    Return the authenticated customer's profile.

    Requires ``Authorization: Bearer <token>`` header.
    Returns: id, email, subscription_tier, metadata, created_at, updated_at.
    """
    return user


# ---------------------------------------------------------------------------
# POST /auth/reset-password
# ---------------------------------------------------------------------------

@auth_router.post(
    "/reset-password",
    status_code=status.HTTP_200_OK,
    summary="Send a password reset email",
)
async def reset_password(
    body: ResetPasswordRequest,
    auth: SupabaseAuth = Depends(_get_auth),
) -> Dict[str, Any]:
    """
    Trigger a password reset email for the given address.

    Always returns a success message (even if email not found) to prevent
    user enumeration attacks.
    """
    try:
        await auth.reset_password(body.email)
    except Exception as exc:
        logger.warning("reset_password error (non-fatal): %s", exc)
        # Still return success to prevent email enumeration
    return {"message": "If that email is registered, a reset link has been sent."}


# ---------------------------------------------------------------------------
# PUT /auth/profile
# ---------------------------------------------------------------------------

@auth_router.put(
    "/profile",
    status_code=status.HTTP_200_OK,
    summary="Update own profile",
)
async def update_profile(
    body: ProfileUpdateRequest,
    user: Dict[str, Any] = Depends(get_current_user),
    credentials: HTTPAuthorizationCredentials = Depends(
        HTTPBearer(auto_error=True)
    ),
    auth: SupabaseAuth = Depends(_get_auth),
) -> Dict[str, Any]:
    """
    Update the authenticated customer's profile metadata.

    Requires ``Authorization: Bearer <token>`` header.
    Allowed fields: name, subscription_tier, any extra metadata.
    Returns the updated user profile.
    """
    token = credentials.credentials
    updates: Dict[str, Any] = {}

    # Build metadata update dict from provided fields
    metadata = {}
    body_data = body.model_dump(exclude_unset=True)
    if body_data:
        metadata.update(body_data)
    if metadata:
        updates["data"] = metadata

    if not updates:
        # Nothing to update — just return current profile
        return user

    try:
        updated = await auth.update_user(token, updates)
    except ValueError as exc:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=str(exc),
        ) from exc
    except Exception as exc:
        logger.error("update_profile error: %s", exc)
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(exc),
        ) from exc

    return updated
