<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Artwork;
use App\Models\Collection;
use App\Models\CollectionQualitySnapshot;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class CollectionHealthService
{
    public function evaluate(Collection $collection): array
    {
        $metadataCompleteness = $this->metadataCompletenessScore($collection);
        $freshness = $this->freshnessScore($collection);
        $engagement = $this->engagementScore($collection);
        $readiness = $this->editorialReadinessScore($collection, $metadataCompleteness, $freshness, $engagement);
        $flags = $this->flags($collection, $metadataCompleteness, $freshness, $engagement, $readiness);
        $healthState = $this->healthStateFromFlags($flags);
        $healthScore = $this->healthScore($metadataCompleteness, $freshness, $engagement, $readiness, $flags);
        $placementEligibility = $this->placementEligibility($collection, $healthState, $readiness);

        return [
            'metadata_completeness_score' => $metadataCompleteness,
            'freshness_score' => $freshness,
            'engagement_score' => $engagement,
            'editorial_readiness_score' => $readiness,
            'health_score' => $healthScore,
            'health_state' => $healthState,
            'health_flags_json' => $flags,
            'readiness_state' => $this->readinessState($placementEligibility, $flags),
            'placement_eligibility' => $placementEligibility,
            'duplicate_cluster_key' => $this->duplicateClusterKey($collection),
            'trust_tier' => $this->trustTier($collection, $healthScore),
            'last_health_check_at' => now(),
        ];
    }

    public function refresh(Collection $collection, ?User $actor = null, string $reason = 'refresh'): Collection
    {
        $payload = $this->evaluate($collection->fresh());
        $snapshotDate = now()->toDateString();

        $collection->forceFill($payload)->save();

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

        if ($snapshot) {
            $snapshot->forceFill([
                'quality_score' => $collection->quality_score,
                'health_score' => $payload['health_score'],
                'metadata_completeness_score' => $payload['metadata_completeness_score'],
                'freshness_score' => $payload['freshness_score'],
                'engagement_score' => $payload['engagement_score'],
                'readiness_score' => $payload['editorial_readiness_score'],
                'flags_json' => $payload['health_flags_json'],
            ])->save();
        } else {
            CollectionQualitySnapshot::query()->create([
                'collection_id' => $collection->id,
                'snapshot_date' => $snapshotDate,
                'quality_score' => $collection->quality_score,
                'health_score' => $payload['health_score'],
                'metadata_completeness_score' => $payload['metadata_completeness_score'],
                'freshness_score' => $payload['freshness_score'],
                'engagement_score' => $payload['engagement_score'],
                'readiness_score' => $payload['editorial_readiness_score'],
                'flags_json' => $payload['health_flags_json'],
            ]);
        }

        $fresh = $collection->fresh();

        app(CollectionHistoryService::class)->record(
            $fresh,
            $actor,
            'health_refreshed',
            sprintf('Collection health refreshed via %s.', $reason),
            null,
            [
                'health_state' => $fresh->health_state,
                'readiness_state' => $fresh->readiness_state,
                'placement_eligibility' => (bool) $fresh->placement_eligibility,
                'health_score' => (float) ($fresh->health_score ?? 0),
                'flags' => $fresh->health_flags_json,
            ]
        );

        return $fresh;
    }

    public function summary(Collection $collection): array
    {
        return [
            'health_state' => $collection->health_state,
            'readiness_state' => $collection->readiness_state,
            'health_score' => $collection->health_score !== null ? (float) $collection->health_score : null,
            'metadata_completeness_score' => $collection->metadata_completeness_score !== null ? (float) $collection->metadata_completeness_score : null,
            'editorial_readiness_score' => $collection->editorial_readiness_score !== null ? (float) $collection->editorial_readiness_score : null,
            'freshness_score' => $collection->freshness_score !== null ? (float) $collection->freshness_score : null,
            'engagement_score' => $collection->engagement_score !== null ? (float) $collection->engagement_score : null,
            'placement_eligibility' => (bool) $collection->placement_eligibility,
            'flags' => is_array($collection->health_flags_json) ? $collection->health_flags_json : [],
            'duplicate_cluster_key' => $collection->duplicate_cluster_key,
            'canonical_collection_id' => $collection->canonical_collection_id ? (int) $collection->canonical_collection_id : null,
            'last_health_check_at' => optional($collection->last_health_check_at)?->toISOString(),
        ];
    }

    private function metadataCompletenessScore(Collection $collection): float
    {
        $score = 0.0;
        $score += filled($collection->title) ? 18.0 : 0.0;
        $score += filled($collection->summary) ? 18.0 : 0.0;
        $score += filled($collection->description) ? 14.0 : 0.0;
        $score += $this->hasStrongCover($collection) ? 18.0 : ($collection->resolvedCoverArtwork(false) ? 8.0 : 0.0);
        $score += (int) $collection->artworks_count >= 6 ? 16.0 : ((int) $collection->artworks_count >= 4 ? 10.0 : ((int) $collection->artworks_count >= 2 ? 5.0 : 0.0));
        $score += $collection->usesPremiumPresentation() ? 8.0 : 0.0;
        $score += filled($collection->campaign_key) || filled($collection->event_key) || filled($collection->season_key) ? 8.0 : 0.0;

        return round(min(100.0, $score), 2);
    }

    private function freshnessScore(Collection $collection): float
    {
        $reference = $collection->last_activity_at ?: $collection->updated_at ?: $collection->published_at;

        if ($reference === null) {
            return 0.0;
        }

        $days = max(0, now()->diffInDays($reference));

        if ($days >= 45) {
            return 0.0;
        }

        return round(max(0.0, 100.0 - (($days / 45) * 100.0)), 2);
    }

    private function engagementScore(Collection $collection): float
    {
        $weighted = ((int) $collection->likes_count * 3.0)
            + ((int) $collection->followers_count * 4.5)
            + ((int) $collection->saves_count * 4.0)
            + ((int) $collection->comments_count * 2.0)
            + ((int) $collection->shares_count * 2.5)
            + ((int) $collection->views_count * 0.08);

        return round(min(100.0, $weighted), 2);
    }

    private function editorialReadinessScore(Collection $collection, float $metadataCompleteness, float $freshness, float $engagement): float
    {
        $score = ($metadataCompleteness * 0.45) + ($freshness * 0.2) + ($engagement * 0.2);
        $score += $collection->moderation_status === Collection::MODERATION_ACTIVE ? 10.0 : -20.0;
        $score += $collection->visibility === Collection::VISIBILITY_PUBLIC ? 10.0 : -10.0;
        $score += in_array((string) $collection->workflow_state, [Collection::WORKFLOW_APPROVED, Collection::WORKFLOW_PROGRAMMED], true) ? 10.0 : 0.0;

        return round(max(0.0, min(100.0, $score)), 2);
    }

    private function flags(Collection $collection, float $metadataCompleteness, float $freshness, float $engagement, float $readiness): array
    {
        $flags = [];
        $artworksCount = (int) $collection->artworks_count;

        if ($metadataCompleteness < 55) {
            $flags[] = Collection::HEALTH_NEEDS_METADATA;
        }

        if ($artworksCount < 6) {
            $flags[] = Collection::HEALTH_LOW_CONTENT;
        }

        if (! $this->hasStrongCover($collection)) {
            $flags[] = Collection::HEALTH_WEAK_COVER;
        }

        if ($freshness <= 0.0 && $collection->isPubliclyAccessible()) {
            $flags[] = Collection::HEALTH_STALE;
        }

        if ($engagement < 15 && $collection->isPubliclyAccessible() && $collection->published_at?->lt(now()->subDays(21))) {
            $flags[] = Collection::HEALTH_LOW_ENGAGEMENT;
        }

        if ($collection->moderation_status !== Collection::MODERATION_ACTIVE || (string) $collection->workflow_state === Collection::WORKFLOW_IN_REVIEW) {
            $flags[] = Collection::HEALTH_NEEDS_REVIEW;
        }

        if ($this->brokenItemsRatio($collection) > 0.25) {
            $flags[] = Collection::HEALTH_BROKEN_ITEMS;
        }

        if ($this->hasDuplicateRisk($collection)) {
            $flags[] = Collection::HEALTH_DUPLICATE_RISK;
        }

        if ($collection->canonical_collection_id !== null) {
            $flags[] = Collection::HEALTH_MERGE_CANDIDATE;
        }

        if ($readiness < 45 && $collection->type === Collection::TYPE_EDITORIAL && $artworksCount < 6) {
            $flags[] = Collection::HEALTH_ATTRIBUTION_INCOMPLETE;
        }

        return array_values(array_unique($flags));
    }

    private function healthStateFromFlags(array $flags): string
    {
        foreach ([
            Collection::HEALTH_MERGE_CANDIDATE,
            Collection::HEALTH_DUPLICATE_RISK,
            Collection::HEALTH_NEEDS_REVIEW,
            Collection::HEALTH_BROKEN_ITEMS,
            Collection::HEALTH_LOW_CONTENT,
            Collection::HEALTH_WEAK_COVER,
            Collection::HEALTH_NEEDS_METADATA,
            Collection::HEALTH_STALE,
            Collection::HEALTH_LOW_ENGAGEMENT,
            Collection::HEALTH_ATTRIBUTION_INCOMPLETE,
        ] as $flag) {
            if (in_array($flag, $flags, true)) {
                return $flag;
            }
        }

        return Collection::HEALTH_HEALTHY;
    }

    private function healthScore(float $metadataCompleteness, float $freshness, float $engagement, float $readiness, array $flags): float
    {
        $score = ($metadataCompleteness * 0.35) + ($freshness * 0.2) + ($engagement * 0.2) + ($readiness * 0.25);
        $score -= count($flags) * 6.5;

        return round(max(0.0, min(100.0, $score)), 2);
    }

    private function placementEligibility(Collection $collection, string $healthState, float $readiness): bool
    {
        if ($collection->visibility !== Collection::VISIBILITY_PUBLIC) {
            return false;
        }

        if ($collection->moderation_status !== Collection::MODERATION_ACTIVE) {
            return false;
        }

        if (! in_array($collection->lifecycle_state, [Collection::LIFECYCLE_PUBLISHED, Collection::LIFECYCLE_FEATURED], true)) {
            return false;
        }

        if (in_array($healthState, [Collection::HEALTH_BROKEN_ITEMS, Collection::HEALTH_MERGE_CANDIDATE], true)) {
            return false;
        }

        if ($collection->workflow_state === Collection::WORKFLOW_IN_REVIEW) {
            return false;
        }

        return $readiness >= 45.0;
    }

    private function readinessState(bool $placementEligibility, array $flags): string
    {
        if (! $placementEligibility) {
            return Collection::READINESS_BLOCKED;
        }

        if ($flags !== []) {
            return Collection::READINESS_NEEDS_WORK;
        }

        return Collection::READINESS_READY;
    }

    private function duplicateClusterKey(Collection $collection): ?string
    {
        $existing = trim((string) ($collection->duplicate_cluster_key ?? ''));

        return $existing !== '' ? $existing : null;
    }

    private function trustTier(Collection $collection, float $healthScore): string
    {
        if ($collection->type === Collection::TYPE_EDITORIAL) {
            return 'editorial';
        }

        if ($healthScore >= 80) {
            return 'high';
        }

        if ($healthScore >= 50) {
            return 'standard';
        }

        return 'limited';
    }

    private function brokenItemsRatio(Collection $collection): float
    {
        if ($collection->isSmart() || (int) $collection->artworks_count === 0) {
            return 0.0;
        }

        $visibleCount = DB::table('collection_artwork as ca')
            ->join('artworks as a', 'a.id', '=', 'ca.artwork_id')
            ->where('ca.collection_id', $collection->id)
            ->whereNull('a.deleted_at')
            ->where('a.is_public', true)
            ->where('a.is_approved', true)
            ->whereNotNull('a.published_at')
            ->where('a.published_at', '<=', now())
            ->count();

        return max(0.0, ((int) $collection->artworks_count - $visibleCount) / max(1, (int) $collection->artworks_count));
    }

    private function hasDuplicateRisk(Collection $collection): bool
    {
        return Collection::query()
            ->where('id', '!=', $collection->id)
            ->where('user_id', $collection->user_id)
            ->whereRaw('LOWER(title) = ?', [mb_strtolower(trim((string) $collection->title))])
            ->exists();
    }

    private function hasStrongCover(Collection $collection): bool
    {
        if (! $collection->cover_artwork_id) {
            return false;
        }

        $cover = $collection->relationLoaded('coverArtwork')
            ? $collection->coverArtwork
            : $collection->coverArtwork()->first();

        if (! $cover instanceof Artwork) {
            return false;
        }

        if ($collection->isPubliclyAccessible() && ! $cover->published_at) {
            return false;
        }

        $width = (int) ($cover->width ?? 0);
        $height = (int) ($cover->height ?? 0);

        return $width >= 320 && $height >= 220;
    }
}