<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\ArtworkListResource;
use App\Models\Artwork;
use App\Models\Category;
use App\Models\ContentType;
use App\Services\ContentTypes\ContentTypeSlugResolver;
use App\Services\RankingService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;

/**
 * RankController
 *
 * Serves pre-computed ranked artwork lists.
 *
 * Endpoints:
 *   GET /api/rank/global?type=trending|new_hot|best
 *   GET /api/rank/category/{id}?type=trending|new_hot|best
 *   GET /api/rank/type/{contentType}?type=trending|new_hot|best
 */
class RankController extends Controller
{
    public function __construct(
        private readonly RankingService $ranking,
        private readonly ContentTypeSlugResolver $contentTypeResolver,
    ) {}

    /**
     * GET /api/rank/global
     *
     * Returns: { data: [...], meta: { list_type, computed_at, model_version, fallback } }
     */
    public function global(Request $request): AnonymousResourceCollection|JsonResponse
    {
        $listType = $this->resolveListType($request);
        $result   = $this->ranking->getList('global', null, $listType);

        return $this->buildResponse($result, $listType);
    }

    /**
     * GET /api/rank/category/{id}
     */
    public function byCategory(Request $request, int $id): AnonymousResourceCollection|JsonResponse
    {
        if (! Category::where('id', $id)->where('is_active', true)->exists()) {
            return response()->json(['message' => 'Category not found.'], 404);
        }

        $listType = $this->resolveListType($request);
        $result   = $this->ranking->getList('category', $id, $listType);

        return $this->buildResponse($result, $listType);
    }

    /**
     * GET /api/rank/type/{contentType}
     *
     * {contentType} is accepted as either a slug (string) or numeric id.
     */
    public function byContentType(Request $request, string $contentType): AnonymousResourceCollection|JsonResponse
    {
        $ct = is_numeric($contentType)
            ? ContentType::find((int) $contentType)
            : $this->contentTypeResolver->resolve($contentType)->contentType;

        if ($ct === null) {
            return response()->json(['message' => 'Content type not found.'], 404);
        }

        $listType = $this->resolveListType($request);
        $result   = $this->ranking->getList('content_type', $ct->id, $listType);

        return $this->buildResponse($result, $listType);
    }

    // ── Private helpers ────────────────────────────────────────────────────

    /**
     * Validate and normalise the ?type query param.
     * Defaults to 'trending'.
     */
    private function resolveListType(Request $request): string
    {
        $allowed = ['trending', 'new_hot', 'best'];
        $type    = $request->query('type', 'trending');

        return in_array($type, $allowed, true) ? $type : 'trending';
    }

    /**
     * Hydrate artwork IDs into Eloquent models (no N+1) and wrap in resources.
     *
     * @param  array{ids: int[], computed_at: string|null, model_version: string, fallback: bool} $result
     */
    private function buildResponse(array $result, string $listType = 'trending'): AnonymousResourceCollection
    {
        $ids      = $result['ids'];
        $artworks = collect();

        if (! empty($ids)) {
            // Single whereIn query — no N+1
            $keyed = Artwork::whereIn('id', $ids)
                ->with([
                    'user:id,name',
                    'user.profile:user_id,avatar_hash',
                    'categories' => function ($q): void {
                        $q->select(
                            'categories.id',
                            'categories.content_type_id',
                            'categories.parent_id',
                            'categories.name',
                            'categories.slug',
                            'categories.sort_order'
                        )->with(['parent:id,parent_id,content_type_id,name,slug', 'contentType:id,slug,name']);
                    },
                ])
                ->get()
                ->keyBy('id');

            // Restore the ranked order
            $artworks = collect($ids)
                ->filter(fn ($id) => $keyed->has($id))
                ->map(fn ($id) => $keyed[$id]);
        }

        $collection = ArtworkListResource::collection($artworks);

        // Attach ranking meta as additional data
        $collection->additional([
            'meta' => [
                'list_type'     => $listType,
                'computed_at'   => $result['computed_at'],
                'model_version' => $result['model_version'],
                'fallback'      => $result['fallback'],
                'count'         => $artworks->count(),
            ],
        ]);

        return $collection;
    }
}
