<?php

namespace App\Models;

use App\Enums\LandingPageLayoutMode;
use App\Support\CatalogAffiliateRegistry;
use App\Support\CatalogMetaLeadEventName;
use App\Support\PromptLandingPagePixels;
use Database\Factories\LandingPageGenerationFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

#[Fillable([
    'user_id', 'brand_name', 'brand_url', 'website_url', 'product_urls', 'notes',
    'layout_mode', 'html_template', 'brief', 'catalog', 'status', 'failure_reason',
    'google_conversion_send_to', 'meta_pixel_id', 'admedia_campaign_id', 'affiliate_key',
    'published_landing_page_id', 'published_at',
])]
class LandingPageGeneration extends Model
{
    /** @use HasFactory<LandingPageGenerationFactory> */
    use HasFactory;

    protected function casts(): array
    {
        return [
            'product_urls' => 'array',
            'layout_mode' => LandingPageLayoutMode::class,
            'brief' => 'array',
            'catalog' => 'array',
            'published_at' => 'datetime',
        ];
    }

    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    public function landingPages(): HasMany
    {
        return $this->hasMany(LandingPage::class);
    }

    public function publishedLandingPage(): BelongsTo
    {
        return $this->belongsTo(LandingPage::class, 'published_landing_page_id');
    }

    public function primaryLiveUrl(): ?string
    {
        $published = $this->relationLoaded('publishedLandingPage')
            ? $this->publishedLandingPage
            : $this->publishedLandingPage()->with('generation')->first();

        if ($published instanceof LandingPage) {
            return $published->partnerPreviewUrl() ?? $published->publicUrl();
        }

        return null;
    }

    /**
     * @return array{visits: int, clickouts: int, clickout_rate: float}
     */
    public function publishedAnalyticsSummary(): array
    {
        $published = $this->relationLoaded('publishedLandingPage')
            ? $this->publishedLandingPage
            : $this->publishedLandingPage()->first();

        if (! $published instanceof LandingPage) {
            return [
                'visits' => 0,
                'clickouts' => 0,
                'clickout_rate' => 0.0,
            ];
        }

        $visits = $published->catalogVisits()->count();
        $visitsWithClickouts = $published->catalogVisits()->whereHas('clickouts')->count();

        return [
            'visits' => $visits,
            'clickouts' => $published->catalogClickouts()->count(),
            'clickout_rate' => $visits > 0 ? round(($visitsWithClickouts / $visits) * 100, 1) : 0.0,
        ];
    }

    /**
     * @return list<array{page: LandingPage, visits: int, clickouts: int}>
     */
    public function variantAnalyticsSummaries(): array
    {
        $pages = $this->relationLoaded('landingPages')
            ? $this->landingPages
            : $this->landingPages()->orderBy('id')->get();

        return $pages->map(function (LandingPage $page): array {
            return [
                'page' => $page,
                'visits' => $page->catalogVisits()->count(),
                'clickouts' => $page->catalogClickouts()->count(),
            ];
        })->all();
    }

    public function selectedTemplate(): ?string
    {
        $template = trim((string) ($this->catalogResearch()['template'] ?? ''));

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

    public function selectedTheme(): ?string
    {
        $theme = trim((string) ($this->catalogResearch()['theme'] ?? ''));

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

    public static function defaultPartnerWebsiteUrl(): ?string
    {
        return PromptLandingPage::normalizeWebsiteUrl(config('catalog.partner_website_url'));
    }

    public function publicHost(): ?string
    {
        $host = parse_url((string) $this->website_url, PHP_URL_HOST);

        return is_string($host) && $host !== '' ? PromptLandingPage::normalizeHost($host) : null;
    }

    public function displayWebsite(): string
    {
        return $this->publicHost() ?? (filled($this->website_url) ? (string) $this->website_url : '—');
    }

    public function canManagePixels(): bool
    {
        return $this->displayStatus() === 'completed';
    }

    public function canManageAffiliateTracking(): bool
    {
        return $this->canManagePixels();
    }

    public function admediaCampaignId(): ?string
    {
        $value = trim((string) ($this->admedia_campaign_id ?? ''));

        return preg_match('/^\d+$/', $value) === 1 ? $value : null;
    }

    public function affiliateKey(): ?string
    {
        $value = trim((string) ($this->affiliate_key ?? ''));

        return CatalogAffiliateRegistry::isValidKey($value) ? $value : null;
    }

    public function affiliateId(): ?string
    {
        return CatalogAffiliateRegistry::affId($this->affiliateKey());
    }

    public function affiliateLabel(): ?string
    {
        return CatalogAffiliateRegistry::label($this->affiliateKey());
    }

    public function hasAffiliateTracking(): bool
    {
        return $this->admediaCampaignId() !== null && $this->affiliateId() !== null;
    }

    public function googleConversionSendTo(): ?string
    {
        $value = trim((string) ($this->google_conversion_send_to ?? ''));

        return PromptLandingPagePixels::isGoogleSendTo($value) ? $value : null;
    }

    public function metaPixelId(): ?string
    {
        $value = trim((string) ($this->meta_pixel_id ?? ''));

        return PromptLandingPagePixels::isMetaPixelId($value) ? $value : null;
    }

    public function catalogAdvertiserId(): ?string
    {
        $catalog = is_array($this->catalog) ? $this->catalog : [];
        $advertiserId = trim((string) ($catalog['advertiser_id'] ?? ''));

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

    public function catalogSubcategory(): ?string
    {
        $catalog = is_array($this->catalog) ? $this->catalog : [];
        $subcategory = trim((string) ($catalog['subcategory'] ?? ''));

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

    public function metaLeadEventName(): string
    {
        return CatalogMetaLeadEventName::fromBrand($this->brand_name);
    }

    /**
     * @return array<string, mixed>
     */
    public function catalogResearch(): array
    {
        $catalog = is_array($this->catalog) ? $this->catalog : [];

        return is_array($catalog['research'] ?? null) ? $catalog['research'] : [];
    }

    /**
     * @param  array<string, mixed>  $patch
     */
    public function mergeCatalogResearch(array $patch): void
    {
        $catalog = is_array($this->catalog) ? $this->catalog : [];
        $research = is_array($catalog['research'] ?? null) ? $catalog['research'] : [];
        $catalog['research'] = array_merge($research, $patch);
        $this->catalog = $catalog;
        $this->save();
    }

    public function isWizardResumable(): bool
    {
        return $this->isSetupIncomplete();
    }

    public function isSetupIncomplete(): bool
    {
        return in_array($this->status, [
            'researching',
            'discovering',
            'listing_subcategories',
            'awaiting_category',
            'awaiting_subcategory',
            'awaiting_products',
            'awaiting_template',
            'awaiting_theme',
        ], true);
    }

    public function shouldPollStatus(): bool
    {
        if (in_array($this->displayStatus(), ['completed', 'failed'], true)) {
            return false;
        }

        return ! $this->isSetupIncomplete();
    }

    public function displayStatus(): string
    {
        if (in_array($this->status, ['completed', 'failed'], true)) {
            return $this->status;
        }

        $pages = $this->relationLoaded('landingPages')
            ? $this->landingPages
            : $this->landingPages()->get();

        if ($pages->isEmpty()) {
            return $this->status;
        }

        if ($pages->every(fn (LandingPage $page): bool => $page->status === 'completed')) {
            return 'completed';
        }

        if ($pages->every(fn (LandingPage $page): bool => in_array($page->status, ['failed', 'skipped'], true))) {
            return 'failed';
        }

        return $this->status;
    }
}
