<?php

namespace App\Livewire\CatalogLandingPages;

use App\Enums\LandingPageLayoutMode;
use App\Jobs\AnalyzeCatalogWebsiteJob;
use App\Jobs\DiscoverCatalogProductsJob;
use App\Jobs\DiscoverCatalogSubcategoriesJob;
use App\Jobs\GenerateCatalogLandingPagesJob;
use App\Models\LandingPageGeneration;
use App\Services\Catalog\CatalogProductImportValidator;
use App\Services\CatalogTemplateLocator;
use App\Support\CatalogThemeRegistry;
use Illuminate\Support\Collection;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Url;
use Livewire\Component;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
use Livewire\WithFileUploads;

#[Layout('layouts.mb', ['pageTitle' => 'New catalog page'])]
class ChatWizard extends Component
{
    use WithFileUploads;

    public string $step = 'brand';

    public string $brandName = '';

    public string $brandUrl = '';

    public string $researchSource = 'listing';

    public string $productSource = 'listing';

    public string $listingUrl = '';

    public string $productsJson = '';

    /** @var TemporaryUploadedFile|null */
    public $productsJsonFile = null;

    #[Locked]
    public string $confirmedBrandName = '';

    #[Url(as: 'generation', history: true)]
    public ?int $generationId = null;

    #[Locked]
    public ?string $category = null;

    #[Locked]
    public ?string $subcategory = null;

    /** @var list<string> */
    #[Locked]
    public array $selectedSubcategories = [];

    /** @var list<array{name: string, url: string}> */
    public array $categories = [];

    /** @var list<array{name: string, url: string}> */
    public array $subcategories = [];

    /** @var list<array{name: string, url: string, popularity_note?: string}> */
    public array $products = [];

    public ?string $emptyReason = null;

    public ?string $listingScrapeError = null;

    public ?string $failureReason = null;

    public function mount(): void
    {
        if ($this->generationId !== null) {
            $this->resumeGeneration();
        }
    }

    public function submitBrand(): void
    {
        $this->validate([
            'brandName' => $this->brandNameRules(),
        ]);

        $this->confirmedBrandName = $this->brandName;
        $this->step = 'products';
    }

    public function updatedBrandName(): void
    {
        $this->restoreConfirmedBrandName();
    }

    public function updatedBrandUrl(): void
    {
        $this->restoreConfirmedBrandName();
    }

    public function chooseProductSource(string $source): void
    {
        if (! in_array($source, ['listing', 'json'], true)) {
            return;
        }

        $this->productSource = $source;
        $this->researchSource = $source;
        $this->markManualProductInput($source);
    }

    public function chooseResearchSource(string $source): void
    {
        if ($source === 'listing') {
            $this->chooseProductSource('listing');

            return;
        }

        if ($source === 'json') {
            $this->chooseProductSource('json');
        }
    }

    public function updatedProductsJsonFile(): void
    {
        $this->validate([
            'productsJsonFile' => ['required', 'file', 'mimes:json,txt', 'max:5120'],
        ]);

        $this->productsJson = (string) file_get_contents($this->productsJsonFile->getRealPath());
        $this->productsJsonFile = null;
    }

    public function validateProductsJson(CatalogProductImportValidator $validator): void
    {
        $this->restoreConfirmedBrandName();

        $this->validate([
            'brandName' => $this->brandNameRules(),
            'productsJson' => ['required', 'string', 'max:500000'],
        ]);

        $result = $validator->validate($this->productsJson);

        if (! $result['valid']) {
            $this->addError('productsJson', implode(' ', $result['errors']));

            return;
        }

        $this->productsJson = $validator->encodeProducts($result['products']);
        $this->resetErrorBag('productsJson');
        $this->step = 'json_edit';
    }

    public function submitJsonProducts(CatalogProductImportValidator $validator): void
    {
        $this->restoreConfirmedBrandName();

        $this->validate([
            'brandName' => $this->brandNameRules(),
            'productsJson' => ['required', 'string', 'max:500000'],
        ]);

        try {
            $products = $validator->validatedProducts($this->productsJson);
        } catch (\InvalidArgumentException $exception) {
            $this->addError('productsJson', $exception->getMessage());

            return;
        }

        $brandUrl = $this->brandUrlFromProducts($products);
        $generation = $this->ownedGeneration();

        if ($generation === null) {
            $generation = auth()->user()->landingPageGenerations()->create([
                'brand_name' => $this->confirmedBrandName !== '' ? $this->confirmedBrandName : $this->brandName,
                'brand_url' => $brandUrl,
                'website_url' => LandingPageGeneration::defaultPartnerWebsiteUrl(),
                'product_urls' => [],
                'notes' => null,
                'layout_mode' => LandingPageLayoutMode::Catalog,
                'html_template' => null,
                'catalog' => null,
                'status' => 'discovering',
            ]);

            $this->generationId = $generation->id;
        } else {
            $generation->update([
                'brand_url' => $brandUrl,
                'product_urls' => [],
                'notes' => null,
                'status' => 'discovering',
                'failure_reason' => null,
            ]);
        }
        $this->productSource = 'json';
        $this->researchSource = 'json';
        $this->emptyReason = null;
        $this->failureReason = null;
        $this->products = [];
        $this->step = 'discovering';

        $generation->mergeCatalogResearch([
            'source' => 'json',
            'uploaded_products' => $products,
            'products' => [],
            'empty_reason' => null,
            'listing_scrape_error' => null,
            'manual_input' => false,
            'manual_input_source' => null,
        ]);

        DiscoverCatalogProductsJob::dispatch($generation->id);

        $this->syncFromGeneration();
    }

    public function submitListingProducts(): void
    {
        $this->restoreConfirmedBrandName();

        $this->validate([
            'brandName' => $this->brandNameRules(),
            'listingUrl' => $this->listingUrlRules(),
        ]);

        $generation = auth()->user()->landingPageGenerations()->create([
            'brand_name' => $this->confirmedBrandName !== '' ? $this->confirmedBrandName : $this->brandName,
            'brand_url' => $this->listingUrl,
            'website_url' => LandingPageGeneration::defaultPartnerWebsiteUrl(),
            'product_urls' => [],
            'notes' => null,
            'layout_mode' => LandingPageLayoutMode::Catalog,
            'html_template' => null,
            'catalog' => null,
            'status' => 'discovering',
        ]);

        $this->generationId = $generation->id;
        $this->productSource = 'listing';
        $this->researchSource = 'listing';
        $this->brandUrl = $this->listingUrl;
        $this->emptyReason = null;
        $this->failureReason = null;
        $this->products = [];
        $this->step = 'discovering';

        $generation->mergeCatalogResearch([
            'source' => 'listing',
            'listing_url' => $this->listingUrl,
            'products' => [],
            'empty_reason' => null,
            'manual_input' => false,
            'manual_input_source' => null,
        ]);

        DiscoverCatalogProductsJob::dispatch($generation->id);

        $this->syncFromGeneration();
    }

    public function submitProducts(): void
    {
        if ($this->productSource === 'json') {
            if ($this->step === 'json_edit') {
                $this->submitJsonProducts(app(CatalogProductImportValidator::class));

                return;
            }

            $this->validateProductsJson(app(CatalogProductImportValidator::class));

            return;
        }

        $this->submitListingProducts();
    }

    public function editUploadedJson(): void
    {
        $this->step = 'json_edit';
    }

    public function backToProducts(?string $source = null): void
    {
        if (in_array($source, ['listing', 'json'], true)) {
            $this->productSource = $source;
            $this->researchSource = $source;
        }

        $generation = $this->ownedGeneration();

        if ($generation !== null && $generation->status === 'discovering') {
            $generation->update(['status' => 'awaiting_products']);
        }

        $this->markManualProductInput($this->productSource);
        $this->emptyReason = null;
        $this->step = 'products';
        $this->resetErrorBag();
    }

    public function useJsonImportInstead(): void
    {
        $this->backToProducts('json');
    }

    public function submitWebsite(): void
    {
        $this->restoreConfirmedBrandName();

        $this->validate([
            'brandName' => $this->brandNameRules(),
            'brandUrl' => $this->brandUrlRules(),
            'researchSource' => ['required', 'in:website,listing'],
        ]);

        $isListing = $this->researchSource === 'listing';

        $generation = auth()->user()->landingPageGenerations()->create([
            'brand_name' => $this->confirmedBrandName !== '' ? $this->confirmedBrandName : $this->brandName,
            'brand_url' => $this->brandUrl,
            'website_url' => LandingPageGeneration::defaultPartnerWebsiteUrl(),
            'product_urls' => [],
            'notes' => null,
            'layout_mode' => LandingPageLayoutMode::Catalog,
            'html_template' => null,
            'catalog' => null,
            'status' => $isListing ? 'discovering' : 'researching',
        ]);

        $this->generationId = $generation->id;
        $this->emptyReason = null;
        $this->failureReason = null;

        if ($isListing) {
            $generation->mergeCatalogResearch([
                'source' => 'listing',
                'listing_url' => $this->brandUrl,
            ]);
            $this->step = 'discovering';
            DiscoverCatalogProductsJob::dispatch($generation->id);
            $this->syncFromGeneration();

            return;
        }

        $generation->mergeCatalogResearch([
            'source' => 'website',
        ]);
        $this->step = 'analyzing';
        AnalyzeCatalogWebsiteJob::dispatch($generation->id);

        $this->syncFromGeneration();
    }

    public function selectCategory(string $name): void
    {
        $chosen = collect($this->categories)->firstWhere('name', $name);

        if (! is_array($chosen) || $this->generationId === null) {
            return;
        }

        $generation = $this->ownedGeneration();

        if ($generation === null || ! in_array($generation->status, ['awaiting_category', 'awaiting_subcategory'], true)) {
            return;
        }

        $this->category = $name;
        $this->subcategory = null;
        $this->selectedSubcategories = [];
        $this->subcategories = [];
        $this->products = [];
        $this->emptyReason = null;
        $this->failureReason = null;
        $this->step = 'loading_subcategories';

        $generation->mergeCatalogResearch([
            'category' => $name,
            'category_url' => (string) ($chosen['url'] ?? ''),
            'subcategory' => null,
            'subcategory_url' => null,
            'selected_subcategories' => [],
            'subcategories' => [],
            'products' => [],
            'empty_reason' => null,
        ]);
        $generation->update(['status' => 'listing_subcategories', 'failure_reason' => null]);

        DiscoverCatalogSubcategoriesJob::dispatch($generation->id);

        $this->syncFromGeneration();
    }

    public function toggleSubcategory(string $name): void
    {
        $generation = $this->ownedGeneration();

        if ($generation === null || $generation->status !== 'awaiting_subcategory') {
            return;
        }

        if (! collect($this->subcategories)->contains(fn (array $item): bool => ($item['name'] ?? '') === $name)) {
            return;
        }

        if (in_array($name, $this->selectedSubcategories, true)) {
            $this->selectedSubcategories = [];

            return;
        }

        $this->selectedSubcategories = [$name];
    }

    public function selectSubcategory(string $name): void
    {
        $this->selectedSubcategories = [$name];
        $this->confirmSubcategories();
    }

    public function confirmSubcategories(): void
    {
        if ($this->generationId === null || $this->category === null || $this->selectedSubcategories === []) {
            return;
        }

        $generation = $this->ownedGeneration();

        if ($generation === null || $generation->status !== 'awaiting_subcategory') {
            return;
        }

        $chosen = [];

        foreach ($this->selectedSubcategories as $name) {
            $row = collect($this->subcategories)->firstWhere('name', $name);

            if (! is_array($row)) {
                continue;
            }

            $chosen[] = [
                'name' => $name,
                'url' => (string) ($row['url'] ?? ''),
            ];
        }

        if ($chosen === []) {
            return;
        }

        $label = implode(', ', array_column($chosen, 'name'));
        $this->subcategory = $label;
        $this->products = [];
        $this->emptyReason = null;
        $this->failureReason = null;
        $this->step = 'discovering';

        $generation->mergeCatalogResearch([
            'subcategory' => $label,
            'subcategory_url' => (string) ($chosen[0]['url'] ?? ''),
            'selected_subcategories' => $chosen,
            'products' => [],
            'empty_reason' => null,
        ]);
        $generation->update(['status' => 'discovering', 'failure_reason' => null]);

        DiscoverCatalogProductsJob::dispatch($generation->id);

        $this->syncFromGeneration();
    }

    public function submitListingFallback(): void
    {
        $generation = $this->ownedGeneration();

        if ($generation === null || $this->step !== 'empty') {
            return;
        }

        $research = $generation->catalogResearch();

        if (($research['source'] ?? 'website') === 'listing') {
            return;
        }

        if (! in_array($generation->status, ['awaiting_category', 'awaiting_subcategory'], true)) {
            return;
        }

        $this->validate([
            'listingUrl' => ['required', 'url', 'max:2000'],
        ]);

        $this->researchSource = 'listing';
        $this->emptyReason = null;
        $this->failureReason = null;
        $this->products = [];
        $this->step = 'discovering';

        $generation->update([
            'brand_url' => $this->listingUrl,
            'status' => 'discovering',
            'failure_reason' => null,
        ]);
        $generation->mergeCatalogResearch([
            'source' => 'listing',
            'listing_url' => $this->listingUrl,
            'products' => [],
            'empty_reason' => null,
        ]);

        DiscoverCatalogProductsJob::dispatch($generation->id);

        $this->syncFromGeneration();
    }

    public function selectTemplate(string $template, CatalogTemplateLocator $locator): void
    {
        $generation = $this->ownedGeneration();

        if ($generation === null || $generation->status !== 'awaiting_template') {
            return;
        }

        if (! in_array($template, $locator->sharedTemplates(), true)) {
            return;
        }

        $generation->mergeCatalogResearch(['template' => $template]);
        $generation->update([
            'status' => 'awaiting_theme',
            'failure_reason' => null,
        ]);

        $this->syncFromGeneration();
    }

    public function selectTheme(string $theme, CatalogTemplateLocator $locator): void
    {
        $generation = $this->ownedGeneration();

        if ($generation === null || $generation->status !== 'awaiting_theme') {
            return;
        }

        $template = trim((string) ($generation->catalogResearch()['template'] ?? ''));

        if ($template === '' || ! CatalogThemeRegistry::isValidForTemplate($template, $theme)) {
            return;
        }

        $slots = $locator->slots();

        if ($slots === []) {
            $generation->update([
                'status' => 'failed',
                'failure_reason' => 'Add at least one template folder (for example template-1 with index.phtml) under resources/landing-page-templates/{variation}.',
            ]);
            $this->syncFromGeneration();

            return;
        }

        if ($generation->landingPages()->exists()) {
            return;
        }

        $generation->mergeCatalogResearch(['theme' => $theme]);
        $generation->update([
            'status' => 'queued',
            'failure_reason' => null,
        ]);

        foreach ($slots as $slot) {
            $generation->landingPages()->create([
                'version' => $slot['version'],
                'template' => $template,
                'theme' => $theme,
                'status' => 'queued',
            ]);
        }

        GenerateCatalogLandingPagesJob::dispatch($generation->id);

        $this->syncFromGeneration();
    }

    public function themeLabel(string $template, string $themeKey): string
    {
        return CatalogThemeRegistry::label($template, $themeKey);
    }

    public function templateLabel(string $template): string
    {
        if (preg_match('/^template-(\d+)$/i', $template, $matches) === 1) {
            return 'Template '.$matches[1];
        }

        return $template;
    }

    public function retryAnalysis(): void
    {
        $generation = $this->ownedGeneration();

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

        $this->emptyReason = null;
        $this->failureReason = null;
        $this->category = null;
        $this->subcategory = null;
        $this->selectedSubcategories = [];
        $this->categories = [];
        $this->subcategories = [];
        $this->products = [];

        $research = $generation->catalogResearch();

        if (($research['source'] ?? 'website') === 'listing') {
            $generation->mergeCatalogResearch([
                'products' => [],
                'empty_reason' => null,
            ]);
            $generation->update([
                'status' => 'discovering',
                'failure_reason' => null,
            ]);
            $this->step = 'discovering';
            DiscoverCatalogProductsJob::dispatch($generation->id);
            $this->syncFromGeneration();

            return;
        }

        if (($research['source'] ?? 'website') === 'json') {
            $generation->mergeCatalogResearch([
                'products' => [],
                'empty_reason' => null,
            ]);
            $generation->update([
                'status' => 'discovering',
                'failure_reason' => null,
            ]);
            $this->step = 'discovering';
            DiscoverCatalogProductsJob::dispatch($generation->id);
            $this->syncFromGeneration();

            return;
        }

        $this->step = 'analyzing';

        $generation->update([
            'catalog' => null,
            'status' => 'researching',
            'failure_reason' => null,
        ]);

        AnalyzeCatalogWebsiteJob::dispatch($generation->id);

        $this->syncFromGeneration();
    }

    public function pickAnotherCategory(): void
    {
        $generation = $this->ownedGeneration();

        if ($generation === null || $this->categories === []) {
            return;
        }

        $this->category = null;
        $this->subcategory = null;
        $this->selectedSubcategories = [];
        $this->subcategories = [];
        $this->products = [];
        $this->emptyReason = null;
        $this->failureReason = null;
        $this->step = 'category';

        $generation->mergeCatalogResearch([
            'category' => null,
            'category_url' => null,
            'subcategory' => null,
            'subcategory_url' => null,
            'selected_subcategories' => [],
            'subcategories' => [],
            'products' => [],
            'empty_reason' => null,
        ]);
        $generation->update(['status' => 'awaiting_category', 'failure_reason' => null]);
    }

    public function pickAnotherSubcategory(): void
    {
        $generation = $this->ownedGeneration();

        if ($generation === null || $this->subcategories === []) {
            return;
        }

        $this->subcategory = null;
        $this->selectedSubcategories = [];
        $this->products = [];
        $this->emptyReason = null;
        $this->failureReason = null;
        $this->step = 'subcategory';

        $generation->mergeCatalogResearch([
            'subcategory' => null,
            'subcategory_url' => null,
            'selected_subcategories' => [],
            'products' => [],
            'empty_reason' => null,
        ]);
        $generation->update(['status' => 'awaiting_subcategory', 'failure_reason' => null]);
    }

    public function syncFromGeneration(): void
    {
        $generation = $this->ownedGeneration();

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

        $generation->refresh();
        $research = $generation->catalogResearch();

        $this->categories = $this->namedChoices($research['categories'] ?? null);
        $this->subcategories = $this->namedChoices($research['subcategories'] ?? null);
        $this->products = is_array($research['products'] ?? null) ? array_values($research['products']) : [];
        $this->emptyReason = filled($research['empty_reason'] ?? null) ? (string) $research['empty_reason'] : null;
        $this->listingScrapeError = filled($research['listing_scrape_error'] ?? null)
            ? (string) $research['listing_scrape_error']
            : null;
        $this->failureReason = $generation->failure_reason;
        $this->category = filled($research['category'] ?? null) ? (string) $research['category'] : $this->category;
        $this->subcategory = filled($research['subcategory'] ?? null) ? (string) $research['subcategory'] : $this->subcategory;
        $this->researchSource = match ($research['source'] ?? $this->researchSource) {
            'listing' => 'listing',
            'json' => 'json',
            default => 'website',
        };
        $this->productSource = in_array($this->researchSource, ['listing', 'json'], true)
            ? $this->researchSource
            : 'listing';

        if ($this->researchSource === 'listing') {
            $this->listingUrl = (string) ($research['listing_url'] ?? $generation->brand_url ?? '');
            $this->brandUrl = $this->listingUrl;
        }

        if ($this->researchSource === 'json' && is_array($research['uploaded_products'] ?? null)) {
            $this->productsJson = app(CatalogProductImportValidator::class)->encodeProducts(
                array_values($research['uploaded_products']),
            );
        }

        $storedSelection = $research['selected_subcategories'] ?? null;

        if (is_array($storedSelection) && $storedSelection !== []) {
            $this->selectedSubcategories = $this->selectedNames($storedSelection);
        }

        if (in_array($generation->status, ['queued', 'generating', 'processing', 'completed'], true)
            && $generation->landingPages()->exists()) {
            $this->redirect(route('catalog-landing-pages.show', $generation), navigate: true);

            return;
        }

        if ($generation->status === 'failed') {
            $this->step = 'failed';

            return;
        }

        if ($generation->status === 'researching') {
            $this->step = 'analyzing';

            return;
        }

        if ($generation->status === 'listing_subcategories') {
            $this->step = 'loading_subcategories';

            return;
        }

        if ($generation->status === 'discovering') {
            $this->step = 'discovering';

            return;
        }

        if ($generation->status === 'awaiting_template') {
            $this->step = 'template';

            return;
        }

        if ($generation->status === 'awaiting_theme') {
            $this->step = 'theme';

            return;
        }

        if ($this->shouldStayOnManualProductInput($generation, $research)) {
            $source = ($research['manual_input_source'] ?? 'listing') === 'json' ? 'json' : 'listing';
            $this->productSource = $source;
            $this->researchSource = $source;
            $this->step = 'products';

            return;
        }

        if ($generation->status === 'awaiting_products') {
            $this->step = ($research['source'] ?? '') === 'json' ? 'json_edit' : 'empty';

            return;
        }

        if ($generation->status === 'awaiting_category') {
            $this->step = in_array($this->emptyReason, ['no_products', 'blocked'], true) || $this->categories === [] ? 'empty' : 'category';

            return;
        }

        if ($generation->status === 'awaiting_subcategory') {
            if (in_array($this->emptyReason, ['no_products', 'blocked'], true)
                || (is_array($research['products'] ?? null) && $research['products'] !== [] && count($this->products) < 5)) {
                $this->step = 'empty';

                return;
            }

            $this->step = $this->subcategories === [] ? 'empty' : 'subcategory';
        }
    }

    public function startOver(): void
    {
        $this->reset([
            'step',
            'brandName',
            'brandUrl',
            'researchSource',
            'productSource',
            'listingUrl',
            'productsJson',
            'productsJsonFile',
            'confirmedBrandName',
            'generationId',
            'category',
            'subcategory',
            'selectedSubcategories',
            'categories',
            'subcategories',
            'products',
            'emptyReason',
            'listingScrapeError',
            'failureReason',
        ]);
        $this->researchSource = 'listing';
        $this->productSource = 'listing';
        $this->step = 'brand';
    }

    public function progressPercent(): int
    {
        $order = $this->wizardOrder();
        $foundIndex = array_search($this->step, $order, true);
        $index = $foundIndex === false ? count($order) - 1 : $foundIndex;

        return (int) round((($index + 1) / count($order)) * 100);
    }

    /**
     * @return list<string>
     */
    public function wizardOrder(): array
    {
        return ['brand', 'products', 'json_edit', 'discovering', 'template', 'theme'];
    }

    /**
     * @return array<string, string>
     */
    public function wizardTabs(): array
    {
        return [
            'Brand' => 'brand',
            'Products' => 'products',
            'Layout' => 'template',
            'Theme' => 'theme',
        ];
    }

    public function wizardTabIndex(): int
    {
        return match ($this->step) {
            'brand' => 0,
            'products', 'json_edit', 'discovering', 'empty', 'failed', 'website', 'analyzing', 'loading_subcategories', 'category', 'subcategory' => 1,
            'template' => 2,
            'theme' => 3,
            default => 0,
        };
    }

    #[Computed]
    public function productsJsonExample(): string
    {
        return app(CatalogProductImportValidator::class)->exampleJson();
    }

    #[Computed]
    public function rankingResearch(): array
    {
        return $this->ownedGeneration()?->catalogResearch() ?? [];
    }

    #[Computed]
    public function recentGenerations(): Collection
    {
        return auth()->user()->landingPageGenerations()
            ->where('layout_mode', LandingPageLayoutMode::Catalog)
            ->latest()
            ->take(8)
            ->get();
    }

    public function render(CatalogTemplateLocator $locator)
    {
        $generation = $this->ownedGeneration();
        $selectedTemplate = trim((string) ($generation?->catalogResearch()['template'] ?? ''));

        return view('livewire.catalog-landing-pages.chat-wizard', [
            'templateChoices' => $this->step === 'template' ? $locator->sharedTemplates() : [],
            'themeChoices' => $this->step === 'theme' && $selectedTemplate !== ''
                ? CatalogThemeRegistry::forTemplate($selectedTemplate)
                : [],
            'selectedTemplate' => $selectedTemplate,
        ]);
    }

    private function ownedGeneration(): ?LandingPageGeneration
    {
        if ($this->generationId === null) {
            return null;
        }

        return LandingPageGeneration::query()
            ->where('user_id', auth()->id())
            ->where('layout_mode', LandingPageLayoutMode::Catalog)
            ->find($this->generationId);
    }

    private function resumeGeneration(): void
    {
        if ($this->generationId === null) {
            return;
        }

        $generation = $this->ownedGeneration();

        if ($generation === null) {
            $this->generationId = null;

            return;
        }

        $this->confirmedBrandName = $generation->brand_name;
        $this->brandName = $generation->brand_name;
        $this->brandUrl = (string) ($generation->brand_url ?? '');

        $research = $generation->catalogResearch();
        $this->researchSource = match ($research['source'] ?? '') {
            'listing' => 'listing',
            'json' => 'json',
            default => 'website',
        };
        $this->productSource = in_array($this->researchSource, ['listing', 'json'], true)
            ? $this->researchSource
            : 'listing';

        $this->syncFromGeneration();
    }

    /**
     * @return list<string>
     */
    private function selectedNames(mixed $items): array
    {
        if (! is_array($items)) {
            return [];
        }

        if ($items !== [] && array_is_list($items) && is_string($items[0] ?? null)) {
            return array_values(array_filter($items, fn (mixed $name): bool => is_string($name) && $name !== ''));
        }

        $names = [];

        foreach ($items as $row) {
            if (! is_array($row)) {
                continue;
            }

            $name = trim((string) ($row['name'] ?? ''));

            if ($name !== '' && ! in_array($name, $names, true)) {
                $names[] = $name;
            }
        }

        return $names;
    }

    /**
     * @return list<string|\Closure>
     */
    private function brandNameRules(): array
    {
        return [
            'required',
            'string',
            'max:255',
            function (string $attribute, mixed $value, \Closure $fail): void {
                if (is_string($value) && filter_var(trim($value), FILTER_VALIDATE_URL)) {
                    $fail('Enter the brand name, not the website URL.');
                }
            },
        ];
    }

    /**
     * @return list<string|\Closure>
     */
    private function brandUrlRules(): array
    {
        $rules = ['required', 'url', 'max:2000'];

        if ($this->researchSource !== 'website') {
            return $rules;
        }

        $rules[] = function (string $attribute, mixed $value, \Closure $fail): void {
            if (! is_string($value)) {
                return;
            }

            $parts = parse_url($value);
            $path = rtrim((string) ($parts['path'] ?? ''), '/');
            $query = (string) ($parts['query'] ?? '');

            if ($path !== '' || $query !== '') {
                $fail('Enter the website domain (for example https://www.officedepot.com), not a product listing URL.');
            }
        };

        return $rules;
    }

    /**
     * @return list<string|\Closure>
     */
    private function listingUrlRules(): array
    {
        return [
            'required',
            'url',
            'max:2000',
            function (string $attribute, mixed $value, \Closure $fail): void {
                if (! is_string($value)) {
                    return;
                }

                $parts = parse_url($value);
                $path = rtrim((string) ($parts['path'] ?? ''), '/');
                $query = (string) ($parts['query'] ?? '');

                if ($path === '' && $query === '') {
                    $fail('Enter a product listing page URL, not the store homepage.');
                }
            },
        ];
    }

    /**
     * @param  list<array<string, mixed>>  $products
     */
    private function brandUrlFromProducts(array $products): string
    {
        $firstUrl = trim((string) ($products[0]['url'] ?? ''));

        if ($firstUrl === '') {
            return '';
        }

        $parts = parse_url($firstUrl);
        $scheme = (string) ($parts['scheme'] ?? 'https');
        $host = (string) ($parts['host'] ?? '');

        return $host !== '' ? "{$scheme}://{$host}" : $firstUrl;
    }

    private function restoreConfirmedBrandName(): void
    {
        if ($this->confirmedBrandName !== '') {
            $this->brandName = $this->confirmedBrandName;
        }
    }

    private function markManualProductInput(string $source): void
    {
        $generation = $this->ownedGeneration();

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

        $generation->mergeCatalogResearch([
            'manual_input' => true,
            'manual_input_source' => $source,
        ]);
    }

    /**
     * @param  array<string, mixed>  $research
     */
    private function shouldStayOnManualProductInput(LandingPageGeneration $generation, array $research): bool
    {
        if (($research['manual_input'] ?? false) !== true) {
            return false;
        }

        return $generation->status === 'awaiting_products';
    }

    /**
     * @return list<array{name: string, url: string}>
     */
    private function namedChoices(mixed $items): array
    {
        $choices = [];

        foreach (is_array($items) ? $items : [] as $row) {
            if (! is_array($row)) {
                continue;
            }

            $name = trim((string) ($row['name'] ?? ''));

            if ($name === '') {
                continue;
            }

            $choices[] = [
                'name' => $name,
                'url' => (string) ($row['url'] ?? ''),
            ];
        }

        return $choices;
    }
}
