<?php

declare(strict_types=1);

namespace App\Services\NovaCards;

use App\Models\NovaCard;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

/**
 * Surfaces "rising" cards — recently published cards that are gaining
 * engagement faster than average, weighted to balance novelty creators.
 */
class NovaCardRisingService
{
    /** Number of hours a card is eligible for "rising" feed. */
    private const WINDOW_HOURS = 96;

    /** Cache TTL in seconds. */
    private const CACHE_TTL = 300;

    public function risingCards(int $limit = 18, bool $cached = true): Collection
    {
        if ($cached) {
            return Cache::remember(
                'nova_cards.rising.' . $limit,
                self::CACHE_TTL,
                fn () => $this->queryRising($limit),
            );
        }

        return $this->queryRising($limit);
    }

    public function invalidateCache(): void
    {
        foreach ([6, 18, 24, 36] as $limit) {
            Cache::forget('nova_cards.rising.' . $limit);
        }
    }

    private function queryRising(int $limit): Collection
    {
        $cutoff = Carbon::now()->subHours(self::WINDOW_HOURS);
        $isSqlite = DB::connection()->getDriverName() === 'sqlite';
        $ageHoursExpression = $isSqlite
            ? "CASE WHEN ((julianday('now') - julianday(published_at)) * 24.0) < 1 THEN 1 ELSE ((julianday('now') - julianday(published_at)) * 24.0) END"
            : 'GREATEST(1, TIMESTAMPDIFF(HOUR, published_at, NOW()))';
        $decayExpression = $isSqlite
            ? $ageHoursExpression
            : 'POWER(' . $ageHoursExpression . ', 0.7)';
        $risingMomentumExpression = '(
                    (saves_count * 5.0 + remixes_count * 6.0 + likes_count * 4.0 + favorites_count * 2.5 + comments_count * 2.0 + challenge_entries_count * 4.0)
                    / ' . $decayExpression . '
                ) AS rising_momentum';

        return NovaCard::query()
            ->publiclyVisible()
            ->where('published_at', '>=', $cutoff)
            // Must have at least one meaningful engagement signal.
            ->where(function (Builder $q): void {
                $q->where('saves_count', '>', 0)
                    ->orWhere('remixes_count', '>', 0)
                    ->orWhere('likes_count', '>', 1);
            })
            ->select([
                'nova_cards.*',
                // Rising score: weight recent engagement, penalise by sqrt(age hours) to let novelty show
                DB::raw($risingMomentumExpression),
            ])
            ->orderByDesc('rising_momentum')
            ->orderByDesc('published_at')
            ->limit($limit)
            ->with(['user.profile', 'category', 'template', 'backgroundImage', 'tags'])
            ->get();
    }
}
