<?php

declare(strict_types=1);

namespace App\Http\Controllers\Academy;

use App\Http\Controllers\Controller;
use App\Models\AcademyPromptTemplate;
use App\Services\Academy\AcademyAccessService;
use App\Services\Academy\AcademyAnalyticsService;
use App\Services\Academy\AcademyCacheService;
use App\Services\Academy\AcademyInteractionService;
use App\Services\Academy\AcademyPopularityService;
use App\Support\AcademyAnalytics\AcademyAnalyticsContentType;
use App\Support\Seo\SeoFactory;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Inertia\Response;

final class AcademyPromptController extends Controller
{
    public function __construct(
        private readonly AcademyAccessService $access,
        private readonly AcademyCacheService $cache,
        private readonly AcademyAnalyticsService $analytics,
        private readonly AcademyInteractionService $interactions,
        private readonly AcademyPopularityService $popularity,
    ) {
    }

    public function index(Request $request): Response|JsonResponse
    {
        abort_unless((bool) config('academy.enabled', true), 404);

        $filters = $request->validate([
            'q' => ['nullable', 'string', 'max:120'],
            'category' => ['nullable', 'string', 'max:140'],
            'difficulty' => ['nullable', 'string', 'max:40'],
            'tag' => ['nullable', 'string', 'max:60'],
        ]);

        $query = AcademyPromptTemplate::query()
            ->with('category')
            ->active()
            ->published()
            ->latest('published_at');

        if (filled($filters['q'] ?? null)) {
            $query->where(function ($builder) use ($filters): void {
                $builder->where('title', 'like', '%' . $filters['q'] . '%')
                    ->orWhere('excerpt', 'like', '%' . $filters['q'] . '%');
            });
        }

        if (filled($filters['category'] ?? null)) {
            $query->whereHas('category', fn ($builder) => $builder->where('slug', $filters['category']));
        }

        if (filled($filters['difficulty'] ?? null)) {
            $query->where('difficulty', $filters['difficulty']);
        }

        if (filled($filters['tag'] ?? null)) {
            $tag = $filters['tag'];
            $query->whereJsonContains('tags', $tag);
        }

        $prompts = $query->paginate(12)->withQueryString();
        $prompts->getCollection()->transform(fn (AcademyPromptTemplate $prompt): array => $this->access->promptPayload($prompt, $request->user()));

        if (filled($filters['q'] ?? null)) {
            $this->analytics->trackSearch((string) $filters['q'], (int) $prompts->total(), array_filter($filters), $request);
        }

        if ($request->expectsJson()) {
            return response()->json($prompts);
        }

        $seo = app(SeoFactory::class)
            ->collectionListing(
                'Academy Prompts — Skinbase',
                'Browse AI prompt templates for wallpapers, worlds, editorial covers, robots, pixel art, and creator workflows.',
                route('academy.prompts.index', $request->query()),
            )
            ->toArray();

        return Inertia::render('Academy/List', [
            'pageType' => 'prompts',
            'promptView' => 'library',
            'title' => 'Prompt library',
            'description' => 'Reusable prompt templates for wallpapers, worlds, mascots, covers, and digital art workflows.',
            'seo' => $seo,
            'breadcrumbs' => [
                ['label' => 'Academy', 'href' => route('academy.index')],
                ['label' => 'Prompt Library', 'href' => route('academy.prompts.index')],
            ],
            'items' => $prompts,
            'filters' => $filters,
            'categories' => $this->cache->categoriesByType('prompt'),
            'pricingUrl' => route('academy.pricing'),
            'coursesUrl' => route('academy.courses.index'),
            'packsUrl' => route('academy.packs.index'),
            'promptPopularUrl' => route('academy.prompts.popular'),
            'promptLibraryUrl' => route('academy.prompts.index'),
            'academyAccess' => array_merge($this->access->accessSummary($request->user()), [
                'billingUrl' => $request->user() && (bool) config('academy_billing.enabled', false)
                    ? route('academy.billing.account')
                    : route('academy.pricing'),
            ]),
            'featuredPrompts' => $this->featuredPromptPayloads($request->user()),
            'popularPrompts' => $this->popularPromptPayloads($request->user()),
            'analytics' => [
                'enabled' => true,
                'contentType' => AcademyAnalyticsContentType::PROMPT_LIBRARY,
                'contentId' => null,
                'eventUrl' => route('academy.analytics.events.store'),
                'pageName' => 'academy_prompts_index',
                'search' => filled($filters['q'] ?? null) ? [
                    'query' => (string) $filters['q'],
                    'resultsCount' => (int) $prompts->total(),
                ] : null,
                'isPremium' => false,
                'isGuest' => $request->user() === null,
                'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(),
            ],
        ])->rootView('collections');
    }

    public function popular(Request $request): Response
    {
        abort_unless((bool) config('academy.enabled', true), 404);

        $validated = $request->validate([
            'period' => ['nullable', 'string', 'in:7d,30d,90d'],
        ]);

        $selectedPeriod = $this->selectedPopularPromptPeriod($validated['period'] ?? null);
        $from = now()->subDays($selectedPeriod['days'] - 1)->startOfDay();
        $to = now()->endOfDay();

        $rows = DB::query()
            ->fromSub(
                $this->popularity->queryBetween($from, $to)
                    ->where('content_type', AcademyAnalyticsContentType::PROMPT)
                    ->whereNotNull('content_id')
                    ->selectRaw('content_id, sum(views) as views, sum(prompt_copies) as prompt_copies, sum(popularity_score) as popularity_score')
                    ->groupBy('content_id'),
                'prompt_rankings'
            )
            ->orderByDesc('popularity_score')
            ->orderByDesc('prompt_copies')
            ->orderByDesc('views')
            ->paginate(12)
            ->withQueryString();

        $prompts = AcademyPromptTemplate::query()
            ->with('category')
            ->active()
            ->published()
            ->whereIn('id', $rows->pluck('content_id')->map(static fn ($value): int => (int) $value)->all())
            ->get()
            ->keyBy('id');

        $baseRank = (($rows->currentPage() - 1) * $rows->perPage());

        $rows->setCollection(
            $rows->getCollection()
                ->values()
                ->map(function (object $row, int $index) use ($prompts, $request, $baseRank, $selectedPeriod): ?array {
                    $prompt = $prompts->get((int) $row->content_id);

                    if (! $prompt instanceof AcademyPromptTemplate) {
                        return null;
                    }

                    $payload = $this->access->promptPayload($prompt, $request->user());
                    $payload['ranking'] = [
                        'rank' => $baseRank + $index + 1,
                        'views' => max(0, (int) ($row->views ?? 0)),
                        'prompt_copies' => max(0, (int) ($row->prompt_copies ?? 0)),
                        'popularity_score' => round((float) ($row->popularity_score ?? 0), 2),
                    ];
                    $payload['spotlight'] = [
                        'eyebrow' => max(0, (int) ($row->prompt_copies ?? 0)) > 0
                            ? sprintf('%d copies %s', (int) $row->prompt_copies, $selectedPeriod['eyebrow_suffix'])
                            : sprintf('%d views %s', (int) $row->views, $selectedPeriod['eyebrow_suffix']),
                    ];

                    return $payload;
                })
                ->filter()
                ->values()
        );

        $seo = app(SeoFactory::class)
            ->collectionListing(
                sprintf('%s Prompts — Skinbase Academy', $selectedPeriod['title_prefix']),
                sprintf('See which Skinbase Academy prompt templates are driving the most views and copies %s.', $selectedPeriod['description_suffix']),
                route('academy.prompts.popular', $request->query()),
            )
            ->toArray();

        return Inertia::render('Academy/List', [
            'pageType' => 'prompts',
            'promptView' => 'popular',
            'title' => sprintf('%s prompts', $selectedPeriod['title_prefix']),
            'description' => sprintf('The prompt templates getting the most momentum from views and copies across the Academy %s.', $selectedPeriod['description_suffix']),
            'seo' => $seo,
            'breadcrumbs' => [
                ['label' => 'Academy', 'href' => route('academy.index')],
                ['label' => 'Prompt Library', 'href' => route('academy.prompts.index')],
                ['label' => 'Popular Prompts', 'href' => route('academy.prompts.popular')],
            ],
            'items' => $rows,
            'filters' => [],
            'categories' => [],
            'pricingUrl' => route('academy.pricing'),
            'coursesUrl' => route('academy.courses.index'),
            'packsUrl' => route('academy.packs.index'),
            'promptPopularUrl' => route('academy.prompts.popular'),
            'promptLibraryUrl' => route('academy.prompts.index'),
            'academyAccess' => array_merge($this->access->accessSummary($request->user()), [
                'billingUrl' => $request->user() && (bool) config('academy_billing.enabled', false)
                    ? route('academy.billing.account')
                    : route('academy.pricing'),
            ]),
            'popularPeriod' => [
                'value' => $selectedPeriod['value'],
                'label' => $selectedPeriod['label'],
                'description' => $selectedPeriod['description'],
            ],
            'popularPeriods' => collect($this->popularPromptPeriods())
                ->map(fn (array $period): array => [
                    'value' => $period['value'],
                    'label' => $period['label'],
                    'description' => $period['description'],
                    'href' => route('academy.prompts.popular', ['period' => $period['value']]),
                    'active' => $period['value'] === $selectedPeriod['value'],
                ])
                ->values()
                ->all(),
            'featuredPrompts' => $this->featuredPromptPayloads($request->user()),
            'popularPrompts' => [],
            'analytics' => [
                'enabled' => true,
                'contentType' => AcademyAnalyticsContentType::PROMPT_POPULAR,
                'contentId' => null,
                'eventUrl' => route('academy.analytics.events.store'),
                'pageName' => 'academy_prompts_popular',
                'trackingKey' => sprintf('period:%s', $selectedPeriod['value']),
                'metadata' => [
                    'period' => $selectedPeriod['value'],
                    'period_days' => $selectedPeriod['days'],
                ],
                'search' => null,
                'isPremium' => false,
                'isGuest' => $request->user() === null,
                'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(),
            ],
        ])->rootView('collections');
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function popularPromptPeriods(): array
    {
        return [
            [
                'value' => '7d',
                'days' => 7,
                'label' => '7 days',
                'description' => 'Fresh momentum from the last 7 days.',
                'title_prefix' => 'Top 7-day',
                'description_suffix' => 'in the last 7 days',
                'eyebrow_suffix' => 'in the last 7 days',
            ],
            [
                'value' => '30d',
                'days' => 30,
                'label' => '30 days',
                'description' => 'The default monthly view of prompt momentum.',
                'title_prefix' => 'Popular',
                'description_suffix' => 'this month',
                'eyebrow_suffix' => 'this month',
            ],
            [
                'value' => '90d',
                'days' => 90,
                'label' => '90 days',
                'description' => 'Longer-running prompt momentum across the quarter.',
                'title_prefix' => 'Top 90-day',
                'description_suffix' => 'in the last 90 days',
                'eyebrow_suffix' => 'in the last 90 days',
            ],
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function selectedPopularPromptPeriod(?string $value): array
    {
        return collect($this->popularPromptPeriods())
            ->first(fn (array $period): bool => $period['value'] === $value)
            ?? $this->popularPromptPeriods()[1];
    }

    public function show(Request $request, string $slug): Response
    {
        abort_unless((bool) config('academy.enabled', true), 404);

        $prompt = AcademyPromptTemplate::query()
            ->with('category')
            ->active()
            ->published()
            ->where('slug', $slug)
            ->firstOrFail();

        $payload = $this->access->promptPayload($prompt, $request->user(), true);
        $canonical = route('academy.prompts.show', ['slug' => $prompt->slug]);
        $description = Str::limit(trim((string) ($prompt->seo_description ?? $prompt->excerpt ?? 'Skinbase Academy prompt template.')), 160, '...');
        $seo = app(SeoFactory::class)->collectionPage(
            (string) ($prompt->seo_title ?? ($prompt->title . ' — Skinbase Academy')),
            $description,
            $canonical,
            $payload['preview_image'] ?? null,
        )->toArray();
        $existingSchemas = $seo['json_ld'] ?? [];
        if (! is_array($existingSchemas) || ! array_is_list($existingSchemas)) {
            $existingSchemas = [$existingSchemas];
        }
        $seo['json_ld'] = [
            ...$existingSchemas,
            $this->promptStructuredData($payload, $canonical, $description),
        ];

        $canSavePrompt = $request->user() !== null && $this->access->canAccessPrompt($request->user(), $prompt);
        $interaction = $this->interactions->getInteractionState($request->user(), AcademyAnalyticsContentType::PROMPT, (int) $prompt->id);

        return Inertia::render('Academy/Show', [
            'pageType' => 'prompt',
            'item' => $payload,
            'seo' => $seo,
            'pricingUrl' => route('academy.pricing'),
            'saveUrl' => $canSavePrompt ? route('academy.prompts.save', ['prompt' => $prompt->id]) : null,
            'unsaveUrl' => $canSavePrompt ? route('academy.prompts.unsave', ['prompt' => $prompt->id]) : null,
            'saved' => $canSavePrompt ? ($request->user()?->academySavedPrompts()->where('prompt_template_id', $prompt->id)->exists() ?? false) : false,
            'interaction' => $interaction,
            'interactionRoutes' => [
                'like' => route('academy.interactions.like'),
                'save' => route('academy.interactions.save'),
            ],
            'loginUrl' => route('login'),
            'analytics' => [
                'enabled' => true,
                'contentType' => AcademyAnalyticsContentType::PROMPT,
                'contentId' => (int) $prompt->id,
                'eventUrl' => route('academy.analytics.events.store'),
                'pageName' => 'academy_prompt_show',
                'isPremium' => (string) ($prompt->access_level ?? 'free') !== 'free',
                'isGuest' => $request->user() === null,
                'isSubscriber' => $request->user()?->hasAcademyCreatorAccess() || $request->user()?->hasAcademyProAccess(),
                'isLocked' => (bool) ($payload['locked'] ?? false),
            ],
        ])->rootView('collections');
    }

    /**
     * @param  array<string, mixed>  $payload
     * @return array<string, mixed>
     */
    private function promptStructuredData(array $payload, string $canonical, string $description): array
    {
        $imageUrls = array_values(array_unique(array_filter([
            $payload['preview_image'] ?? null,
            ...collect((array) ($payload['public_examples'] ?? []))
                ->map(fn (array $example): ?string => $example['image_url'] ?? $example['thumb_url'] ?? null)
                ->filter()
                ->values()
                ->all(),
        ], fn (mixed $value): bool => is_string($value) && $value !== '')));
        $isFree = (string) ($payload['access_level'] ?? 'free') === 'free';

        return array_filter([
            '@context' => 'https://schema.org',
            '@type' => ['CreativeWork', 'LearningResource'],
            'name' => (string) ($payload['title'] ?? 'Skinbase Academy prompt'),
            'description' => $description,
            'url' => $canonical,
            'image' => $imageUrls !== [] ? $imageUrls : null,
            'isAccessibleForFree' => $isFree,
            'hasPart' => $isFree ? null : [
                '@type' => 'WebPageElement',
                'isAccessibleForFree' => false,
                'cssSelector' => '.academy-paywalled-content',
            ],
        ], fn (mixed $value): bool => $value !== null && $value !== '' && $value !== []);
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function featuredPromptPayloads(mixed $viewer, int $limit = 4): array
    {
        return collect($this->cache->featuredPrompts())
            ->take($limit)
            ->map(function (AcademyPromptTemplate $prompt) use ($viewer): array {
                $payload = $this->access->promptPayload($prompt, $viewer);
                $payload['spotlight'] = [
                    'eyebrow' => $prompt->prompt_of_week ? 'Prompt of the week' : 'Featured pick',
                ];

                return $payload;
            })
            ->values()
            ->all();
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function popularPromptPayloads(mixed $viewer, int $limit = 4): array
    {
        $rows = $this->popularity->queryBetween(now()->subDays(29)->startOfDay(), now()->endOfDay())
            ->where('content_type', AcademyAnalyticsContentType::PROMPT)
            ->whereNotNull('content_id')
            ->selectRaw('content_id, sum(views) as views, sum(prompt_copies) as prompt_copies, sum(popularity_score) as popularity_score')
            ->groupBy('content_id')
            ->orderByDesc('popularity_score')
            ->limit($limit)
            ->get();

        if ($rows->isEmpty()) {
            return [];
        }

        $prompts = AcademyPromptTemplate::query()
            ->with('category')
            ->active()
            ->published()
            ->whereIn('id', $rows->pluck('content_id')->all())
            ->get()
            ->keyBy('id');

        return $rows->map(function ($row) use ($prompts, $viewer): ?array {
            $prompt = $prompts->get((int) $row->content_id);

            if (! $prompt instanceof AcademyPromptTemplate) {
                return null;
            }

            $payload = $this->access->promptPayload($prompt, $viewer);
            $copies = max(0, (int) ($row->prompt_copies ?? 0));
            $views = max(0, (int) ($row->views ?? 0));
            $payload['spotlight'] = [
                'eyebrow' => $copies > 0 ? sprintf('%d copies this month', $copies) : sprintf('%d views this month', $views),
            ];

            return $payload;
        })
            ->filter()
            ->values()
            ->all();
    }
}