"""
Authentication and User API routes
Story 3.6: Documentation OpenAPI complète avec exemples et codes d'erreur
"""

import os
import secrets
import logging
from datetime import timedelta, datetime, timezone
from fastapi import APIRouter, HTTPException, Depends, Header, Request, Query
from fastapi.responses import JSONResponse
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel, EmailStr, ValidationError as PydanticValidationError
from typing import Optional, Dict, Any

from models.subscription import (
    UserCreate,
    UserLogin,
    UserResponse,
    PlanType,
    PLANS,
    CREDIT_PACKAGES,
)
from services.auth_service import (
    create_user,
    authenticate_user,
    get_user_by_id,
    create_access_token,
    create_refresh_token,
    verify_token,
    revoke_token_jti,
    check_usage_limits,
    update_user,
    get_user_by_email,
    verify_password,
    get_or_create_google_user,
    PASSLIB_AVAILABLE,
)
from services.payment_service import (
    create_checkout_session,
    sync_checkout_session,
    create_credits_checkout,
    cancel_subscription,
    get_billing_portal_url,
    handle_webhook,
    is_stripe_configured,
)

logger = logging.getLogger(__name__)


security = HTTPBearer(auto_error=False)


# Request/Response models
class TokenResponse(BaseModel):
    access_token: str
    refresh_token: str
    token_type: str = "bearer"
    user: UserResponse


class RefreshRequest(BaseModel):
    refresh_token: str


class GoogleAuthRequest(BaseModel):
    credential: Optional[str] = None   # Google ID token (from GoogleLogin component)
    access_token: Optional[str] = None  # Google OAuth2 access token (from useGoogleLogin)


class CheckoutRequest(BaseModel):
    plan: PlanType
    billing_period: str = "monthly"
    # Code promo optionnel — validé localement AVANT toute création de session
    # Stripe (actif, forfait, expiration, plafond).
    promo_code: Optional[str] = None


class CreditsCheckoutRequest(BaseModel):
    package_index: int


async def get_current_user(
    credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
):
    if not credentials:
        return None
    payload = verify_token(credentials.credentials)
    if not payload:
        return None
    return get_user_by_id(payload.get("sub"))


async def require_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
    if not credentials:
        raise HTTPException(status_code=401, detail="Not authenticated")
    payload = verify_token(credentials.credentials)
    if not payload:
        raise HTTPException(status_code=401, detail="Token invalide ou expiré")
    user = get_user_by_id(payload.get("sub"))
    if not user:
        raise HTTPException(status_code=401, detail="User not found")
    return user


def user_to_response(user) -> UserResponse:
    """Convert User to UserResponse with plan limits"""
    plan_limits = PLANS[user.plan]
    tier = getattr(user, "tier", None) or user.plan
    return UserResponse(
        id=user.id,
        email=user.email,
        name=user.name,
        avatar_url=user.avatar_url,
        plan=user.plan,
        tier=tier,
        subscription_status=user.subscription_status,
        subscription_ends_at=getattr(user, 'subscription_ends_at', None),
        cancel_at_period_end=getattr(user, 'cancel_at_period_end', False),
        docs_translated_this_month=user.docs_translated_this_month,
        pages_translated_this_month=user.pages_translated_this_month,
        api_calls_this_month=user.api_calls_this_month,
        extra_credits=user.extra_credits,
        created_at=user.created_at,
        plan_limits={
            "docs_per_month": plan_limits["docs_per_month"],
            "max_pages_per_doc": plan_limits["max_pages_per_doc"],
            "max_file_size_mb": plan_limits["max_file_size_mb"],
            "providers": plan_limits["providers"],
            "features": plan_limits["features"],
            "api_access": plan_limits.get("api_access", False),
        },
    )


# ============== API v1 Router ==============

router_v1 = APIRouter(prefix="/api/v1/auth", tags=["Authentication v1"])


def _has_email_validation_error(exc: PydanticValidationError) -> bool:
    """Return True when pydantic validation errors include the email field."""
    for err in exc.errors():
        loc = err.get("loc", ())
        if isinstance(loc, (list, tuple)) and "email" in loc:
            return True
    return False


@router_v1.post(
    "/register",
    status_code=201,
    summary="Inscription d'un nouvel utilisateur",
    description="""
Créer un nouveau compte utilisateur.

**Paramètres requis:**
- `email`: Adresse email valide (sera utilisée pour la connexion)
- `password`: Mot de passe (minimum 8 caractères)
- `name`: Nom complet (optionnel)

**Réponse:**
- HTTP 201 avec les données de l'utilisateur créé
- Le `tier` par défaut est "free"

**Codes d'erreur:**
- `INVALID_EMAIL`: Invalid email format
- `EMAIL_EXISTS`: Un compte existe déjà avec cet email
- `INVALID_REQUEST`: Données d'inscription invalides
    """,
    responses={
        201: {
            "description": "Utilisateur créé avec succès",
            "content": {
                "application/json": {
                    "example": {
                        "data": {
                            "id": "usr_abc123def456",
                            "email": "utilisateur@exemple.com",
                            "tier": "free",
                        },
                        "meta": {},
                    }
                }
            },
        },
        400: {
            "description": "Erreur de validation",
            "content": {
                "application/json": {
                    "examples": {
                        "INVALID_EMAIL": {
                            "summary": "Invalid email format",
                            "value": {
                                "error": "INVALID_EMAIL",
                                "message": "Invalid email format",
                            },
                        },
                        "EMAIL_EXISTS": {
                            "summary": "Email already used",
                            "value": {
                                "error": "EMAIL_EXISTS",
                                "message": "An account already exists with this email address",
                            },
                        },
                    }
                }
            },
        },
    },
)
async def register_v1(request: Request):
    """Inscription d'un nouvel utilisateur (API v1) — retourne 201 avec données utilisateur"""
    try:
        body = await request.json()
    except Exception:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Corps de requête JSON invalide",
            },
        )

    try:
        user_create = UserCreate.model_validate(body)
    except PydanticValidationError as exc:
        if _has_email_validation_error(exc):
            return JSONResponse(
                status_code=400,
                content={
                    "error": "INVALID_EMAIL",
                    "message": "Invalid email format",
                },
            )
        # Check if it's a password validation error
        for error in exc.errors():
            loc = error.get("loc", ())
            if "password" in loc:
                msg = error.get("msg", "")
                # If password is missing entirely, return INVALID_REQUEST
                if "Field required" in msg or "required" in msg.lower():
                    break
                # Otherwise it's a weak password
                # Translate common pydantic messages to French
                if "at least 8 characters" in msg.lower() or "8 caractères" in msg:
                    msg = "Password must be at least 8 characters long"
                return JSONResponse(
                    status_code=400,
                    content={
                        "error": "WEAK_PASSWORD",
                        "message": msg,
                    },
                )
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Invalid registration data",
            },
        )

    # In production, registration must rely on passlib[bcrypt] hashing.
    if (
        os.getenv("ENVIRONMENT", "development").lower() == "production"
        and not PASSLIB_AVAILABLE
    ):
        return JSONResponse(
            status_code=503,
            content={
                "error": "AUTH_HASHING_UNAVAILABLE",
                "message": "Registration service temporarily unavailable",
            },
        )

    try:
        user = create_user(user_create)
        # Alert admin via Telegram on signup
        try:
            from utils.telegram import send_telegram_notification
            import asyncio
            msg = (
                f"👤 *Nouvelle inscription !*\n\n"
                f"• *Email* : `{user.email}`\n"
                f"• *Nom* : `{user.name or 'Non renseigné'}`\n"
                f"• *ID* : `{user.id}`\n"
                f"• *Forfait* : `{user.plan.value}`\n"
                f"• *Date* : {datetime.now(timezone.utc).strftime('%d/%m/%Y %H:%M:%S')} UTC"
            )
            asyncio.create_task(send_telegram_notification(msg))
        except Exception as tel_err:
            logger.error(f"Failed to send telegram notification: {tel_err}")
    except ValueError as exc:
        msg = str(exc).strip().lower()
        if "email already registered" in msg or "email already" in msg:
            return JSONResponse(
                status_code=400,
                content={
                    "error": "EMAIL_EXISTS",
                    "message": "An account already exists with this email address",
                },
            )
        return JSONResponse(
            status_code=400,
            content={
                "error": "REGISTRATION_FAILED",
                "message": "Unable to create account with the provided data",
            },
        )

    return JSONResponse(
        status_code=201,
        content={
            "data": {
                "id": user.id,
                "email": user.email,
                "tier": user.plan.value,
            },
            "meta": {},
        },
    )


@router_v1.post(
    "/logout",
    summary="Déconnexion utilisateur",
    description="""
Déconnecte l'utilisateur en révoquant son token d'accès.

**Authentification requise:** Bearer token dans le header Authorization

**Paramètres optionnels:**
- `refresh_token`: Peut être fourni pour révoquer également le refresh token

**Réponse:**
- HTTP 200 avec message de confirmation

**Codes d'erreur:**
- `TOKEN_MISSING`: Authentication token missing
- `TOKEN_INVALID`: Token invalide ou expiré
    """,
    responses={
        200: {
            "description": "Déconnexion réussie",
            "content": {
                "application/json": {
                    "example": {"data": {"message": "Déconnexion réussie"}, "meta": {}}
                }
            },
        },
        401: {
            "description": "Erreur d'authentification",
            "content": {
                "application/json": {
                    "examples": {
                        "TOKEN_MISSING": {
                            "summary": "Missing token",
                            "value": {
                                "error": "TOKEN_MISSING",
                                "message": "Token d'authentification requis",
                            },
                        },
                        "TOKEN_INVALID": {
                            "summary": "Invalid token",
                            "value": {
                                "error": "TOKEN_INVALID",
                                "message": "Token invalide ou expiré",
                            },
                        },
                    }
                }
            },
        },
    },
)
async def logout_v1(request: Request):
    """Logout utilisateur (API v1) — révoque l'access token et optionnellement le refresh token"""
    auth_header = request.headers.get("Authorization", "")
    if not auth_header.startswith("Bearer "):
        return JSONResponse(
            status_code=401,
            content={
                "error": "TOKEN_MISSING",
                "message": "Token d'authentification requis",
            },
        )
    access_token = auth_header[7:]

    payload = verify_token(access_token)
    if not payload:
        return JSONResponse(
            status_code=401,
            content={
                "error": "TOKEN_INVALID",
                "message": "Token invalide ou expiré",
            },
        )

    jti = payload.get("jti")
    if jti:
        revoke_token_jti(jti, float(payload.get("exp", 0)))

    try:
        body = await request.json()
        refresh_token = body.get("refresh_token")
        if refresh_token:
            refresh_payload = verify_token(refresh_token)
            if refresh_payload and refresh_payload.get("jti"):
                revoke_token_jti(
                    refresh_payload["jti"],
                    float(refresh_payload.get("exp", 0)),
                )
    except Exception:
        pass

    return JSONResponse(
        status_code=200,
        content={"data": {"message": "Déconnexion réussie"}, "meta": {}},
    )


@router_v1.post(
    "/login",
    summary="Connexion utilisateur",
    description="""
Authentifie un utilisateur et retourne les tokens JWT.

**Paramètres requis:**
- `email`: Adresse email de l'utilisateur
- `password`: Mot de passe

**Réponse:**
- HTTP 200 avec `access_token` (expire dans 15 min) et `refresh_token` (expire dans 7 jours)

**Codes d'erreur:**
- `INVALID_REQUEST`: Corps de requête JSON invalide
- `INVALID_EMAIL`: Invalid email format
- `INVALID_CREDENTIALS`: Email ou mot de passe incorrect
    """,
    responses={
        200: {
            "description": "Connexion réussie",
            "content": {
                "application/json": {
                    "example": {
                        "data": {
                            "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                            "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                            "token_type": "bearer",
                        },
                        "meta": {},
                    }
                }
            },
        },
        400: {
            "description": "Erreur de validation",
            "content": {
                "application/json": {
                    "examples": {
                        "INVALID_REQUEST": {
                            "summary": "Invalid body",
                            "value": {
                                "error": "INVALID_REQUEST",
                                "message": "Corps de requête JSON invalide",
                            },
                        },
                        "INVALID_EMAIL": {
                            "summary": "Invalid email",
                            "value": {
                                "error": "INVALID_EMAIL",
                                "message": "Invalid email format",
                            },
                        },
                    }
                }
            },
        },
        401: {
            "description": "Identifiants invalides",
            "content": {
                "application/json": {
                    "example": {
                        "error": "INVALID_CREDENTIALS",
                        "message": "Incorrect email or password",
                    }
                }
            },
        },
    },
)
async def login_v1(request: Request):
    """Login utilisateur (API v1) — retourne access_token (15min) et refresh_token (7j)"""
    try:
        body = await request.json()
    except Exception:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Corps de requête JSON invalide",
            },
        )

    try:
        user_login = UserLogin.model_validate(body)
    except PydanticValidationError as exc:
        if _has_email_validation_error(exc):
            return JSONResponse(
                status_code=400,
                content={
                    "error": "INVALID_EMAIL",
                    "message": "Invalid email format",
                },
            )
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Invalid registration data",
            },
        )

    user = get_user_by_email(user_login.email)
    if not user:
        # Constant-time dummy verify to prevent user enumeration via response time
        verify_password("__dummy__", "$2b$12$mBw4RxPJBaaS1FtEZcT/E.E35YUCk1Zx0ICzIzNUSdzHmQmko1.WW")
        return JSONResponse(
            status_code=401,
            content={
                "error": "INVALID_CREDENTIALS",
                "message": "Incorrect email or password",
            },
        )

    if not verify_password(user_login.password, user.password_hash):
        return JSONResponse(
            status_code=401,
            content={
                "error": "INVALID_CREDENTIALS",
                "message": "Incorrect email or password",
            },
        )

    access_token = create_access_token(
        user.id, tier=getattr(user, "tier", user.plan.value), expires_delta=timedelta(minutes=15)
    )
    refresh_token = create_refresh_token(user.id, expires_delta=timedelta(days=7))

    return JSONResponse(
        status_code=200,
        content={
            "data": {
                "access_token": access_token,
                "refresh_token": refresh_token,
                "token_type": "bearer",
            },
            "meta": {},
        },
    )


@router_v1.post("/google")
async def google_auth_v1(body: GoogleAuthRequest):
    """Authentification via Google OAuth.
    Accepte soit un credential (ID token) soit un access_token Google,
    vérifie avec l'API Google, puis crée ou connecte l'utilisateur."""
    import httpx as _httpx

    if not body.credential and not body.access_token:
        return JSONResponse(
            status_code=400,
            content={"error": "MISSING_TOKEN", "message": "credential or access_token required."},
        )

    google_client_id = os.getenv("GOOGLE_CLIENT_ID", "")

    try:
        async with _httpx.AsyncClient(timeout=10.0) as client:
            if body.credential:
                # Verify ID token via tokeninfo
                resp = await client.get(
                    "https://oauth2.googleapis.com/tokeninfo",
                    params={"id_token": body.credential},
                )
            else:
                # Verify access token via userinfo
                resp = await client.get(
                    "https://www.googleapis.com/oauth2/v3/userinfo",
                    headers={"Authorization": f"Bearer {body.access_token}"},
                )
    except Exception:
        return JSONResponse(
            status_code=503,
            content={"error": "GOOGLE_UNREACHABLE", "message": "Unable to contact Google servers."},
        )

    if resp.status_code != 200:
        return JSONResponse(
            status_code=401,
            content={"error": "INVALID_GOOGLE_TOKEN", "message": "Invalid or expired Google token."},
        )

    google_data = resp.json()

    # Validate audience only for ID tokens (not for access_token flow)
    if body.credential and google_client_id and google_data.get("aud") != google_client_id:
        return JSONResponse(
            status_code=401,
            content={"error": "INVALID_TOKEN_AUDIENCE", "message": "Google token not intended for this application."},
        )

    email = google_data.get("email")
    if not email:
        return JSONResponse(
            status_code=400,
            content={"error": "MISSING_EMAIL", "message": "Email not provided by Google."},
        )

    name = google_data.get("name") or google_data.get("given_name") or email.split("@")[0]
    avatar_url = google_data.get("picture")

    try:
        user = get_or_create_google_user(email=email, name=name, avatar_url=avatar_url)
    except Exception as exc:
        logger.exception("Google authentication failed in get_or_create_google_user")
        return JSONResponse(
            status_code=500,
            content={"error": "USER_CREATE_FAILED", "message": str(exc)},
        )

    access_token = create_access_token(user.id, tier=getattr(user, "tier", "free"))
    refresh_token = create_refresh_token(user.id)

    return JSONResponse(
        status_code=200,
        content={
            "data": {
                "access_token": access_token,
                "refresh_token": refresh_token,
                "token_type": "bearer",
            },
            "meta": {},
        },
    )


@router_v1.get("/config")
async def get_auth_config():
    """Retrieve public configuration settings, such as Google Client ID."""
    return JSONResponse(
        status_code=200,
        content={
            "data": {
                "google_client_id": os.getenv("GOOGLE_CLIENT_ID", ""),
                "google_auth_enabled": bool(os.getenv("GOOGLE_CLIENT_ID", "").strip()),
            },
            "meta": {},
        },
    )


@router_v1.post("/refresh")
async def refresh_v1(request: Request):
    """Refresh tokens (API v1) — accepte refresh_token en corps, retourne nouvel access_token et refresh_token."""
    try:
        body = await request.json()
    except Exception:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Corps de requête JSON invalide",
            },
        )

    if not isinstance(body, dict):
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Corps de requête JSON invalide",
            },
        )

    refresh_token = body.get("refresh_token")
    if (
        not refresh_token
        or not isinstance(refresh_token, str)
        or not refresh_token.strip()
    ):
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Refresh token requis",
            },
        )

    payload = verify_token(refresh_token)
    if not payload:
        return JSONResponse(
            status_code=401,
            content={
                "error": "TOKEN_EXPIRED",
                "message": "Token invalide ou expiré",
            },
        )

    if payload.get("type") != "refresh":
        return JSONResponse(
            status_code=401,
            content={
                "error": "TOKEN_EXPIRED",
                "message": "Token invalide ou expiré",
            },
        )

    user = get_user_by_id(payload.get("sub"))
    if not user:
        return JSONResponse(
            status_code=401,
            content={
                "error": "TOKEN_EXPIRED",
                "message": "Token invalide ou expiré",
            },
        )

    access_token = create_access_token(
        user.id, tier=getattr(user, "tier", user.plan.value), expires_delta=timedelta(minutes=15)
    )
    new_refresh_token = create_refresh_token(user.id, expires_delta=timedelta(days=7))

    return JSONResponse(
        status_code=200,
        content={
            "data": {
                "access_token": access_token,
                "refresh_token": new_refresh_token,
                "token_type": "bearer",
            },
            "meta": {},
        },
    )


@router_v1.get(
    "/me",
    summary="Informations utilisateur",
    description="Retourne les informations de l'utilisateur authentifié.",
)
async def get_me_v1(user=Depends(require_user)):
    return JSONResponse(
        status_code=200,
        headers={"Cache-Control": "no-cache, no-store, must-revalidate"},
        content={"data": user_to_response(user).model_dump(mode="json"), "meta": {}},
    )


@router_v1.get(
    "/plans",
    summary="Liste des forfaits disponibles",
    description="Retourne tous les forfaits et packs de crédits disponibles (endpoint public).",
)
async def get_plans_v1():
    from models.subscription import PLANS, CREDIT_PACKAGES
    from services import pricing_config as pricing_cfg

    plans_list = []
    for plan_type, plan in PLANS.items():
        plan_id = plan_type.value
        monthly, yearly = pricing_cfg.get_effective_monthly_yearly(plan_id)
        plans_list.append(
            {
                "id": plan_id,
                "name": plan["name"],
                "price_monthly": monthly,
                "price_yearly": yearly,
                "annual_discount_percent": pricing_cfg.ANNUAL_DISCOUNT_PERCENT,
                "docs_per_month": plan["docs_per_month"],
                "max_pages_per_doc": plan["max_pages_per_doc"],
                "max_file_size_mb": plan["max_file_size_mb"],
                "max_chars_per_month": plan.get("max_chars_per_month", -1),
                "providers": plan["providers"],
                "features": plan["features"],
                "ai_translation": plan.get("ai_translation", False),
                "ai_tier": plan.get("ai_tier"),
                "ai_models_essential": plan.get("ai_models_essential", []),
                "ai_models_premium": plan.get("ai_models_premium", []),
                "api_access": plan.get("api_access", False),
                "priority_processing": plan.get("priority_processing", False),
                "team_seats": plan.get("team_seats"),
                "highlight": plan.get("highlight"),
                "description": plan.get("description"),
                "badge": plan.get("badge"),
                "popular": plan.get("badge") == "POPULAIRE",
            }
        )

    packages_list = []
    for pkg in CREDIT_PACKAGES:
        packages_list.append(
            {
                "credits": pkg["credits"],
                "price": pkg["price"],
                "price_per_credit": round(pkg["price"] / pkg["credits"], 4),
                "popular": pkg.get("popular", False),
            }
        )

    return JSONResponse(
        status_code=200,
        headers={
            "Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
        },
        content={
            "data": {"plans": plans_list, "credit_packages": packages_list},
            "meta": {
                "annual_discount_percent": pricing_cfg.ANNUAL_DISCOUNT_PERCENT,
                "yearly_discount_factor": pricing_cfg.YEARLY_DISCOUNT_FACTOR,
            },
        },
    )



@router_v1.get(
    "/usage",
    summary="Utilisation et limites",
    description="Retourne l'utilisation actuelle et les limites du plan de l'utilisateur.",
)
async def get_usage_v1(user=Depends(require_user)):
    return JSONResponse(
        status_code=200,
        content={"data": check_usage_limits(user), "meta": {}},
    )


@router_v1.post(
    "/create-checkout",
    summary="Créer une session de paiement",
    description="Crée une session Stripe Checkout pour souscrire à un forfait.",
)
async def create_checkout_v1(request: CheckoutRequest, user=Depends(require_user)):
    result = await create_checkout_session(
        user_id=user.id,
        plan=request.plan,
        billing_period=request.billing_period,
        promo_code=(request.promo_code or "").strip(),
    )
    if "error" in result and not result.get("demo_mode"):
        return JSONResponse(
            status_code=400,
            content={
                "error": result.get("error_code") or "CHECKOUT_FAILED",
                "message": result["error"],
                **(
                    {"promo_reason": result["promo_reason"]}
                    if result.get("promo_reason")
                    else {}
                ),
            },
        )
    return JSONResponse(status_code=200, content={"data": result, "meta": {}})


@router_v1.get(
    "/checkout/sync",
    summary="Synchroniser un checkout Stripe",
    description=(
        "Synchronise un paiement Stripe finalisé via session_id "
        "(utile en dev/local quand le webhook n'est pas reçu)."
    ),
)
async def sync_checkout_v1(
    session_id: str = Query(..., min_length=5),
    user=Depends(require_user),
):
    result = await sync_checkout_session(user_id=user.id, session_id=session_id)
    if "error" in result:
        return JSONResponse(
            status_code=400,
            content={
                "error": "CHECKOUT_SYNC_FAILED",
                "message": result["error"],
            },
        )
    return JSONResponse(status_code=200, content={"data": result, "meta": {}})


@router_v1.post(
    "/create-credits-checkout",
    summary="Créer une session de paiement pour crédits",
    description="Crée une session Stripe Checkout pour l'achat de crédits supplémentaires.",
)
async def create_credits_checkout_v1(request: CreditsCheckoutRequest, user=Depends(require_user)):
    result = await create_credits_checkout(
        user_id=user.id,
        package_index=request.package_index,
    )
    if "error" in result and not result.get("demo_mode"):
        return JSONResponse(
            status_code=400,
            content={
                "error": "CHECKOUT_FAILED",
                "message": result["error"],
            },
        )
    return JSONResponse(status_code=200, content={"data": result, "meta": {}})


@router_v1.post(
    "/cancel-subscription",
    summary="Annuler l'abonnement",
    description="Annule l'abonnement Stripe de l'utilisateur à la fin de la période en cours.",
)
async def cancel_subscription_v1(user=Depends(require_user)):
    result = await cancel_subscription(user.id)
    if "error" in result:
        return JSONResponse(
            status_code=400,
            content={"error": "CANCEL_FAILED", "message": result["error"]},
        )
    return JSONResponse(status_code=200, content={"data": result, "meta": {}})


@router_v1.get(
    "/billing-portal",
    summary="Portail de facturation",
    description="Retourne l'URL du portail de facturation Stripe.",
)
async def get_billing_portal_v1(user=Depends(require_user)):
    url = await get_billing_portal_url(user.id)
    if not url:
        return JSONResponse(
            status_code=400,
            content={
                "error": "BILLING_UNAVAILABLE",
                "message": "Billing portal unavailable",
            },
        )
    return JSONResponse(status_code=200, content={"data": {"url": url}, "meta": {}})


# ============== Codes promo (validation publique avant paiement) ==============

promos_public_router = APIRouter(prefix="/api/v1/promos", tags=["Promotions"])


@promos_public_router.get(
    "/validate",
    summary="Valider un code promo",
    description=(
        "Vérifie un code promo pour un forfait donné AVANT l'ouverture du "
        "paiement : renvoie valid, reason_code (cause explicite) et le montant "
        "de la remise. Aucune authentification requise."
    ),
)
async def validate_promo_v1(
    code: str = Query(..., min_length=1, max_length=24),
    plan: str = Query("pro", min_length=1, max_length=32),
):
    from services.payment_service import PromoValidationError, validate_promo_code

    wanted = (code or "").strip().upper()
    plan_id = (plan or "").strip().lower()
    try:
        promo = validate_promo_code(wanted, plan_id)
    except PromoValidationError as e:
        return JSONResponse(
            status_code=200,
            content={
                "data": {
                    "valid": False,
                    "reason_code": e.reason,
                    "message": e.message,
                },
                "meta": {},
            },
        )
    discount: Dict[str, Any] = {}
    if promo.get("type") == "percent":
        discount["percent_off"] = promo.get("value")
    else:
        discount["amount_off"] = promo.get("value")
    return JSONResponse(
        status_code=200,
        content={
            "data": {"valid": True, "reason_code": None, **discount},
            "meta": {},
        },
    )


# ============== Forgot / Reset password ==============


class ForgotPasswordRequest(BaseModel):
    email: EmailStr


class ResetPasswordRequest(BaseModel):
    token: str
    password: str


@router_v1.post(
    "/forgot-password",
    summary="Demander une reinitialisation de mot de passe",
    description="""
Envoie un email de reinitialisation si un compte existe avec cette adresse.

**Securite:** Retourne toujours 200 pour ne pas reveler si l'email existe.
    """,
)
async def forgot_password(request: Request):
    from services.email_service import is_smtp_configured, send_email_async

    if not is_smtp_configured():
        return JSONResponse(
            status_code=503,
            content={
                "error": "EMAIL_SERVICE_UNAVAILABLE",
                "message": "Email service not configured",
            },
        )

    try:
        body = await request.json()
    except Exception:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Corps de requête JSON invalide",
            },
        )

    email = body.get("email", "").strip()
    if not email:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Email required",
            },
        )

    # Always return 200 to avoid email enumeration
    user = get_user_by_email(email)

    if user and user.id:
        token = secrets.token_urlsafe(32)
        expires = datetime.now(timezone.utc) + timedelta(hours=1)

        # Store token in database
        try:
            from database.connection import get_sync_session
            from database.repositories import UserRepository
            from database.models import User as DBUser

            with get_sync_session() as session:
                repo = UserRepository(session)

                # Check if user exists in DB; if not (legacy JSON-only user), create DB record
                db_user = repo.get_by_email(email)
                if not db_user:
                    db_user = repo.create(
                        email=user.email,
                        name=user.name,
                        hashed_password=user.password_hash or "",
                        tier="free",
                    )

                repo.update(
                    db_user.id,
                    reset_token=token,
                    reset_token_expires=expires,
                )
        except Exception as exc:
            logger.error(f"Failed to store reset token: {exc}")
            # Still return 200 for security

        # Send email with reset link
        frontend_url = os.getenv("FRONTEND_URL", os.getenv("NEXT_PUBLIC_APP_URL", "http://localhost:3000"))
        reset_link = f"{frontend_url}/auth/reset-password?token={token}"

        html_body = f"""
        <html>
        <body style="font-family: Arial, sans-serif; padding: 20px;">
            <h2>Reinitialisation de votre mot de passe</h2>
            <p>Vous avez demande la reinitialisation de votre mot de passe.</p>
            <p>Cliquez sur le lien ci-dessous pour definir un nouveau mot de passe :</p>
            <p><a href="{reset_link}" style="background-color: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">Reinitialiser mon mot de passe</a></p>
            <p>Ce lien expire dans 1 heure.</p>
            <p>Si vous n'avez pas fait cette demande, ignorez cet email.</p>
        </body>
        </html>
        """

        await send_email_async(
            to=email,
            subject="Reinitialisation de votre mot de passe - Office Translator",
            body=html_body,
        )

    return JSONResponse(
        status_code=200,
        content={
            "data": {
                "message": "If an account exists with this email, a reset email has been sent."
            },
            "meta": {},
        },
    )


@router_v1.post(
    "/reset-password",
    summary="Reinitialiser le mot de passe",
    description="""
Reinitialise le mot de passe a l'aide d'un token valide.

**Parametres requis:**
- `token`: Le token recu par email
- `password`: Le nouveau mot de passe (min 8 caracteres, 1 majuscule, 1 minuscule, 1 chiffre)
    """,
)
async def reset_password(request: Request):
    try:
        body = await request.json()
    except Exception:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Corps de requête JSON invalide",
            },
        )

    token = body.get("token", "").strip()
    password = body.get("password", "")

    if not token or not password:
        return JSONResponse(
            status_code=400,
            content={
                "error": "INVALID_REQUEST",
                "message": "Token and password required",
            },
        )

    # Validate password strength
    if len(password) < 8 or not any(c.isupper() for c in password) or not any(c.islower() for c in password) or not any(c.isdigit() for c in password):
        return JSONResponse(
            status_code=400,
            content={
                "error": "WEAK_PASSWORD",
                "message": "Password must be at least 8 characters with at least one uppercase letter, one lowercase letter, and one digit",
            },
        )

    # Find user by reset token
    try:
        from database.connection import get_sync_session
        from database.models import User as DBUser
        from services.auth_service import hash_password

        with get_sync_session() as session:
            db_user = (
                session.query(DBUser)
                .filter(DBUser.reset_token == token)
                .first()
            )

            if not db_user:
                return JSONResponse(
                    status_code=400,
                    content={
                        "error": "INVALID_TOKEN",
                        "message": "Token invalide ou expiré",
                    },
                )

            # Check token expiry
            if db_user.reset_token_expires:
                expires = db_user.reset_token_expires
                if expires.tzinfo is None:
                    expires = expires.replace(tzinfo=timezone.utc)
                if expires < datetime.now(timezone.utc):
                    return JSONResponse(
                        status_code=400,
                        content={
                            "error": "TOKEN_EXPIRED",
                            "message": "Token expired, please request a new reset",
                        },
                    )

            # Update password and invalidate token
            db_user.hashed_password = hash_password(password)
            db_user.reset_token = None
            db_user.reset_token_expires = None
            db_user.updated_at = datetime.utcnow()
            session.commit()

    except Exception as exc:
        logger.error(f"Reset password failed: {exc}")
        return JSONResponse(
            status_code=500,
            content={
                "error": "RESET_FAILED",
                "message": "Error during password reset",
            },
        )

    return JSONResponse(
        status_code=200,
        content={
            "data": {"message": "Password reset successfully"},
            "meta": {},
        },
    )


# ============== Stripe webhook (versioned) ==============


@router_v1.post("/webhook/stripe")
async def stripe_webhook(request: Request, stripe_signature: str = Header(None)):
    """Handle Stripe webhooks"""
    payload = await request.body()

    result = await handle_webhook(payload, stripe_signature or "")

    if "error" in result:
        raise HTTPException(status_code=400, detail=result["error"])

    return result
