<?php

declare(strict_types=1);

namespace App\Services\Academy;

use App\Models\AcademyContentMetricDaily;
use App\Models\AcademySearchLog;
use App\Models\AcademyUserProgress;
use App\Support\AcademyAnalytics\AcademyAnalyticsContentType;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;

final class AcademyContentIntelligenceService
{
    public function __construct(private readonly AcademyAnalyticsContentResolver $resolver) {}

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    public function getContentOpportunities(array $filters = []): array
    {
        return $this->remember('content-opportunities', $filters, function (Carbon $from, Carbon $to, int $limit): array {
            $searchGaps = $this->getSearchGaps(['from' => $from, 'to' => $to, 'limit' => $limit]);
            $promptInsights = $this->getPromptInsights(['from' => $from, 'to' => $to, 'limit' => $limit]);
            $lessonDropoffs = $this->getLessonDropoffs(['from' => $from, 'to' => $to, 'limit' => $limit]);
            $courseHealth = $this->getCourseHealth(['from' => $from, 'to' => $to, 'limit' => $limit]);
            $premiumInterest = $this->getPremiumInterest(['from' => $from, 'to' => $to, 'limit' => $limit]);
            $recommendations = $this->getEditorialRecommendations(['from' => $from, 'to' => $to, 'limit' => $limit]);

            $cards = [
                [
                    'label' => 'Content opportunities',
                    'value' => count($recommendations['rows']),
                    'description' => 'Actionable content, conversion, and editorial recommendations generated from Academy analytics.',
                ],
                [
                    'label' => 'Search gaps',
                    'value' => (int) $searchGaps['summary']['gap_count'],
                    'description' => 'Queries with zero results, weak CTR, or no result clicks.',
                ],
                [
                    'label' => 'Prompt insights',
                    'value' => (int) $promptInsights['summary']['signal_count'],
                    'description' => 'Prompts that should be improved, promoted, or expanded.',
                ],
                [
                    'label' => 'Lesson drop-offs',
                    'value' => (int) $lessonDropoffs['summary']['signal_count'],
                    'description' => 'Lessons losing users before they meaningfully start or finish.',
                ],
                [
                    'label' => 'Course health',
                    'value' => (int) $courseHealth['summary']['signal_count'],
                    'description' => 'Courses that need restructuring or are ready for expansion.',
                ],
                [
                    'label' => 'Premium interest',
                    'value' => (int) $premiumInterest['summary']['signal_count'],
                    'description' => 'Content that shows premium teaser strength or weakness.',
                ],
                [
                    'label' => 'Editorial recommendations',
                    'value' => (int) $recommendations['summary']['total'],
                    'description' => 'Prioritized actions for what to create, improve, promote, or premiumize next.',
                ],
            ];

            return [
                'cards' => $cards,
                'highlights' => collect($recommendations['rows'])
                    ->take(6)
                    ->map(fn (array $row): array => [
                        'title' => (string) $row['title'],
                        'priority' => (string) $row['priority'],
                        'reason' => (string) $row['reason'],
                        'suggested_action' => (string) $row['suggested_action'],
                    ])
                    ->values()
                    ->all(),
            ];
        });
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    public function getSearchGaps(array $filters = []): array
    {
        return $this->remember('search-gaps', $filters, function (Carbon $from, Carbon $to, int $limit): array {
            $rows = $this->searchQuery($from, $to)->get()->map(function ($row): array {
                $searches = max(0, (int) $row->searches);
                $clicks = max(0, (int) ($row->clicks ?? 0));
                $resultsCount = round((float) ($row->avg_results_count ?? 0), 1);
                $ctr = $searches > 0 ? round(($clicks / $searches) * 100, 1) : 0.0;
                $signal = $this->classifySearchGap(
                    searches: $searches,
                    resultsCount: $resultsCount,
                    clicks: $clicks,
                    ctr: $ctr,
                    loggedInSearches: max(0, (int) ($row->logged_in_searches ?? 0)),
                    subscriberSearches: max(0, (int) ($row->subscriber_searches ?? 0)),
                );

                return [
                    'query' => (string) ($row->query ?: $row->normalized_query),
                    'normalized_query' => (string) $row->normalized_query,
                    'searches' => $searches,
                    'results_count' => $resultsCount,
                    'clicks' => $clicks,
                    'ctr' => $ctr,
                    'last_searched_at' => $row->last_searched_at ? Carbon::parse((string) $row->last_searched_at)->toDateTimeString() : null,
                    'logged_in_searches' => max(0, (int) ($row->logged_in_searches ?? 0)),
                    'subscriber_searches' => max(0, (int) ($row->subscriber_searches ?? 0)),
                    'issue' => $signal['issue'],
                    'priority' => $signal['priority'],
                    'priority_score' => $signal['priority_score'],
                    'suggested_action' => $signal['suggested_action'],
                ];
            })->filter(fn (array $row): bool => $row['issue'] !== null)->values();

            $zeroResultSearches = $rows
                ->filter(fn (array $row): bool => $row['issue'] === 'Zero-result demand')
                ->sortByDesc('searches')
                ->take($limit)
                ->values();
            $searchesWithResultsNoClicks = $rows
                ->filter(fn (array $row): bool => $row['issue'] === 'Results with no clicks')
                ->sortByDesc('searches')
                ->take($limit)
                ->values();
            $lowCtrSearches = $rows
                ->filter(fn (array $row): bool => $row['issue'] === 'Low click-through rate')
                ->sortBy('ctr')
                ->take($limit)
                ->values();
            $highCtrSearches = $rows
                ->filter(fn (array $row): bool => $row['issue'] === 'High click-through topic')
                ->sortByDesc('ctr')
                ->take($limit)
                ->values();
            $repeatedQueries = $rows
                ->filter(fn (array $row): bool => $row['logged_in_searches'] >= 2 || $row['subscriber_searches'] >= 2)
                ->sortByDesc('searches')
                ->take($limit)
                ->values();

            $dedupedRows = $this->dedupeByKey(
                collect([$zeroResultSearches, $searchesWithResultsNoClicks, $lowCtrSearches, $highCtrSearches])
                    ->flatten(1)
                    ->sortByDesc('priority_score')
                    ->sortByDesc('searches')
                    ->values(),
                'normalized_query',
            )->take($limit)->values();

            return [
                'summary' => [
                    'gap_count' => $dedupedRows->count(),
                    'zero_result_count' => $zeroResultSearches->count(),
                    'no_click_count' => $searchesWithResultsNoClicks->count(),
                    'low_ctr_count' => $lowCtrSearches->count(),
                    'high_ctr_count' => $highCtrSearches->count(),
                    'repeated_member_count' => $repeatedQueries->count(),
                ],
                'rows' => $dedupedRows->all(),
                'zero_result_searches' => $zeroResultSearches->all(),
                'searches_with_results_no_clicks' => $searchesWithResultsNoClicks->all(),
                'low_ctr_searches' => $lowCtrSearches->all(),
                'high_ctr_searches' => $highCtrSearches->all(),
                'repeated_queries' => $repeatedQueries->all(),
            ];
        });
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    public function getPromptInsights(array $filters = []): array
    {
        return $this->remember('prompt-insights', $filters, function (Carbon $from, Carbon $to, int $limit): array {
            $rows = $this->contentMetrics($from, $to, AcademyAnalyticsContentType::PROMPT)->map(function (array $row): ?array {
                $signal = $this->classifyPromptInsight($row);

                if ($signal === null) {
                    return null;
                }

                return array_merge($row, $signal);
            })->filter()->sortByDesc('priority_score')->take($limit)->values();

            return [
                'summary' => [
                    'signal_count' => $rows->count(),
                    'high_view_low_copy' => $rows->where('issue', 'High views, low copies')->count(),
                    'low_view_high_copy_rate' => $rows->where('issue', 'Low views, high copy rate')->count(),
                    'high_save_low_copy' => $rows->where('issue', 'High saves, low copies')->count(),
                    'high_copy_low_like' => $rows->where('issue', 'High copies, low likes')->count(),
                    'high_upgrade_interest' => $rows->where('issue', 'High upgrade interest')->count(),
                ],
                'rows' => $rows->all(),
            ];
        });
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    public function getLessonDropoffs(array $filters = []): array
    {
        return $this->remember('lesson-dropoffs', $filters, function (Carbon $from, Carbon $to, int $limit): array {
            $rows = $this->contentMetrics($from, $to, AcademyAnalyticsContentType::LESSON)->map(function (array $row): ?array {
                $signal = $this->classifyLessonDropoff($row);

                if ($signal === null) {
                    return null;
                }

                return array_merge($row, $signal);
            })->filter()->sortByDesc('priority_score')->take($limit)->values();

            return [
                'summary' => [
                    'signal_count' => $rows->count(),
                    'low_start_rate' => $rows->where('issue', 'High views, low starts')->count(),
                    'low_completion_rate' => $rows->where('issue', 'High starts, low completions')->count(),
                    'underpromoted_winners' => $rows->where('issue', 'High completions, low views')->count(),
                    'upgrade_interest' => $rows->where('issue', 'Upgrade interest')->count(),
                ],
                'rows' => $rows->all(),
            ];
        });
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    public function getCourseHealth(array $filters = []): array
    {
        return $this->remember('course-health', $filters, function (Carbon $from, Carbon $to, int $limit): array {
            $progress = AcademyUserProgress::query()
                ->selectRaw('course_id, avg(progress_percent) as avg_progress_percent, count(*) as learners')
                ->whereNotNull('course_id')
                ->whereNull('lesson_id')
                ->whereBetween('updated_at', [$from, $to])
                ->groupBy('course_id')
                ->get()
                ->keyBy(fn ($row): int => (int) $row->course_id);

            $rows = $this->contentMetrics($from, $to, AcademyAnalyticsContentType::COURSE)->map(function (array $row) use ($progress): ?array {
                $courseProgress = $progress->get((int) $row['content_id']);
                $row['avg_progress'] = $courseProgress ? round((float) ($courseProgress->avg_progress_percent ?? 0), 1) : 0.0;
                $row['learners'] = $courseProgress ? (int) ($courseProgress->learners ?? 0) : 0;
                $signal = $this->classifyCourseHealth($row);

                if ($signal === null) {
                    return null;
                }

                return array_merge($row, $signal);
            })->filter()->sortByDesc('priority_score')->take($limit)->values();

            return [
                'summary' => [
                    'signal_count' => $rows->count(),
                    'low_start_rate' => $rows->where('issue', 'Low course start rate')->count(),
                    'low_completion_rate' => $rows->where('issue', 'Low course completion rate')->count(),
                    'expandable_courses' => $rows->where('issue', 'Expansion candidate')->count(),
                    'upgrade_interest' => $rows->where('issue', 'Premium follow-up opportunity')->count(),
                ],
                'rows' => $rows->all(),
            ];
        });
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    public function getPremiumInterest(array $filters = []): array
    {
        return $this->remember('premium-interest', $filters, function (Carbon $from, Carbon $to, int $limit): array {
            $rows = collect([
                ...$this->contentMetrics($from, $to, AcademyAnalyticsContentType::PROMPT)->all(),
                ...$this->contentMetrics($from, $to, AcademyAnalyticsContentType::LESSON)->all(),
                ...$this->contentMetrics($from, $to, AcademyAnalyticsContentType::COURSE)->all(),
                ...$this->contentMetrics($from, $to, AcademyAnalyticsContentType::PROMPT_PACK)->all(),
                ...$this->contentMetrics($from, $to, AcademyAnalyticsContentType::CHALLENGE)->all(),
            ])->map(function (array $row): ?array {
                $signal = $this->classifyPremiumInterest($row);

                if ($signal === null) {
                    return null;
                }

                return array_merge($row, $signal, [
                    'premium_interest_score' => round(((float) $row['premium_preview_views'] * 2) + ((float) $row['upgrade_clicks'] * 10), 1),
                ]);
            })->filter()->sortByDesc('priority_score')->sortByDesc('premium_interest_score')->take($limit)->values();

            return [
                'summary' => [
                    'signal_count' => $rows->count(),
                    'strong_candidates' => $rows->where('issue', 'Strong premium candidate')->count(),
                    'weak_teasers' => $rows->where('issue', 'Weak premium teaser')->count(),
                ],
                'rows' => $rows->all(),
            ];
        });
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    public function getEditorialRecommendations(array $filters = []): array
    {
        return $this->remember('editorial-recommendations', $filters, function (Carbon $from, Carbon $to, int $limit): array {
            $searchGaps = $this->getSearchGaps(['from' => $from, 'to' => $to, 'limit' => $limit]);
            $promptInsights = $this->getPromptInsights(['from' => $from, 'to' => $to, 'limit' => $limit]);
            $lessonDropoffs = $this->getLessonDropoffs(['from' => $from, 'to' => $to, 'limit' => $limit]);
            $courseHealth = $this->getCourseHealth(['from' => $from, 'to' => $to, 'limit' => $limit]);
            $premiumInterest = $this->getPremiumInterest(['from' => $from, 'to' => $to, 'limit' => $limit]);

            $recommendations = collect();

            foreach (array_slice($searchGaps['zero_result_searches'], 0, 5) as $row) {
                $recommendations->push([
                    'title' => sprintf('Create content for "%s"', $row['query']),
                    'description' => sprintf('Users searched for "%s" %d times and saw %.1f results.', $row['query'], $row['searches'], $row['results_count']),
                    'reason' => 'Repeated zero-result searches indicate missing Academy content coverage.',
                    'priority' => $row['searches'] >= 3 ? 'high' : 'medium',
                    'priority_score' => $row['searches'] >= 3 ? 300 + $row['searches'] : 200 + $row['searches'],
                    'content_type' => null,
                    'content_id' => null,
                    'metric_snapshot' => [
                        'searches' => $row['searches'],
                        'results_count' => $row['results_count'],
                        'clicks' => $row['clicks'],
                    ],
                    'suggested_action' => 'Create content for this topic',
                ]);
            }

            foreach (array_slice($promptInsights['rows'], 0, 4) as $row) {
                $recommendations->push([
                    'title' => sprintf('Review prompt "%s"', $row['title']),
                    'description' => sprintf('%s with %d views, %d copies, and a %.1f%% copy rate.', $row['issue'], $row['views'], $row['prompt_copies'], $row['copy_rate']),
                    'reason' => 'Prompt performance suggests either discoverability or quality improvements are needed.',
                    'priority' => $row['priority'],
                    'priority_score' => 180 + (int) $row['priority_score'],
                    'content_type' => $row['content_type'],
                    'content_id' => $row['content_id'],
                    'metric_snapshot' => [
                        'views' => $row['views'],
                        'copies' => $row['prompt_copies'],
                        'copy_rate' => $row['copy_rate'],
                        'upgrade_clicks' => $row['upgrade_clicks'],
                    ],
                    'suggested_action' => $row['suggested_action'],
                ]);
            }

            foreach (array_slice($lessonDropoffs['rows'], 0, 4) as $row) {
                $recommendations->push([
                    'title' => sprintf('Improve lesson "%s"', $row['title']),
                    'description' => sprintf('%s with %d starts and a %.1f%% completion rate.', $row['issue'], $row['starts'], $row['completion_rate']),
                    'reason' => 'Lesson funnel data shows where learners hesitate or drop off.',
                    'priority' => $row['priority'],
                    'priority_score' => 170 + (int) $row['priority_score'],
                    'content_type' => $row['content_type'],
                    'content_id' => $row['content_id'],
                    'metric_snapshot' => [
                        'views' => $row['views'],
                        'starts' => $row['starts'],
                        'completions' => $row['completions'],
                        'completion_rate' => $row['completion_rate'],
                    ],
                    'suggested_action' => $row['suggested_action'],
                ]);
            }

            foreach (array_slice($courseHealth['rows'], 0, 4) as $row) {
                $recommendations->push([
                    'title' => sprintf('Review course "%s"', $row['title']),
                    'description' => sprintf('%s with a %.1f%% completion rate and %.1f%% average progress.', $row['issue'], $row['completion_rate'], $row['avg_progress']),
                    'reason' => 'Course progression data highlights where sequencing or positioning may be blocking learners.',
                    'priority' => $row['priority'],
                    'priority_score' => 160 + (int) $row['priority_score'],
                    'content_type' => $row['content_type'],
                    'content_id' => $row['content_id'],
                    'metric_snapshot' => [
                        'views' => $row['views'],
                        'starts' => $row['starts'],
                        'completions' => $row['completions'],
                        'avg_progress' => $row['avg_progress'],
                    ],
                    'suggested_action' => $row['suggested_action'],
                ]);
            }

            foreach (array_slice($premiumInterest['rows'], 0, 4) as $row) {
                $recommendations->push([
                    'title' => sprintf('Use "%s" as a premium signal', $row['title']),
                    'description' => sprintf('%s with %d preview views and %d upgrade clicks.', $row['issue'], $row['premium_preview_views'], $row['upgrade_clicks']),
                    'reason' => 'Premium preview behavior shows which topics can sell subscriptions or need better teaser copy.',
                    'priority' => $row['priority'],
                    'priority_score' => 150 + (int) $row['priority_score'],
                    'content_type' => $row['content_type'],
                    'content_id' => $row['content_id'],
                    'metric_snapshot' => [
                        'premium_preview_views' => $row['premium_preview_views'],
                        'upgrade_clicks' => $row['upgrade_clicks'],
                        'upgrade_rate' => $row['upgrade_rate'],
                    ],
                    'suggested_action' => $row['suggested_action'],
                ]);
            }

            $rows = $recommendations
                ->sortByDesc('priority_score')
                ->take($limit)
                ->values()
                ->map(function (array $row): array {
                    unset($row['priority_score']);

                    return $row;
                });

            return [
                'summary' => [
                    'total' => $rows->count(),
                    'high_priority' => $rows->where('priority', 'high')->count(),
                    'medium_priority' => $rows->where('priority', 'medium')->count(),
                    'low_priority' => $rows->where('priority', 'low')->count(),
                ],
                'rows' => $rows->all(),
            ];
        });
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array{0: Carbon, 1: Carbon, 2: int}
     */
    private function resolveFilters(array $filters): array
    {
        $from = ($filters['from'] ?? null) instanceof Carbon
            ? $filters['from']->copy()->startOfDay()
            : Carbon::parse((string) ($filters['from'] ?? now()->subDays(29)->toDateString()))->startOfDay();
        $to = ($filters['to'] ?? null) instanceof Carbon
            ? $filters['to']->copy()->endOfDay()
            : Carbon::parse((string) ($filters['to'] ?? now()->toDateString()))->endOfDay();
        $limit = max(1, min(50, (int) ($filters['limit'] ?? 25)));

        return [$from, $to, $limit];
    }

    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    private function remember(string $suffix, array $filters, callable $callback): array
    {
        [$from, $to, $limit] = $this->resolveFilters($filters);

        return Cache::remember(
            sprintf('academy_analytics_%s:%s:%s:%d', $suffix, $from->toDateString(), $to->toDateString(), $limit),
            now()->addMinutes(10),
            fn (): array => $callback($from, $to, $limit),
        );
    }

    private function searchQuery(Carbon $from, Carbon $to): Builder
    {
        return AcademySearchLog::query()
            ->whereBetween('created_at', [$from, $to])
            ->selectRaw('normalized_query, max(query) as query, count(*) as searches, avg(results_count) as avg_results_count, sum(case when clicked_content_id is not null then 1 else 0 end) as clicks, max(created_at) as last_searched_at, sum(case when is_logged_in = 1 then 1 else 0 end) as logged_in_searches, sum(case when is_subscriber = 1 then 1 else 0 end) as subscriber_searches')
            ->whereNotNull('normalized_query')
            ->groupBy('normalized_query');
    }

    /**
     * @return Collection<int, array<string, int|float|string|null>>
     */
    private function contentMetrics(Carbon $from, Carbon $to, string $contentType): Collection
    {
        return AcademyContentMetricDaily::query()
            ->whereBetween('date', [$from->toDateString(), $to->toDateString()])
            ->where('content_type', $contentType)
            ->selectRaw('content_type, content_id, sum(views) as views, sum(unique_visitors) as unique_visitors, sum(engaged_views) as engaged_views, sum(likes) as likes, sum(saves) as saves, sum(prompt_copies) as prompt_copies, sum(negative_prompt_copies) as negative_prompt_copies, sum(starts) as starts, sum(completions) as completions, sum(upgrade_clicks) as upgrade_clicks, sum(premium_preview_views) as premium_preview_views, sum(search_clicks) as search_clicks, sum(popularity_score) as popularity_score')
            ->groupBy('content_type', 'content_id')
            ->get()
            ->map(function ($row) use ($contentType): array {
                $contentId = (int) $row->content_id;
                $uniqueVisitors = max(0, (int) ($row->unique_visitors ?? 0));
                $promptCopies = max(0, (int) ($row->prompt_copies ?? 0));
                $likes = max(0, (int) ($row->likes ?? 0));
                $saves = max(0, (int) ($row->saves ?? 0));
                $starts = max(0, (int) ($row->starts ?? 0));
                $completions = max(0, (int) ($row->completions ?? 0));
                $searchClicks = max(0, (int) ($row->search_clicks ?? 0));
                $premiumPreviewViews = max(0, (int) ($row->premium_preview_views ?? 0));
                $upgradeClicks = max(0, (int) ($row->upgrade_clicks ?? 0));

                return [
                    'content_type' => $contentType,
                    'content_id' => $contentId,
                    'content_type_label' => (string) Str::of(str_replace('academy_', '', $contentType))->replace('_', ' ')->headline(),
                    'title' => $this->resolver->title($contentType, $contentId),
                    'views' => max(0, (int) ($row->views ?? 0)),
                    'unique_visitors' => $uniqueVisitors,
                    'engaged_views' => max(0, (int) ($row->engaged_views ?? 0)),
                    'likes' => $likes,
                    'saves' => $saves,
                    'prompt_copies' => $promptCopies,
                    'negative_prompt_copies' => max(0, (int) ($row->negative_prompt_copies ?? 0)),
                    'starts' => $starts,
                    'completions' => $completions,
                    'search_clicks' => $searchClicks,
                    'premium_preview_views' => $premiumPreviewViews,
                    'upgrade_clicks' => $upgradeClicks,
                    'popularity_score' => round((float) ($row->popularity_score ?? 0), 2),
                    'copy_rate' => $uniqueVisitors > 0 ? round(($promptCopies / $uniqueVisitors) * 100, 1) : 0.0,
                    'save_rate' => $uniqueVisitors > 0 ? round(($saves / $uniqueVisitors) * 100, 1) : 0.0,
                    'like_rate' => $uniqueVisitors > 0 ? round(($likes / $uniqueVisitors) * 100, 1) : 0.0,
                    'search_click_rate' => $uniqueVisitors > 0 ? round(($searchClicks / $uniqueVisitors) * 100, 1) : 0.0,
                    'start_rate' => $uniqueVisitors > 0 ? round(($starts / $uniqueVisitors) * 100, 1) : 0.0,
                    'completion_rate' => $starts > 0 ? round(($completions / $starts) * 100, 1) : 0.0,
                    'engagement_rate' => $uniqueVisitors > 0 ? round((((int) ($row->engaged_views ?? 0)) / $uniqueVisitors) * 100, 1) : 0.0,
                    'upgrade_rate' => $premiumPreviewViews > 0 ? round(($upgradeClicks / $premiumPreviewViews) * 100, 1) : 0.0,
                ];
            });
    }

    /**
     * @return array{issue: string|null, priority: string, priority_score: int, suggested_action: string}
     */
    private function classifySearchGap(int $searches, float $resultsCount, int $clicks, float $ctr, int $loggedInSearches, int $subscriberSearches): array
    {
        if ($resultsCount <= 0.4) {
            return [
                'issue' => 'Zero-result demand',
                'priority' => $searches >= 3 || $subscriberSearches >= 2 ? 'high' : 'medium',
                'priority_score' => 300 + $searches,
                'suggested_action' => 'Create content for this topic',
            ];
        }

        if ($resultsCount > 0 && $clicks === 0) {
            return [
                'issue' => 'Results with no clicks',
                'priority' => $searches >= 3 || $loggedInSearches >= 2 ? 'high' : 'medium',
                'priority_score' => 240 + $searches,
                'suggested_action' => 'Improve titles, excerpts, thumbnails, or relevance',
            ];
        }

        if ($searches >= 2 && $ctr < 10) {
            return [
                'issue' => 'Low click-through rate',
                'priority' => 'medium',
                'priority_score' => 180 + $searches,
                'suggested_action' => 'Improve matching content or create better content',
            ];
        }

        if ($searches >= 2 && $ctr >= 40) {
            return [
                'issue' => 'High click-through topic',
                'priority' => 'medium',
                'priority_score' => 140 + $searches,
                'suggested_action' => 'Consider expanding this topic',
            ];
        }

        return [
            'issue' => null,
            'priority' => 'low',
            'priority_score' => 0,
            'suggested_action' => 'Monitor search intent',
        ];
    }

    /**
     * @param  array<string, int|float|string|null>  $row
     * @return array<string, int|string>|null
     */
    private function classifyPromptInsight(array $row): ?array
    {
        if ((int) $row['upgrade_clicks'] >= 3 || ((float) $row['upgrade_rate'] >= 15 && (int) $row['premium_preview_views'] >= 5)) {
            return [
                'issue' => 'High upgrade interest',
                'priority' => 'high',
                'priority_score' => 300 + (int) $row['upgrade_clicks'],
                'suggested_action' => 'Create premium pack or advanced lesson around this topic',
            ];
        }

        if ((int) $row['views'] >= 120 && (float) $row['copy_rate'] < 8) {
            return [
                'issue' => 'High views, low copies',
                'priority' => 'medium',
                'priority_score' => 230 + (int) $row['views'],
                'suggested_action' => 'Improve prompt quality, preview image, title, or negative prompt',
            ];
        }

        if ((int) $row['views'] <= 30 && (int) $row['prompt_copies'] >= 3 && (float) $row['copy_rate'] >= 35) {
            return [
                'issue' => 'Low views, high copy rate',
                'priority' => 'medium',
                'priority_score' => 210 + (int) $row['prompt_copies'],
                'suggested_action' => 'Feature this prompt, improve SEO, add to related content',
            ];
        }

        if ((int) $row['saves'] >= 5 && (int) $row['prompt_copies'] < (int) $row['saves']) {
            return [
                'issue' => 'High saves, low copies',
                'priority' => 'medium',
                'priority_score' => 190 + (int) $row['saves'],
                'suggested_action' => 'Add examples, variations, or usage notes',
            ];
        }

        if ((int) $row['prompt_copies'] >= 8 && (float) $row['like_rate'] < 5) {
            return [
                'issue' => 'High copies, low likes',
                'priority' => 'low',
                'priority_score' => 160 + (int) $row['prompt_copies'],
                'suggested_action' => 'Improve like/save UI visibility or ask for feedback',
            ];
        }

        return null;
    }

    /**
     * @param  array<string, int|float|string|null>  $row
     * @return array<string, int|string>|null
     */
    private function classifyLessonDropoff(array $row): ?array
    {
        if ((int) $row['starts'] >= 12 && (float) $row['completion_rate'] < 35) {
            return [
                'issue' => 'High starts, low completions',
                'priority' => 'high',
                'priority_score' => 300 + (int) $row['starts'],
                'suggested_action' => 'Lesson may be too long, confusing, or missing examples',
            ];
        }

        if ((int) $row['views'] >= 80 && (float) $row['start_rate'] < 18) {
            return [
                'issue' => 'High views, low starts',
                'priority' => 'medium',
                'priority_score' => 230 + (int) $row['views'],
                'suggested_action' => 'Improve lesson intro, title, excerpt, or call-to-action',
            ];
        }

        if ((int) $row['completions'] >= 8 && (int) $row['views'] <= 35) {
            return [
                'issue' => 'High completions, low views',
                'priority' => 'medium',
                'priority_score' => 200 + (int) $row['completions'],
                'suggested_action' => 'Promote this lesson more',
            ];
        }

        if ((int) $row['upgrade_clicks'] >= 3 || ((float) $row['upgrade_rate'] >= 12 && (int) $row['premium_preview_views'] >= 5)) {
            return [
                'issue' => 'Upgrade interest',
                'priority' => 'medium',
                'priority_score' => 180 + (int) $row['upgrade_clicks'],
                'suggested_action' => 'This lesson may be useful as a subscription conversion entry point',
            ];
        }

        return null;
    }

    /**
     * @param  array<string, int|float|string|null>  $row
     * @return array<string, int|string>|null
     */
    private function classifyCourseHealth(array $row): ?array
    {
        if ((int) $row['starts'] >= 10 && (float) $row['completion_rate'] < 35) {
            return [
                'issue' => 'Low course completion rate',
                'priority' => 'high',
                'priority_score' => 300 + (int) $row['starts'],
                'suggested_action' => 'Add shorter lessons, move the strongest lesson earlier, or improve examples',
            ];
        }

        if ((int) $row['views'] >= 60 && (float) $row['start_rate'] < 18) {
            return [
                'issue' => 'Low course start rate',
                'priority' => 'medium',
                'priority_score' => 220 + (int) $row['views'],
                'suggested_action' => 'Improve course landing page, cover image, or course positioning',
            ];
        }

        if ((int) $row['upgrade_clicks'] >= 3) {
            return [
                'issue' => 'Premium follow-up opportunity',
                'priority' => 'medium',
                'priority_score' => 190 + (int) $row['upgrade_clicks'],
                'suggested_action' => 'Add a premium follow-up course around this topic',
            ];
        }

        if ((int) $row['completions'] >= 8 && (float) $row['completion_rate'] >= 65) {
            return [
                'issue' => 'Expansion candidate',
                'priority' => 'medium',
                'priority_score' => 170 + (int) $row['completions'],
                'suggested_action' => 'Expand this course with advanced follow-up material',
            ];
        }

        return null;
    }

    /**
     * @param  array<string, int|float|string|null>  $row
     * @return array<string, int|string>|null
     */
    private function classifyPremiumInterest(array $row): ?array
    {
        if ((int) $row['upgrade_clicks'] >= 3) {
            return [
                'issue' => 'Strong premium candidate',
                'priority' => 'high',
                'priority_score' => 300 + (int) $row['upgrade_clicks'],
                'suggested_action' => 'Create advanced premium content around this topic',
            ];
        }

        if ((int) $row['premium_preview_views'] >= 15 && (int) $row['upgrade_clicks'] <= 1) {
            return [
                'issue' => 'Weak premium teaser',
                'priority' => 'medium',
                'priority_score' => 190 + (int) $row['premium_preview_views'],
                'suggested_action' => 'Improve teaser copy, preview images, or value proposition',
            ];
        }

        return null;
    }

    /**
     * @param  Collection<int, array<string, mixed>>  $rows
     * @return Collection<int, array<string, mixed>>
     */
    private function dedupeByKey(Collection $rows, string $key): Collection
    {
        $seen = [];

        return $rows->filter(function (array $row) use (&$seen, $key): bool {
            $value = (string) ($row[$key] ?? '');

            if ($value === '' || isset($seen[$value])) {
                return false;
            }

            $seen[$value] = true;

            return true;
        });
    }
}
