<?php

declare(strict_types=1);

namespace App\Services\EarlyGrowth;

use App\Models\Artwork;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

/**
 * SpotlightEngine
 *
 * Selects and rotates curated spotlight artworks for use in feed blending,
 * grid filling, and dedicated spotlight sections.
 *
 * Selection is date-seeded so the spotlight rotates daily without DB writes.
 * No artwork timestamps or engagement metrics are modified — this is purely
 * a read-and-present layer.
 */
final class SpotlightEngine implements SpotlightEngineInterface
{
    /**
     * Return spotlight artworks for the current day.
     * Cached for `early_growth.cache_ttl.spotlight` seconds (default 1 hour).
     * Rotates daily via a date-seeded RAND() expression.
     *
     * Returns empty collection when SpotlightEngine is disabled.
     */
    public function getSpotlight(int $limit = 6): Collection
    {
        if (! EarlyGrowth::spotlightEnabled()) {
            return collect();
        }

        $ttl      = (int) config('early_growth.cache_ttl.spotlight', 3600);
        $cacheKey = 'egs.spotlight.' . now()->format('Y-m-d') . ".{$limit}";

        return Cache::remember($cacheKey, $ttl, fn (): Collection => $this->selectSpotlight($limit));
    }

    /**
     * Return high-quality older artworks for feed blending ("curated" pool).
     * Excludes artworks newer than $olderThanDays to keep them out of the
     * "fresh" section yet available for blending.
     *
     * Cached per (limit, olderThanDays) tuple and rotated daily.
     */
    public function getCurated(int $limit = 12, int $olderThanDays = 7): Collection
    {
        if (! EarlyGrowth::enabled()) {
            return collect();
        }

        $ttl      = (int) config('early_growth.cache_ttl.spotlight', 3600);
        $cacheKey = 'egs.curated.' . now()->format('Y-m-d') . ".{$limit}.{$olderThanDays}";

        return Cache::remember($cacheKey, $ttl, fn (): Collection => $this->selectCurated($limit, $olderThanDays));
    }

    // ─── Private selection logic ──────────────────────────────────────────────

    /**
     * Select spotlight artworks.
     * Uses a date-based seed for deterministic daily rotation.
     * Fetches 3× the needed count and selects the top-ranked subset.
     */
    private function selectSpotlight(int $limit): Collection
    {
        $seed = (int) now()->format('Ymd');
        $randomExpr = $this->dailyRandomExpression('artworks.id', $seed);

        // Artworks published > 7 days ago with meaningful ranking score
        return Artwork::query()
            ->public()
            ->published()
            ->with([
                'user:id,name,username',
                'user.profile:user_id,avatar_hash',
                'categories:id,name,slug,content_type_id,parent_id,sort_order',
            ])
            ->leftJoin('artwork_stats as _ast', '_ast.artwork_id', '=', 'artworks.id')
            ->select('artworks.*')
            ->where('artworks.published_at', '<=', now()->subDays(7))
            // Blend ranking quality with daily-seeded randomness so spotlight varies
            ->orderByRaw("COALESCE(_ast.ranking_score, 0) * 0.6 + {$randomExpr} * 0.4 DESC")
            ->limit($limit * 3)
            ->get()
            ->sortByDesc(fn ($a) => optional($a->artworkStats)->ranking_score ?? 0)
            ->take($limit)
            ->values();
    }

    /**
     * Select curated older artworks for feed blending.
     */
    private function selectCurated(int $limit, int $olderThanDays): Collection
    {
        $seed = (int) now()->format('Ymd');
        $randomExpr = $this->dailyRandomExpression('artworks.id', $seed);

        return Artwork::query()
            ->public()
            ->published()
            ->with([
                'user:id,name,username',
                'user.profile:user_id,avatar_hash',
                'categories:id,name,slug,content_type_id,parent_id,sort_order',
            ])
            ->leftJoin('artwork_stats as _ast2', '_ast2.artwork_id', '=', 'artworks.id')
            ->select('artworks.*')
            ->where('artworks.published_at', '<=', now()->subDays($olderThanDays))
            ->orderByRaw("COALESCE(_ast2.ranking_score, 0) * 0.7 + {$randomExpr} * 0.3 DESC")
            ->limit($limit)
            ->get()
            ->values();
    }

    private function dailyRandomExpression(string $idColumn, int $seed): string
    {
        if (DB::connection()->getDriverName() === 'sqlite') {
            return "(ABS((({$idColumn} * 1103515245) + {$seed}) % 2147483647) / 2147483647.0)";
        }

        return "RAND({$seed} + {$idColumn})";
    }
}
