<?php

declare(strict_types=1);

final class MyronComponentPreviewRenderer
{
    /**
     * Atom-shaped primitives that are rendered as bespoke specimens in the viewer
     * because they don't appear in templates as section types (e.g. dialog, drawer,
     * toast, popover) — for these the canonical PrimitiveRenderer would still fall
     * through to a generic stub. Listed explicitly here so the routing decision is
     * legible. Anything NOT in this list is checked against `myronPrimitiveRenderer()->canRender()`.
     */
    private const VIEWER_ONLY_SPECIMENS = [
        'alert', 'avatar', 'badge', 'button', 'card',
        'cms-content-title', 'cms-content-body', 'cms-content-excerpt', 'cms-featured-media', 'cms-embed',
        'checkbox', 'radio', 'switch',
        'popover', 'tooltip', 'dropdown-menu', 'dialog', 'drawer', 'toast',
        'profile-panel', 'profile-summary-card', 'form-field',
        'input', 'text-input', 'search-input', 'textarea', 'select',
    ];

    /**
     * @param array<string, mixed> $component
     */
    public function renderPreview(array $component): string
    {
        $componentId = trim((string) ($component['id'] ?? 'component'));
        $slug = myronNormalizeSlug($componentId !== '' ? $componentId : 'component');
        $variant = $this->primaryVariant($component);
        $variantSlug = $variant === '' ? '' : myronNormalizeSlug($variant);
        $envelope = myronComponentPreviewCatalog()->previewEnvelope($component);
        $useCanonical = $this->shouldUseCanonicalRenderer($slug);
        $source = $useCanonical ? 'public-renderer' : 'component-specimen';
        $classes = 'mc-component-preview__surface mc-component-preview__surface--' . $slug . ' mc-component-preview__surface--envelope-' . $envelope;

        return '<div class="' . $this->escape($classes) . '" data-myron-rendered-component-preview="' . $this->escape($componentId) . '" data-myron-preview-envelope="' . $this->escape($envelope) . '" data-myron-preview-source="' . $this->escape($source) . '">'
            . $this->renderSpecimen($component, $slug, $variantSlug)
            . '</div>';
    }

    /**
     * Decides whether the viewer routes through `MyronPrimitiveRenderer` (the canonical
     * render path used by template previews and the live page) or falls back to a
     * viewer-side specimen. Section-shaped primitives go through the canonical path;
     * atom-shaped specimens that don't have a section renderer use the viewer-side stubs.
     */
    private function shouldUseCanonicalRenderer(string $slug): bool
    {
        if (in_array($slug, self::VIEWER_ONLY_SPECIMENS, true)) {
            return false;
        }

        return myronPrimitiveRenderer()->canRender($slug);
    }

    /**
     * @param array<string, mixed> $component
     */
    private function renderSpecimen(array $component, string $slug, string $variantSlug): string
    {
        if ($this->shouldUseCanonicalRenderer($slug)) {
            return $this->renderPublicSection($slug, $variantSlug);
        }

	        return match ($slug) {
	            'alert' => $this->renderAlert($variantSlug),
	            'avatar' => '<div class="myron-avatar myron-avatar--md myron-avatar--circle" aria-label="Preview user"><span class="myron-avatar__initials">MC</span></div>',
	            'badge' => '<span class="myron-badge">Preview badge</span>',
	            'button' => '<button class="myron-button myron-button--intent-primary myron-button--appearance-solid myron-button--size-md" type="button">Preview action</button>',
	            'card' => $this->renderCard(),
	            'cms-content-title' => '<h2 class="myron-heading myron-cms-content-title">Content title preview</h2>',
	            'cms-content-body' => $this->renderCmsContentBody(),
	            'cms-content-excerpt' => '<p class="myron-paragraph myron-cms-content-excerpt">A short governed excerpt rendered through the shared paragraph primitive.</p>',
	            'cms-featured-media' => $this->renderFeaturedMedia(),
	            'cms-embed' => $this->renderEmbed(),
	            'checkbox' => '<label class="mc-component-preview__choice"><input class="myron-checkbox" type="checkbox" checked><span class="myron-text">Checked option</span></label>',
	            'radio' => '<label class="mc-component-preview__choice"><input class="myron-radio" type="radio" checked><span class="myron-text">Selected option</span></label>',
	            'switch' => '<label class="mc-component-preview__choice"><input class="myron-switch" type="checkbox" checked><span class="myron-text">Enabled setting</span></label>',
	            'popover' => $this->renderPopover(),
	            'tooltip' => $this->renderTooltip(),
	            'dropdown-menu' => $this->renderDropdownMenu(),
	            'dialog' => $this->renderDialog(),
	            'drawer' => $this->renderDrawer(),
	            'toast' => $this->renderToast(),
	            'profile-panel', 'profile-summary-card' => $this->renderProfilePanel(),
	            'form-field' => $this->renderFormField(),
	            'input', 'text-input', 'search-input' => '<label class="mc-component-preview__field"><span class="myron-text">Input label</span><input class="myron-input" type="text" value="Preview value"></label>',
	            'textarea' => '<label class="mc-component-preview__field"><span class="myron-text">Message</span><textarea class="myron-textarea" rows="3">Preview copy</textarea></label>',
	            'select' => '<label class="mc-component-preview__field"><span class="myron-text">Option</span><select class="myron-select"><option>Preview option</option></select></label>',
	            default => $this->renderGeneric($component, $slug, $variantSlug),
	        };
	    }

    private function renderPublicSection(string $slug, string $variantSlug): string
    {
        // APO-061 R4c: route directly through MyronPrimitiveRenderer instead of
        // wrapping a fake page and regex-extracting the body. The viewer now emits
        // the same HTML the live page does for the same `(componentId, variant,
        // archetypeContext)` tuple — verified by app/tests/verify-primitive-renderer-parity.php.
        $isEcom = in_array($slug, [
            'product-grid', 'product-filters', 'product-sort-control',
            'product-gallery', 'product-details', 'variant-selector',
            'quantity-stepper', 'rating-summary', 'cart-table', 'order-summary',
            'checkout-stepper', 'checkout-address-form', 'shipping-method-selector',
            'payment-form', 'checkout-review',
        ], true);

        $context = [
            'bundleId' => $isEcom ? 'site-type.ecom' : 'site-type.biz',
            'templateId' => 'template.component-preview.v1',
            'basePath' => '/app/site',
        ];
        $props = [
            'variant' => $variantSlug !== '' && $variantSlug !== 'default' ? $variantSlug : 'preview',
        ];

        $html = trim(myronPrimitiveRenderer()->renderById($slug, $props, $context));
        if ($html === '') {
            return $this->renderGeneric(['id' => $slug], $slug, $variantSlug);
        }

        return $html;
    }

	    private function renderAlert(string $variantSlug): string
	    {
	        $tone = in_array($variantSlug, ['tone-success', 'tone-warning', 'tone-danger', 'tone-info'], true) ? ' myron-alert--' . $variantSlug : ' myron-alert--tone-info';

	        return '<div class="myron-alert' . $this->escape($tone) . '" role="alert">'
	            . '<span class="myron-alert__icon" aria-hidden="true">i</span>'
	            . '<div class="myron-alert__body"><p class="myron-text myron-alert__title">Preview alert</p><p class="myron-paragraph">Deterministic component specimen.</p></div>'
	            . '</div>';
	    }

	    private function renderCard(): string
	    {
	        return '<article class="myron-card myron-card--tone-default myron-card--border-thin myron-card--padding-md">'
	            . '<header class="myron-card__header"><h3 class="myron-heading">Card preview</h3></header>'
	            . '<div class="myron-card__body"><p class="myron-paragraph">Body content is rendered with the shared paragraph primitive.</p></div>'
	            . '<footer class="myron-card__footer"><a class="myron-button myron-button--intent-neutral myron-button--appearance-outline myron-button--size-sm" href="#">View details</a></footer>'
	            . '</article>';
	    }

	    private function renderCmsContentBody(): string
	    {
	        return '<div class="myron-cms-content-body">'
	            . '<h2 class="myron-heading">Cms Content Body</h2>'
	            . '<p class="myron-paragraph">Published body copy renders through governed text primitives.</p>'
	            . '<ul class="myron-list"><li class="myron-list__item">List content keeps primitive spacing.</li><li class="myron-list__item">Inline content inherits tokenized typography.</li></ul>'
	            . '</div>';
	    }

	    private function renderFeaturedMedia(): string
	    {
	        return '<figure class="myron-figure myron-cms-featured-media">'
	            . '<div class="myron-image mc-component-preview__media-placeholder" aria-hidden="true"></div>'
	            . '<figcaption class="myron-text myron-text--muted">Featured media caption</figcaption>'
	            . '</figure>';
	    }

	    private function renderEmbed(): string
	    {
	        return '<div class="myron-card myron-card--border-thin myron-card--padding-md myron-cms-embed">'
	            . '<h3 class="myron-heading">Embedded content</h3>'
	            . '<p class="myron-paragraph">Third-party output is framed by governed card and text primitives.</p>'
	            . '</div>';
	    }

	    private function renderPopover(): string
	    {
	        return '<span class="myron-popover-wrapper">'
	            . '<button class="myron-button myron-button--intent-neutral myron-button--appearance-outline myron-button--size-sm" type="button" aria-expanded="true" aria-controls="component-preview-popover">Options</button>'
	            . '<div class="myron-popover" id="component-preview-popover">'
	            . '<p class="myron-paragraph">Popover content stays anchored to its trigger.</p>'
	            . '<a class="myron-button myron-button--intent-primary myron-button--appearance-solid myron-button--size-sm" href="#">Apply</a>'
	            . '</div></span>';
	    }

	    private function renderTooltip(): string
	    {
	        return '<span class="myron-tooltip-wrapper">'
	            . '<button class="myron-button myron-button--intent-neutral myron-button--appearance-outline myron-button--size-sm" type="button" aria-describedby="component-preview-tooltip">Hover target</button>'
	            . '<span class="myron-tooltip" role="tooltip" id="component-preview-tooltip">Primitive tooltip text</span>'
	            . '</span>';
	    }

	    private function renderDropdownMenu(): string
	    {
	        return '<div class="myron-dropdown-menu">'
	            . '<button class="myron-button myron-button--intent-neutral myron-button--appearance-outline myron-button--size-sm myron-dropdown-menu__trigger" type="button" aria-expanded="true">Actions</button>'
	            . '<div class="myron-dropdown-menu__panel" role="menu">'
	            . '<button class="myron-dropdown-menu__item" type="button" role="menuitem"><span class="myron-dropdown-menu__item-label">Edit</span></button>'
	            . '<button class="myron-dropdown-menu__item" type="button" role="menuitem"><span class="myron-dropdown-menu__item-label">Duplicate</span></button>'
	            . '<div class="myron-dropdown-menu__separator" role="separator"></div>'
	            . '<button class="myron-dropdown-menu__item" type="button" role="menuitem"><span class="myron-dropdown-menu__item-label">Archive</span></button>'
	            . '</div></div>';
	    }

	    private function renderDialog(): string
	    {
	        return '<div class="mc-component-preview__overlay-stage" data-myron-preview-contained-overlay="dialog">'
	            . '<div class="myron-dialog-backdrop" aria-hidden="true"></div>'
	            . '<section class="myron-dialog myron-dialog--confirmation" role="dialog" aria-modal="true" aria-hidden="false" aria-labelledby="component-preview-dialog-title">'
	            . '<header class="myron-dialog__header"><h3 class="myron-heading" id="component-preview-dialog-title">Confirm change</h3><button class="myron-dialog__close" type="button" aria-label="Close">x</button></header>'
	            . '<div class="myron-dialog__body"><p class="myron-paragraph">Dialog body copy uses the same paragraph primitive.</p></div>'
	            . '<footer class="myron-dialog__actions"><button class="myron-button myron-button--intent-neutral myron-button--appearance-outline myron-button--size-sm" type="button">Cancel</button><button class="myron-button myron-button--intent-primary myron-button--appearance-solid myron-button--size-sm" type="button">Confirm</button></footer>'
	            . '</section></div>';
	    }

	    private function renderDrawer(): string
	    {
	        return '<div class="mc-component-preview__overlay-stage" data-myron-preview-contained-overlay="drawer">'
	            . '<div class="myron-drawer-backdrop" aria-hidden="true"></div>'
	            . '<aside class="myron-drawer myron-drawer--size-sm myron-drawer--side-right" aria-hidden="false" aria-labelledby="component-preview-drawer-title">'
	            . '<header class="myron-drawer__header"><h3 class="myron-heading" id="component-preview-drawer-title">Drawer panel</h3><button class="myron-drawer__close" type="button" aria-label="Close">x</button></header>'
	            . '<div class="myron-drawer__body"><p class="myron-paragraph">Drawer content remains scoped to the preview frame.</p></div>'
	            . '<footer class="myron-drawer__footer"><button class="myron-button myron-button--intent-primary myron-button--appearance-solid myron-button--size-sm" type="button">Save</button></footer>'
	            . '</aside></div>';
	    }

	    private function renderToast(): string
	    {
	        return '<div class="mc-component-preview__overlay-stage" data-myron-preview-contained-overlay="toast">'
	            . '<div class="myron-toast-region myron-toast-region--position-top-right">'
	            . '<div class="myron-toast myron-toast--tone-success" role="status">'
	            . '<span class="myron-toast__icon" aria-hidden="true">i</span><div class="myron-toast__body"><p class="myron-text myron-toast__title">Saved</p><p class="myron-paragraph myron-toast__message">The notification uses primitive text styles.</p></div>'
	            . '<button class="myron-toast__dismiss" type="button" aria-label="Dismiss">x</button>'
	            . '</div></div></div>';
	    }

	    private function renderProfilePanel(): string
	    {
	        return '<article class="myron-card myron-card--tone-default myron-card--border-thin myron-card--padding-md myron-profile-panel">'
	            . '<div class="myron-avatar myron-avatar--md myron-avatar--circle" aria-hidden="true"><span class="myron-avatar__initials">MC</span></div>'
	            . '<h3 class="myron-heading">Morgan Chen</h3>'
	            . '<p class="myron-paragraph">Administrator profile composed from avatar, text, form, and button primitives.</p>'
	            . '<label class="mc-component-preview__field"><span class="myron-text">Display name</span><input class="myron-input" type="text" value="Morgan Chen"></label>'
	            . '<a class="myron-button myron-button--intent-primary myron-button--appearance-solid myron-button--size-sm" href="#">Update profile</a>'
	            . '</article>';
	    }

	    private function renderFormField(): string
	    {
	        return '<label class="mc-component-preview__field myron-form-field">'
	            . '<span class="myron-text">Field label</span>'
	            . '<input class="myron-input" type="text" value="Primitive-backed input">'
	            . '<span class="myron-text myron-text--muted">Helpful field guidance.</span>'
	            . '</label>';
	    }

    /**
     * @param array<string, mixed> $component
     */
	    private function renderGeneric(array $component, string $slug, string $variantSlug): string
	    {
	        $baseClass = 'myron-' . $slug;
	        $slots = is_array($component['slots'] ?? null) ? $component['slots'] : [];
	        $composedOf = is_array($component['composedOf'] ?? null) ? array_map('strval', $component['composedOf']) : [];
	        $slotHtml = '';

	        foreach (array_slice(array_keys($slots), 0, 4) as $slotName) {
	            $slotSlug = myronNormalizeSlug((string) $slotName);
	            $slotHtml .= $this->renderPrimitiveSlot($baseClass, $slotSlug, (string) $slotName);
	        }

	        if ($slotHtml === '') {
	            $slotHtml = $this->renderComposedPrimitiveSample($composedOf);
	        }

	        if ($slotHtml === '') {
	            $slotHtml = '<h3 class="myron-heading">' . $this->escape($this->label($slug)) . '</h3><p class="myron-paragraph">Deterministic primitive-composed specimen.</p>';
	        }

	        return '<article class="mc-component-preview__fallback" data-myron-preview-fallback-component="' . $this->escape($slug) . '" data-myron-preview-fallback-variant="' . $this->escape($variantSlug) . '">' . $slotHtml . '</article>';
	    }

	    private function renderPrimitiveSlot(string $baseClass, string $slotSlug, string $slotName): string
	    {
	        $slotClass = $baseClass . '__' . $slotSlug;
	        $label = $this->escape($this->label($slotName));

	        return match (true) {
	            str_contains($slotSlug, 'title'), str_contains($slotSlug, 'headline'), $slotSlug === 'name' => '<h3 class="myron-heading ' . $this->escape($slotClass) . '">' . $label . '</h3>',
	            str_contains($slotSlug, 'icon') => '<span class="myron-icon ' . $this->escape($slotClass) . '" aria-hidden="true"></span>',
	            str_contains($slotSlug, 'action'), str_contains($slotSlug, 'cta'), str_contains($slotSlug, 'button') => '<a class="myron-button myron-button--intent-primary myron-button--appearance-solid myron-button--size-sm ' . $this->escape($slotClass) . '" href="#">Action</a>',
	            str_contains($slotSlug, 'input'), str_contains($slotSlug, 'field') => '<label class="mc-component-preview__field ' . $this->escape($slotClass) . '"><span class="myron-text">' . $label . '</span><input class="myron-input" type="text" value="Preview value"></label>',
	            default => '<p class="myron-paragraph ' . $this->escape($slotClass) . '">' . $label . ' preview content.</p>',
	        };
	    }

	    /**
	     * @param list<string> $composedOf
	     */
	    private function renderComposedPrimitiveSample(array $composedOf): string
	    {
	        $html = '';
	        foreach (array_slice($composedOf, 0, 4) as $primitiveId) {
	            $primitiveSlug = myronNormalizeSlug($primitiveId);
	            $html .= match ($primitiveSlug) {
	                'heading' => '<h3 class="myron-heading">Primitive heading</h3>',
	                'paragraph', 'text' => '<p class="myron-paragraph">Primitive paragraph content.</p>',
	                'button' => '<button class="myron-button myron-button--intent-primary myron-button--appearance-solid myron-button--size-sm" type="button">Primitive button</button>',
	                'input' => '<input class="myron-input" type="text" value="Primitive input">',
	                'textarea' => '<textarea class="myron-textarea" rows="2">Primitive textarea</textarea>',
	                'select' => '<select class="myron-select"><option>Primitive select</option></select>',
	                'avatar' => '<div class="myron-avatar myron-avatar--sm myron-avatar--circle" aria-hidden="true"><span class="myron-avatar__initials">MC</span></div>',
	                'card' => $this->renderCard(),
	                default => '',
	            };
	        }

	        return $html;
	    }

    /**
     * @param array<string, mixed> $component
     */
    private function primaryVariant(array $component): string
    {
        $variants = is_array($component['variants'] ?? null) ? $component['variants'] : [];
        foreach ($variants as $variant) {
            if (!is_array($variant)) {
                continue;
            }
            $id = trim((string) ($variant['id'] ?? ''));
            if ($id !== '' && $id !== 'default') {
                return $id;
            }
        }

        return 'default';
    }

    private function label(string $value): string
    {
        return ucwords(str_replace('-', ' ', myronNormalizeSlug($value)));
    }

    private function escape(string $value): string
    {
        return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
    }
}
