"""FastAPI router exposing billing endpoints."""

from __future__ import annotations

from typing import Annotated

from errors import (
    NotFoundError,
    ValidationError,
)
from fastapi import APIRouter, Header, Request
from pydantic import BaseModel

from billing.checkout import create_checkout, create_portal_session
from billing.invoices import get_invoices
from billing.models import CheckoutSession, Invoice, Subscription
from billing.subscriptions import cancel_subscription, get_subscription
from billing.usage import record_usage
from billing.webhooks import handle_webhook


class CheckoutRequest(BaseModel):
    """Request body for creating a checkout session."""

    user_id: str
    plan: str
    email: str | None = None


class PortalRequest(BaseModel):
    """Request body for creating a customer portal session."""

    customer_id: str


class UsageRequest(BaseModel):
    """Request body for recording usage."""

    user_id: str
    metric: str
    quantity: int = 1


class CancelRequest(BaseModel):
    """Request body for cancelling a subscription."""

    user_id: str
    at_period_end: bool = True


class PortalResponse(BaseModel):
    """Response containing a portal session URL."""

    url: str


BillingRouter = APIRouter(prefix="/billing", tags=["billing"])


@BillingRouter.post("/checkout", response_model=CheckoutSession)
async def checkout_endpoint(body: CheckoutRequest) -> CheckoutSession:
    """Create a Stripe Checkout session for a plan."""
    try:
        return create_checkout(
            user_id=body.user_id,
            plan=body.plan,
            email=body.email,
        )
    except ValueError as e:
        raise ValidationError(str(e)) from e


@BillingRouter.get("/subscription", response_model=Subscription | None)
async def subscription_endpoint(user_id: str) -> Subscription | None:
    """Get the current subscription for a user."""
    return get_subscription(user_id)


@BillingRouter.post("/cancel", response_model=Subscription)
async def cancel_endpoint(body: CancelRequest) -> Subscription:
    """Cancel a user's subscription."""
    try:
        return cancel_subscription(
            user_id=body.user_id,
            at_period_end=body.at_period_end,
        )
    except ValueError as e:
        raise NotFoundError(str(e)) from e


@BillingRouter.post("/portal", response_model=PortalResponse)
async def portal_endpoint(body: PortalRequest) -> PortalResponse:
    """Create a Stripe Customer Portal session."""
    url = create_portal_session(customer_id=body.customer_id)
    return PortalResponse(url=url)


@BillingRouter.post("/webhook")
async def webhook_endpoint(
    request: Request,
    stripe_signature: Annotated[str, Header(alias="Stripe-Signature")],
) -> dict:
    """Handle Stripe webhook events.

    Reads the raw body and verifies the Stripe signature before processing.
    """
    payload = await request.body()

    try:
        event = handle_webhook(payload=payload, signature=stripe_signature)
    except ValueError as e:
        raise ValidationError(str(e)) from e

    return {"type": event.type, "received": True}


@BillingRouter.post("/usage")
async def usage_endpoint(body: UsageRequest) -> dict:
    """Record metered usage for billing."""
    try:
        record_usage(
            user_id=body.user_id,
            metric=body.metric,
            quantity=body.quantity,
        )
    except ValueError as e:
        raise ValidationError(str(e)) from e

    return {"recorded": True}


@BillingRouter.get("/invoices", response_model=list[Invoice])
async def invoices_endpoint(user_id: str, limit: int = 10) -> list[Invoice]:
    """Fetch recent invoices for a user."""
    try:
        return get_invoices(user_id=user_id, limit=limit)
    except ValueError as e:
        raise NotFoundError(str(e)) from e
