<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Collection;
use App\Models\CollectionRecommendationSnapshot;

class CollectionRankingService
{
    public function explain(Collection $collection, string $context = 'default'): array
    {
        $signals = [
            'quality_score' => (float) ($collection->quality_score ?? 0),
            'health_score' => (float) ($collection->health_score ?? 0),
            'freshness_score' => (float) ($collection->freshness_score ?? 0),
            'engagement_score' => (float) ($collection->engagement_score ?? 0),
            'editorial_readiness_score' => (float) ($collection->editorial_readiness_score ?? 0),
        ];

        $score = ($signals['quality_score'] * 0.3)
            + ($signals['health_score'] * 0.3)
            + ($signals['freshness_score'] * 0.15)
            + ($signals['engagement_score'] * 0.2)
            + ($signals['editorial_readiness_score'] * 0.05);

        if ($collection->is_featured) {
            $score += 5.0;
        }

        if ($context === 'campaign' && filled($collection->campaign_key)) {
            $score += 7.5;
        }

        if ($context === 'evergreen' && $signals['freshness_score'] < 35 && $signals['quality_score'] >= 70) {
            $score += 6.0;
        }

        $score = round(max(0.0, min(150.0, $score)), 2);

        return [
            'score' => $score,
            'context' => $context,
            'signals' => $signals,
            'bucket' => $this->rankingBucket($score),
            'recommendation_tier' => $this->recommendationTier($score),
            'search_boost_tier' => $this->searchBoostTier($collection, $score),
            'rationale' => [
                sprintf('Quality %.1f', $signals['quality_score']),
                sprintf('Health %.1f', $signals['health_score']),
                sprintf('Freshness %.1f', $signals['freshness_score']),
                sprintf('Engagement %.1f', $signals['engagement_score']),
            ],
        ];
    }

    public function refresh(Collection $collection, string $context = 'default'): Collection
    {
        $explanation = $this->explain($collection->fresh(), $context);
        $snapshotDate = now()->toDateString();

        $collection->forceFill([
            'ranking_bucket' => $explanation['bucket'],
            'recommendation_tier' => $explanation['recommendation_tier'],
            'search_boost_tier' => $explanation['search_boost_tier'],
            'last_recommendation_refresh_at' => now(),
        ])->save();

        $snapshot = CollectionRecommendationSnapshot::query()
            ->where('collection_id', $collection->id)
            ->where('context_key', $context)
            ->whereDate('snapshot_date', $snapshotDate)
            ->first();

        if ($snapshot) {
            $snapshot->forceFill([
                'recommendation_score' => $explanation['score'],
                'rationale_json' => $explanation,
            ])->save();
        } else {
            CollectionRecommendationSnapshot::query()->create([
                'collection_id' => $collection->id,
                'context_key' => $context,
                'recommendation_score' => $explanation['score'],
                'rationale_json' => $explanation,
                'snapshot_date' => $snapshotDate,
            ]);
        }

        return $collection->fresh();
    }

    private function rankingBucket(float $score): string
    {
        return match (true) {
            $score >= 110 => 'elite',
            $score >= 85 => 'strong',
            $score >= 60 => 'steady',
            $score >= 35 => 'emerging',
            default => 'cold',
        };
    }

    private function recommendationTier(float $score): string
    {
        return match (true) {
            $score >= 105 => 'premium',
            $score >= 80 => 'primary',
            $score >= 55 => 'secondary',
            default => 'fallback',
        };
    }

    private function searchBoostTier(Collection $collection, float $score): string
    {
        if ($collection->type === Collection::TYPE_EDITORIAL && $score >= 80) {
            return 'editorial';
        }

        if ($collection->placement_eligibility && $score >= 70) {
            return 'high';
        }

        if ($score >= 45) {
            return 'standard';
        }

        return 'low';
    }
}