"""FastAPI router factory for referral endpoints."""

from __future__ import annotations

from typing import Any
from uuid import UUID

from fastapi import APIRouter, HTTPException, Query
from pydantic import BaseModel

from referral.models import (
    ApplyReferralRequest,
    CompleteReferralRequest,
    CreateCodeRequest,
    LeaderboardEntry,
    Referral,
    ReferralCode,
    ReferralStats,
)
from referral.rewards import RewardEngine
from referral.service import ReferralService
from referral.store import ReferralStore
from errors import AuthorizationError, BusinessLogicError, ConflictError, NotFoundError, ValidationError
from api_core.responses import ApiResponse, PaginatedResponse


# ---------------------------------------------------------------------------
# Response models
# ---------------------------------------------------------------------------


class CodeResponse(BaseModel):
    """Wrapper for a single referral code response."""

    code: ReferralCode


class CodesListResponse(BaseModel):
    """List of referral codes."""

    codes: list[ReferralCode]


class ReferralResponse(BaseModel):
    """Wrapper for a single referral."""

    referral: Referral


class StatsResponse(BaseModel):
    """Wrapper for user referral stats."""

    stats: ReferralStats


class LeaderboardResponse(BaseModel):
    """Referral leaderboard response."""

    entries: list[LeaderboardEntry]


# ---------------------------------------------------------------------------
# Router factory
# ---------------------------------------------------------------------------


def create_referral_router(
    store: ReferralStore,
    *,
    reward_engine: RewardEngine | None = None,
    prefix: str = "/referrals",
    tags: list[str] | None = None,
    dependencies: list[Any] | None = None,
) -> APIRouter:
    """Create a FastAPI router wired to the given store and reward engine.

    Args:
        store: Persistence backend for referral data.
        reward_engine: Optional reward engine for processing rewards on completion.
        prefix: URL prefix for all routes.
        tags: OpenAPI tags.
        dependencies: Optional list of FastAPI dependencies applied to all routes.

    Returns:
        A configured APIRouter with referral endpoints.
    """
    router = APIRouter(prefix=prefix, tags=tags or ["referrals"], dependencies=dependencies or [])
    service = ReferralService(store)

    @router.post("/codes", response_model=CodeResponse, status_code=201)
    async def create_code(request: CreateCodeRequest) -> CodeResponse:
        """Create a new referral code for a user."""
        try:
            code = await service.create_code(request.user_id, max_uses=request.max_uses)
        except ValueError as exc:
            raise ValidationError(str(exc)) from exc
        return CodeResponse(code=code)

    @router.get("/codes/{code}", response_model=CodeResponse)
    async def get_code(code: str) -> CodeResponse:
        """Look up a referral code by its string value."""
        result = await service.get_code(code)
        if result is None:
            raise NotFoundError("Referral code not found.")
        return CodeResponse(code=result)

    @router.get("/codes", response_model=CodesListResponse)
    async def list_user_codes(
        user_id: str = Query(..., description="User ID to list codes for"),
    ) -> CodesListResponse:
        """List all referral codes for a user."""
        codes = await service.get_user_codes(user_id)
        return CodesListResponse(codes=codes)

    @router.post("/apply", response_model=ReferralResponse, status_code=201)
    async def apply_referral(request: ApplyReferralRequest) -> ReferralResponse:
        """Apply a referral code for a new user."""
        try:
            referral = await service.apply_referral(request.code, request.referred_user_id)
        except ValueError as exc:
            raise ValidationError(str(exc)) from exc
        return ReferralResponse(referral=referral)

    @router.post("/{referral_id}/complete", response_model=ReferralResponse)
    async def complete_referral(referral_id: UUID) -> ReferralResponse:
        """Mark a referral as completed and optionally trigger rewards."""
        try:
            referral = await service.complete_referral(referral_id)
        except ValueError as exc:
            raise ValidationError(str(exc)) from exc

        if reward_engine is not None:
            await reward_engine.process_completed_referrals([referral])

        return ReferralResponse(referral=referral)

    @router.get("/stats/{user_id}", response_model=StatsResponse)
    async def get_stats(user_id: str) -> StatsResponse:
        """Get aggregate referral statistics for a user."""
        stats = await service.get_stats(user_id)
        return StatsResponse(stats=stats)

    @router.get("/leaderboard", response_model=LeaderboardResponse)
    async def get_leaderboard(
        limit: int = Query(10, ge=1, le=100, description="Number of top referrers"),
    ) -> LeaderboardResponse:
        """Get the referral leaderboard (top referrers)."""
        stats_list = await service.get_leaderboard(limit)
        entries = [
            LeaderboardEntry(
                user_id=s.user_id,
                completed_referrals=s.completed,
                total_rewards=s.total_rewards_earned,
            )
            for s in stats_list
        ]
        return LeaderboardResponse(entries=entries)

    @router.delete("/codes/{code}", status_code=204)
    async def deactivate_code(code: str) -> None:
        """Deactivate a referral code."""
        result = await service.deactivate_code(code)
        if result is None:
            raise NotFoundError("Referral code not found.")

    return router
