<?php

namespace App\Models;

use App\Enums\LandingPageVersion;
use App\Support\CatalogThemeRegistry;
use Database\Factories\LandingPageFactory;
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;
use Illuminate\Support\Str;

#[Fillable([
    'landing_page_generation_id', 'version', 'template', 'theme', 'title', 'status',
    'html', 'css', 'js', 'prompt_used', 'failure_reason',
])]
class LandingPage extends Model
{
    /** @use HasFactory<LandingPageFactory> */
    use HasFactory;

    public const CATALOG_PURPOSE_PREFIX = 'catalog-';

    protected function casts(): array
    {
        return [
            'version' => LandingPageVersion::class,
        ];
    }

    public function generation(): BelongsTo
    {
        return $this->belongsTo(LandingPageGeneration::class, 'landing_page_generation_id');
    }

    public function catalogVisits(): HasMany
    {
        return $this->hasMany(CatalogLandingPageVisit::class)->latest('id');
    }

    public function catalogClickouts(): HasMany
    {
        return $this->hasMany(CatalogLandingPageClickout::class)->latest('id');
    }

    public function displayLabel(): string
    {
        $label = $this->version->label();

        if (filled($this->template)) {
            return $label.' · '.$this->template;
        }

        return $label;
    }

    public function isReady(): bool
    {
        return $this->status === 'completed' && filled($this->html);
    }

    public function isPubliclyServable(): bool
    {
        if (! $this->isReady() || $this->publishHost() === null) {
            return false;
        }

        $generation = $this->relationLoaded('generation')
            ? $this->generation
            : $this->generation()->first();

        if ($generation === null || ! $generation->hasAffiliateTracking()) {
            return false;
        }

        if ($generation->published_landing_page_id === null) {
            return false;
        }

        return (int) $generation->published_landing_page_id === (int) $this->id;
    }

    public function isPublished(): bool
    {
        $generation = $this->relationLoaded('generation')
            ? $this->generation
            : $this->generation()->first();

        return $generation !== null
            && (int) $generation->published_landing_page_id === (int) $this->id;
    }

    public function themeLabel(): string
    {
        return CatalogThemeRegistry::label((string) $this->template, $this->theme);
    }

    public function publishSummary(): string
    {
        $parts = [$this->version->label()];

        if (filled($this->template)) {
            $parts[] = str_replace('-', ' ', (string) $this->template);
        }

        $parts[] = $this->themeLabel();

        return implode(' · ', $parts);
    }

    public static function isCatalogPurposeSlug(string $purpose): bool
    {
        return str_starts_with($purpose, self::CATALOG_PURPOSE_PREFIX);
    }

    public function purposeSlug(): string
    {
        return self::CATALOG_PURPOSE_PREFIX.Str::slug($this->version->value);
    }

    public static function versionFromPurposeSlug(string $slug): ?LandingPageVersion
    {
        if (! self::isCatalogPurposeSlug($slug)) {
            return null;
        }

        $versionSlug = substr($slug, strlen(self::CATALOG_PURPOSE_PREFIX));

        foreach (LandingPageVersion::cases() as $version) {
            if (Str::slug($version->value) === $versionSlug) {
                return $version;
            }
        }

        return null;
    }

    /**
     * @return array{domain: string, purpose: string, landing_page: int}|null
     */
    public function publicRouteParameters(): ?array
    {
        $host = $this->publishHost();
        $purpose = $this->purposeSlug();

        if ($host === null || $purpose === '' || $this->id === null) {
            return null;
        }

        return [
            'domain' => $host,
            'purpose' => $purpose,
            'landing_page' => $this->id,
        ];
    }

    public function publicUrl(): ?string
    {
        if (! $this->isPubliclyServable()) {
            return null;
        }

        $parameters = $this->publicRouteParameters();

        return $parameters === null ? null : route('public-prompt-landing-pages.show', $parameters);
    }

    public function partnerPreviewUrl(): ?string
    {
        if (! $this->isReady() || $this->publishHost() === null) {
            return null;
        }

        $host = $this->publishHost();
        $purpose = $this->purposeSlug();
        $previewHost = is_string($host) ? (PromptLandingPage::PARTNER_PREVIEW_HOSTS[$host] ?? null) : null;

        if ($previewHost === null || $purpose === '' || $this->id === null) {
            return null;
        }

        return 'https://'.$previewHost.'/lp/'.$purpose.'/'.$this->id;
    }

    public static function findPublic(string $domain, string $purposeSlug, int $id): ?self
    {
        $version = self::versionFromPurposeSlug($purposeSlug);

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

        $page = self::query()->with('generation')->find($id);

        if ($page === null || ! $page->isPubliclyServable() || $page->version !== $version) {
            return null;
        }

        if ($page->generation?->publicHost() !== PromptLandingPage::normalizeHost($domain)) {
            return null;
        }

        return $page;
    }

    private function publishHost(): ?string
    {
        $generation = $this->relationLoaded('generation')
            ? $this->generation
            : $this->generation()->first();

        return $generation?->publicHost();
    }

    public function canRetry(): bool
    {
        return ! in_array($this->status, ['queued', 'generating', 'processing'], true);
    }

    public function renderedDocument(): string
    {
        $html = $this->html ?? '';

        if ($this->isFullHtmlDocument($html)) {
            return $this->previewFullDocument($html);
        }

        $title = e($this->title ?: $this->displayLabel());
        [$bodyAttributes, $innerHtml] = $this->splitStoredHtml();

        return '<!doctype html>'."\n"
            .'<html>'."\n"
            .'<head>'."\n"
            .'<meta charset="utf-8">'."\n"
            .'<title>'.$title.'</title>'."\n"
            .'<style>'."\n".($this->css ?? '')."\n".$this->detailsAccordionCss()."\n".'</style>'."\n"
            .'</head>'."\n"
            .'<body'.$bodyAttributes.'>'."\n"
            .$innerHtml."\n"
            .'<script>'."\n".$this->detailsAccordionScript()."\n".'</script>'."\n"
            .'<script>'."\n".($this->js ?? '')."\n".'</script>'."\n"
            .'</body>'."\n"
            .'</html>';
    }

    private function isFullHtmlDocument(string $html): bool
    {
        return (bool) preg_match('/<!doctype html>|<html[\s>]/i', $html);
    }

    private function previewFullDocument(string $html): string
    {
        $css = trim((string) ($this->css ?? ''));
        $js = trim((string) ($this->js ?? ''));

        if ($css !== '' && preg_match('/<\/head>/i', $html) === 1) {
            $html = preg_replace(
                '/<\/head>/i',
                "<style>\n{$css}\n</style>\n</head>",
                $html,
                1,
            ) ?? $html;
        }

        if ($js !== '' && preg_match('/<\/body>/i', $html) === 1) {
            $html = preg_replace(
                '/<\/body>/i',
                "<script>\n{$js}\n</script>\n</body>",
                $html,
                1,
            ) ?? $html;
        }

        return $html;
    }

    /**
     * @return array{0: string, 1: string}
     */
    private function splitStoredHtml(): array
    {
        $html = $this->html ?? '';

        if (preg_match('/<body([^>]*)>(.*)<\/body>/is', $html, $matches) !== 1) {
            return ['', $html];
        }

        $attributes = trim($matches[1]);

        return [$attributes === '' ? '' : ' '.$attributes, trim($matches[2])];
    }

    private function detailsAccordionCss(): string
    {
        return '.js-details-panel{display:none;} .is-open > .js-details-panel,.is-open .js-details-panel{display:block;}';
    }

    private function detailsAccordionScript(): string
    {
        return <<<'JS'
document.addEventListener('click', function (event) {
    var trigger = event.target.closest('button, a, [role="button"], .js-show-details');
    if (!trigger) {
        return;
    }

    var label = (trigger.textContent || '').replace(/\s+/g, ' ').trim();
    var isToggle = trigger.classList.contains('js-show-details') || /^(show|hide) details$/i.test(label);
    if (!isToggle) {
        return;
    }

    event.preventDefault();
    event.stopImmediatePropagation();

    var card = trigger.closest('article, li, section, .product-card, [class*="card"]') || trigger.parentElement;
    if (!card) {
        return;
    }

    var panel = card.querySelector('.js-details-panel');
    if (!panel) {
        var row = trigger.parentElement;
        panel = row && row.nextElementSibling ? row.nextElementSibling : trigger.nextElementSibling;
    }
    if (!panel || panel === trigger) {
        return;
    }

    panel.classList.add('js-details-panel');
    var open = card.classList.toggle('is-open');
    trigger.setAttribute('aria-expanded', open ? 'true' : 'false');
    trigger.innerHTML = trigger.innerHTML.replace(open ? /Show details/i : /Hide details/i, open ? 'Hide details' : 'Show details');
}, true);
JS;
    }
}
