<?php

namespace App\Jobs;

use App\Contracts\CatalogPageDataResolver;
use App\DTOs\CatalogPageData;
use App\Models\LandingPage;
use App\Models\LandingPageGeneration;
use App\Services\CatalogTemplateLocator;
use App\Services\CatalogTemplateRenderer;
use App\Support\CatalogThemeApplicator;
use App\Support\CatalogThemeRegistry;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Throwable;

class GenerateCatalogLandingPagesJob implements ShouldQueue
{
    use Queueable;

    public int $timeout = 2000;

    public function __construct(public int $generationId, public ?int $landingPageId = null) {}

    public function handle(
        CatalogPageDataResolver $catalogResolver,
        CatalogTemplateRenderer $renderer,
        CatalogTemplateLocator $locator,
    ): void {
        $generation = LandingPageGeneration::query()
            ->with('landingPages')
            ->findOrFail($this->generationId);

        if ($generation->status === 'completed' && $this->landingPageId === null) {
            return;
        }

        $generation->update(['status' => 'generating']);

        try {
            $stored = is_array($generation->catalog) ? $generation->catalog : [];
            $research = is_array($stored['research'] ?? null) ? $stored['research'] : [];
            $hints = [
                'advertiser_id' => isset($stored['advertiser_id']) ? (string) $stored['advertiser_id'] : null,
                'campaign_id' => isset($stored['campaign_id']) ? (string) $stored['campaign_id'] : null,
            ];

            $catalog = CatalogPageData::isComplete($stored)
                ? CatalogPageData::fromArray($stored)
                : $catalogResolver->resolve(
                    $generation->brand_name,
                    $generation->product_urls,
                    $generation->brand_url,
                    $generation->notes,
                    $hints['advertiser_id'] !== '' ? $hints['advertiser_id'] : null,
                    $hints['campaign_id'] !== '' ? $hints['campaign_id'] : null,
                );

            $catalog = $this->applyResearchPicks($catalog, $research);
            $saved = $catalog->toArray();

            if ($research !== []) {
                $saved['research'] = $research;
            }

            $generation->update([
                'status' => 'processing',
                'catalog' => $saved,
            ]);

            $pages = $generation->landingPages
                ->sortBy('id')
                ->values();

            if ($this->landingPageId !== null) {
                $pages = $pages
                    ->where('id', $this->landingPageId)
                    ->values();
            }

            foreach ($pages as $page) {
                $this->generatePage($page, $catalog, $renderer, $locator);
            }
        } catch (Throwable $e) {
            $failedPages = $generation->landingPages()->where('status', '!=', 'completed');

            if ($this->landingPageId !== null) {
                $failedPages->where('id', $this->landingPageId);
            }

            $failedPages->update([
                'status' => 'failed',
                'failure_reason' => $e->getMessage(),
            ]);

            $succeeded = $generation->landingPages()->where('status', 'completed')->count();

            $generation->update([
                'status' => $succeeded > 0 ? 'completed' : 'failed',
                'failure_reason' => $succeeded > 0 ? null : $e->getMessage(),
            ]);

            throw $e;
        }

        $succeeded = $generation->landingPages()->where('status', 'completed')->count();

        $generation->update([
            'status' => $succeeded > 0 ? 'completed' : 'failed',
            'failure_reason' => $succeeded > 0 ? null : 'All catalog landing page variations failed to generate.',
        ]);
    }

    /**
     * @param  array<string, mixed>  $research
     */
    private function applyResearchPicks(CatalogPageData $catalog, array $research): CatalogPageData
    {
        $data = $catalog->toArray();

        if (filled($research['category'] ?? null)) {
            $data['category'] = (string) $research['category'];
        }

        if (filled($research['subcategory'] ?? null)) {
            $data['subcategory'] = (string) $research['subcategory'];
        }

        $discoveredByUrl = [];

        foreach ($research['products'] ?? [] as $product) {
            if (! is_array($product)) {
                continue;
            }

            $url = trim((string) ($product['url'] ?? ''));

            if ($url !== '') {
                $discoveredByUrl[$url] = $product;
            }
        }

        if ($discoveredByUrl === []) {
            return CatalogPageData::fromArray($data);
        }

        $data['products'] = array_map(function (mixed $product) use ($discoveredByUrl): array {
            if (! is_array($product)) {
                return [];
            }

            $url = trim((string) ($product['retailer_url'] ?? $product['url'] ?? ''));
            $discovered = $discoveredByUrl[$url] ?? null;

            if (! is_array($discovered)) {
                return $product;
            }

            foreach (['image_url', 'price', 'list_price', 'rating', 'review_count'] as $field) {
                $current = trim((string) ($product[$field] ?? ''));

                if ($current !== '') {
                    continue;
                }

                $value = $discovered[$field] ?? null;

                if ($value === null || trim((string) $value) === '') {
                    continue;
                }

                $product[$field] = $value;
            }

            return $product;
        }, is_array($data['products'] ?? null) ? $data['products'] : []);

        return CatalogPageData::fromArray($data);
    }

    private function generatePage(
        LandingPage $page,
        CatalogPageData $catalog,
        CatalogTemplateRenderer $renderer,
        CatalogTemplateLocator $locator,
    ): void {
        if ($page->status === 'completed' && $this->landingPageId !== $page->id) {
            return;
        }

        try {
            $template = $locator->pickTemplate($page->version, $page->template);
            $generated = $renderer->render($page->version, $catalog, $template);
            $themeKey = trim((string) ($page->theme ?? ''));
            $bodyClass = $themeKey !== ''
                ? CatalogThemeRegistry::bodyClass($template, $themeKey)
                : '';

            $page->update([
                'status' => 'completed',
                'template' => $template,
                'title' => $generated->title,
                'html' => CatalogThemeApplicator::apply($generated->html, $bodyClass),
                'css' => $generated->css,
                'js' => $generated->js,
                'prompt_used' => $generated->prompt,
                'failure_reason' => null,
            ]);
        } catch (Throwable $e) {
            $page->refresh();
            $page->update([
                'status' => 'failed',
                'failure_reason' => $e->getMessage(),
            ]);
        }
    }
}
