<?php

declare(strict_types=1);

namespace App\Services\NovaCards;

use App\Models\NovaCard;
use App\Models\NovaCardChallengeEntry;
use App\Models\NovaCardComment;
use App\Models\NovaCardCollectionItem;
use App\Models\NovaCardReaction;
use Carbon\Carbon;
use Carbon\CarbonInterface;

class NovaCardTrendingService
{
    public function refreshCard(NovaCard $card): NovaCard
    {
        $likes = NovaCardReaction::query()->where('card_id', $card->id)->where('type', NovaCardReaction::TYPE_LIKE)->count();
        $favorites = NovaCardReaction::query()->where('card_id', $card->id)->where('type', NovaCardReaction::TYPE_FAVORITE)->count();
        $saves = NovaCardCollectionItem::query()->where('card_id', $card->id)->count();
        $remixes = NovaCard::query()->where('original_card_id', $card->id)->count();
        $comments = NovaCardComment::query()->where('card_id', $card->id)->where('status', 'visible')->count();
        $challengeEntries = NovaCardChallengeEntry::query()->where('card_id', $card->id)->count();
        $lastEngagedAt = $this->lastEngagedAt($card);

        $card->forceFill([
            'likes_count' => $likes,
            'favorites_count' => $favorites,
            'saves_count' => $saves,
            'remixes_count' => $remixes,
            'comments_count' => $comments,
            'challenge_entries_count' => $challengeEntries,
            'last_engaged_at' => $lastEngagedAt,
            'trending_score' => $this->score($card, $likes, $favorites, $saves, $remixes, $comments, $challengeEntries, $lastEngagedAt),
        ])->save();

        return $card->refresh();
    }

    public function rebuildAll(): void
    {
        NovaCard::query()->select('id')->orderBy('id')->chunkById(100, function ($cards): void {
            foreach ($cards as $card) {
                $this->refreshCard(NovaCard::query()->findOrFail($card->id));
            }
        });
    }

    private function score(NovaCard $card, int $likes, int $favorites, int $saves, int $remixes, int $comments, int $challengeEntries, ?CarbonInterface $lastEngagedAt): float
    {
        $base = ($likes * 4.0)
            + ($favorites * 2.5)
            + ($saves * 5.0)
            + ($remixes * 6.0)
            + ($comments * 2.0)
            + ($challengeEntries * 4.0)
            + ($card->shares_count * 3.0)
            + ($card->downloads_count * 2.0)
            + ($card->views_count * 0.25);

        $engagedAt = $lastEngagedAt ?? $card->published_at ?? now();
        $ageHours = max(1.0, (float) $engagedAt->diffInHours(now()));
        $decay = max(0.2, 1 / (1 + ($ageHours / 72)));

        return round($base * $decay, 4);
    }

    private function lastEngagedAt(NovaCard $card): ?CarbonInterface
    {
        $timestamps = array_filter([
            NovaCardReaction::query()->where('card_id', $card->id)->max('created_at'),
            NovaCardCollectionItem::query()->where('card_id', $card->id)->max('created_at'),
            NovaCard::query()->where('original_card_id', $card->id)->max('created_at'),
            NovaCardComment::query()->where('card_id', $card->id)->max('created_at'),
            NovaCardChallengeEntry::query()->where('card_id', $card->id)->max('created_at'),
            $card->updated_at?->toDateTimeString(),
            $card->published_at?->toDateTimeString(),
        ]);

        if ($timestamps === []) {
            return null;
        }

        return collect($timestamps)
            ->map(fn ($timestamp) => Carbon::parse($timestamp))
            ->sortDesc()
            ->first();
    }
}