<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Services\Recommendation\UserPreferenceBuilder;
use App\Services\Tags\TagDiscoveryService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * GET /api/user/suggestions/tags
 *
 * Returns up to 20 tag suggestions for the authenticated user.
 *
 * Sources:
 *   1. Tags from the user's favourited artworks and awards (affinity-ranked)
 *   2. Trending tags from global activity (fallback / discovery)
 *
 * Does NOT require the user to follow tags.  This endpoint provides the foundation
 * for a future "follow tags" feature while being useful immediately as discovery input.
 *
 * Cached per user for config('recommendations.ttl.tag_suggestions') seconds (default 60 min).
 */
final class SuggestedTagsController extends Controller
{
    private const LIMIT = 20;

    public function __construct(
        private readonly UserPreferenceBuilder $prefBuilder,
        private readonly TagDiscoveryService $tagDiscoveryService,
    ) {}

    public function __invoke(Request $request): JsonResponse
    {
        $user = $request->user();

        $ttl      = (int) config('recommendations.ttl.tag_suggestions', 60 * 60);
        $cacheKey = "tag_suggestions:{$user->id}";

        $data = Cache::remember($cacheKey, $ttl, function () use ($user) {
            return $this->buildSuggestions($user);
        });

        return response()->json(['data' => $data]);
    }

    private function buildSuggestions(\App\Models\User $user): array
    {
        try {
            $profile       = $this->prefBuilder->build($user);
            $knownTagSlugs = $profile->topTagSlugs; // already in user's profile – skip

            // ── Personalised tags (with normalised weights) ───────────────────
            $personalised = [];
            foreach ($profile->tagWeights as $slug => $weight) {
                if ($weight > 0.0) {
                    $personalised[$slug] = (float) $weight;
                }
            }
            arsort($personalised);

            // ── Trending tags (global, last 7 days) ───────────────────────────
            $trending = $this->trendingTags(40);

            // ── Merge: personalised first, then trending discovery ─────────────
            $merged = [];
            foreach ($personalised as $slug => $weight) {
                $merged[$slug] = [
                    'slug'   => $slug,
                    'score'  => $weight * 2.0, // boost personal signal
                    'source' => 'affinity',
                ];
            }

            foreach ($trending as $row) {
                $slug = (string) $row->slug;
                if (isset($merged[$slug])) {
                    $merged[$slug]['score'] += (float) $row->trend_score;
                } else {
                    $merged[$slug] = [
                        'slug'   => $slug,
                        'score'  => (float) $row->trend_score,
                        'source' => 'trending',
                    ];
                }
            }

            uasort($merged, fn ($a, $b) => $b['score'] <=> $a['score']);
            $top = array_slice(array_values($merged), 0, self::LIMIT);

            // ── Hydrate with DB info ──────────────────────────────────────────
            $slugs   = array_column($top, 'slug');
            $tagRows = DB::table('tags')
                ->whereIn('slug', $slugs)
                ->where('is_active', true)
                ->get(['id', 'name', 'slug', 'usage_count'])
                ->keyBy('slug');

            $result = [];
            foreach ($top as $item) {
                $tag = $tagRows->get($item['slug']);
                if ($tag === null) {
                    continue;
                }

                $result[] = [
                    'id'          => (int)    $tag->id,
                    'name'        => (string) $tag->name,
                    'slug'        => (string) $tag->slug,
                    'usage_count' => (int)    $tag->usage_count,
                    'source'      => (string) $item['source'],
                ];
            }

            return $result;
        } catch (\Throwable $e) {
            Log::warning('SuggestedTagsController: failed', [
                'user_id' => $user->id,
                'error'   => $e->getMessage(),
            ]);

            return [];
        }
    }

    /**
     * @return \Illuminate\Support\Collection<int, object{slug:string, trend_score:float}>
     */
    private function trendingTags(int $limit): \Illuminate\Support\Collection
    {
        return $this->tagDiscoveryService
            ->popularTags($limit, 7)
            ->map(static fn ($tag) => (object) [
                'slug' => (string) $tag->slug,
                'trend_score' => max(
                    (float) ($tag->recent_clicks ?? 0),
                    (float) ($tag->usage_count ?? 0) / 1000
                ),
            ]);
    }
}
