<?php

namespace Components\Ui;

use Components\Base;
use Components\Newsletter\PageConfig;
use Components\Support\Devices;

/**
 * EmailEditor UI component.
 *
 * Renders the email editor shell with language tabs, an Email Details panel,
 * and an editor iframe per language. All inline HTML is captured with ob_start.
 *
 * Options:
 * - lng_id: int active language id (defaults to session languages_id)
 * - url: optional base URL for the editor when per-language URL is not provided
 * - modal_id: optional string id of a BeezUI Modal root; when provided, the
 *   editor toolbar is teleported into that modal's header toolbar container
 *   using Alpine's x-teleport ("<modal_id>-header-toolbar"), similar to
 *   Components\Ui\EmailPreviewer.
 *
 * $langInfos (optional): array of per-language hashes like
 *   [
 *     'id' => 2,
 *     'text' => 'EN',
 *     'visualEditorUrl' => '...',     // iframe URL for editor (preferred)
 *     'autosaveUrl' => '...',         // htmx autosave endpoint for details
 *     'subject' => '...',
 *     'preheader' => '...'
 *   ]
 */
class EmailEditor
{
    /**
     * @return string
     */
    public static function showSubjectRequiredWarning()
    {
        return Badge::render('danger', mh_lng('MAILBEEZ_EDITOR_SUBJECT', 'Subject'), [
                'icon' => Base::SVG_EXCLAMATION_TRIANGLE,
                'variant' => 'soft',
                'rounded' => 'rounded',
                'title' => mh_lng('MAILBEEZ_SUBJECT_REQUIRED', 'Subject is required')
        ]);
    }

    /**
     * Render the shared status block ("Draft" + autosave indicator).
     *
     * Kept in a dedicated helper so that we can place it either inside the
     * standalone editor header or, when embedded in a modal, directly next to
     * the modal title (via the modal header extras slot).
     */
    private static function renderStatusBlock(): string
    {
        $labelSaved = mh_lng('MAILBEEZ_AUTOSAVE_ALL_SAVED', 'All changes saved');
        $labelSaving = mh_lng('MAILBEEZ_AUTOSAVE_SAVING', 'Saving...');
        ob_start();
        ?>

        <div class="flex items-center gap-3 shrink-0">
            <style>
                /* default state */
                #autosave-indicator .is-saving {
                    opacity: 0;
                    visibility: hidden;
                    display: none;
                }

                #autosave-indicator .is-saved {
                    opacity: 1;
                    visibility: visible;
                    display: block;
                }

                /* animate both directions */
                #autosave-indicator .is-saving,
                #autosave-indicator .is-saved {
                    transition: opacity 200ms ease-in;
                }

                /* during request (htmx adds .htmx-request to the indicator element) */
                #autosave-indicator.htmx-request .is-saving {
                    opacity: 1;
                    visibility: visible;
                    display: block;
                }

                #autosave-indicator.htmx-request .is-saved {
                    opacity: 0;
                    visibility: hidden;
                    display: none;
                }
            </style>

            <!--            <span class="text-sm text-muted-foreground">Draft 1</span>-->

            <div id="autosave-indicator" class="text-sm text-secondary text-nowrap w-42">
                <div class="is-saving">
                    <?= Base::h($labelSaving) ?>
                </div>

                <div id="autosave-status"
                     class="is-saved"
                     data-label-saved="<?= Base::h($labelSaved, false) ?>">
                    <?= Base::h($labelSaved) ?>
                </div>
            </div>
        </div>
        <?php
        return ob_get_clean();
    }

    /**
     * Render the top bar header (status, language tabs, device buttons, actions dropdown).
     *
     * The returned HTML can either be rendered in-place (standalone editor page)
     * or teleported into a modal header toolbar container when the editor is
     * embedded in a BeezUI modal. In modal mode the status block itself is
     * rendered separately and teleported next to the modal title so that the
     * "start" section visually follows the title.
     *
     * @param array $items Normalized language items
     * @param int $lngId Active language id
     * @param array $editorDevices Device presets for preview buttons
     * @param array $actionsDropdown Optional dropdown configuration
     * @param bool $teleport When true, omit the status block from this
     *                               header as it is rendered in the modal
     *                               header extras slot instead.
     */
    private static function renderTopBar(array $items, int $lngId, array $editorDevices, array $actionsDropdown, bool $teleport = false, ?string $module = null, array $options = [], ?PageConfig $pageConfig = null): string
    {
        if (!$pageConfig) {
            $pageConfig = new PageConfig($options['page_mode'] ?? PageConfig::fromRequest()->mode);
        }
        // Standalone editor uses a single three-part header (status left,
        // language tabs center, tools right). In modal mode we no longer
        // render this header directly; instead the individual pieces are
        // teleported into the dedicated modal header areas.

        $headerCls = $teleport
                ? 'z-20 mr-2 h-6 flex items-center justify-between gap-4'
                : 'flex items-center justify-between gap-4 px-4 h-14 bg-elevated border-b border-divider mb-2';

        $statusHtml = self::renderStatusBlock();
        $tabsHtml = self::renderLanguageTabs($items, $lngId);
        $toolsHtml = self::renderTools($editorDevices, $actionsDropdown, $module, $options, $pageConfig);

        ob_start();
        ?>

        <!-- Top bar: left=status, center=language tabs, right=tools -->
        <header class="<?= $headerCls; ?>">

            <?php if (!$teleport): ?>
                <?= $statusHtml; ?>
            <?php endif; ?>

            <!-- Center: language tabs & gender selection -->
            <div class="flex-1 flex justify-center items-center gap-4 min-w-0 overflow-hidden">
                <?= $tabsHtml; ?>
            </div>

            <!-- Right: tools/actions -->
            <?= $toolsHtml; ?>
        </header>

        <?php
        return ob_get_clean();
    }

    /**
     * Render just the language tabs block (center area of the toolbar).
     *
     * This helper is used both by the standalone editor header and when
     * teleported into the modal header center slot.
     */
    private static function renderLanguageTabs(array $items, int $lngId): string
    {
        ob_start();
        $itemCount = count($items);
        if ($itemCount < 1) {
            return '<div id="renderLanguageTabs: no languageItems"></div>';
        }
        ?>
        <div class="flex items-center gap-2 min-w-0 max-w-full">
            <!-- Tabs view: lg and up -->
            <div class="hidden lg:flex items-center gap-1 min-w-0 max-w-full ml-10"
                 x-data="<?= Base::h(\Components\Ui\FlowHelpers::alpineData('auto'), false); ?>"
                 x-init="update(); $nextTick(() => update()); setTimeout(() => update(), 60)"
                 @resize.window.debounce.120="update()"
                 @mouseover.debounce.120="update()">

                <?= \Components\Ui\FlowHelpers::renderScrollButton('left', 120, 'text-secondary/80 hover:text-secondary/100'); ?>

                <div x-ref="scroller" @scroll.passive="update()"
                     class="flex gap-1 overflow-x-auto overflow-y-hidden no-scrollbar snap-x snap-mandatory min-w-0">
                    <?php
                    foreach ($items as $li) {
                        $id = (int)($li['id'] ?? 0);
                        if ($id <= 0) {
                            continue;
                        }
                        $text = (string)($li['text'] ?? ('#' . $id));
                        $code = (string)($li['code'] ?? '');
                        $selected = ($id === (int)$lngId);

                        $flag = \Components\Form\BaseField::flagIconHtml($code, 'mr-1');
                        $labelHtml = $flag !== ''
                                ? '<span class="inline-flex items-center gap-1">' . $flag . '<span>' . Base::h($text) . '</span></span>'
                                : Base::h($text);

                        echo '<button ' . Base::attrs([
                                        'type' => 'button',
                                        'class' => 'px-3 py-2 rounded-md text-sm opacity-80 hover:opacity-100 focus:outline-none hover:bg-muted whitespace-nowrap snap-start relative',
                                        ':class' => "activeLngId === $id ? ' opacity-100 bg-muted' : ''",
                                        '@click' => 'activeLngId = ' . $id,
                                        'aria-pressed' => $selected ? 'true' : 'false',
                                ]) . '>' . $labelHtml . '
                            <span x-show="hasSubject && !subjects[' . $id . ']" class="absolute top-1 right-1 flex h-2 w-2 rounded-full bg-red-500 ring-1 ring-white dark:ring-gray-900" x-cloak></span>
                            </button>';
                    }
                    ?>
                </div>

                <?= \Components\Ui\FlowHelpers::renderScrollButton('right', 120, 'text-secondary/80 hover:text-secondary/100'); ?>
            </div>

            <!-- Dropdown view: shown on mobile -->
            <div class="flex lg:hidden items-center min-w-0">
                <div class="grid grid-cols-1 bg-white dark:bg-gray-800 rounded-lg border border-divider">
                    <select
                            x-model.number="activeLngId"
                            class="col-start-1 row-start-1 h-9 appearance-none rounded-lg border-0 bg-transparent pr-8 pl-3 font-medium sm:text-sm dark:*:bg-gray-950 focus:outline-none">
                        <?php foreach ($items as $li): ?>
                            <option value="<?= (int)$li['id'] ?>"><?= Base::h($li['text']) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"
                         class="pointer-events-none col-start-1 row-start-1 mr-2 size-4 self-center justify-self-end text-gray-400">
                        <path fill-rule="evenodd"
                              d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z"
                              clip-rule="evenodd"></path>
                    </svg>
                </div>
            </div>
        </div>
        <?php
        return ob_get_clean();
    }

    /**
     * Render the persona selection and preview controls in the details area.
     */
    public static function renderPreviewControls(int $lngId, array $overrides = []): string
    {
        $B = '\\Components\\Base';

        $genderVar = $overrides['gender_var'] ?? 'activeGnd';
        $lngVar = $overrides['lng_var'] ?? 'activeLngId';
        $applyJs = $overrides['apply_js'] ?? '$dispatch(\'email_editor:refresh\', { id: ' . $lngVar . ' });';
        $showLabel = $overrides['show_label'] ?? false;

        $genderMale = mh_lng('MAILBEEZ_GENDER_MALE', 'Mr.');
        $genderFemale = mh_lng('MAILBEEZ_GENDER_FEMALE', 'Mrs.');
        $genderUnknown = mh_lng('MAILBEEZ_GENDER_UNKNOWN', 'Unknown');
        $genderSelection = mh_lng('MAILBEEZ_GENDER_SELECTION', 'Salutation');
        $previewPrefix = mh_lng('MAILBEEZ_PREVIEW_PREFIX', 'Preview');
        $btnReset = mh_lng('MAILBEEZ_RESET', 'Reset');
        $btnUpdatePreview = mh_lng('MAILBEEZ_UPDATE_PREVIEW', 'Apply');

        $popoverId = 'persona-popover-' . $lngId;

        // Support for generic persona slots
        $slots = $overrides['persona_slots'] ?? [];
        if (empty($slots)) {
            $items = [
                    ['id' => 'f', 'label' => $genderFemale, 'icon_html' => $B::SVG_GENDER_F],
                    ['id' => 'm', 'label' => $genderMale, 'icon_html' => $B::SVG_GENDER_M],
                    ['id' => 'unknown', 'label' => $genderUnknown, 'icon_html' => '<span class="text-xl font-bold leading-none">-</span>'],
            ];
            // Default gender slot
            $slots[] = [
                    'label' => $genderSelection,
                    'content' => \Components\Ui\DevicePreviewer::renderPersonaOptionGroup($items, $genderVar, $genderVar . "='__ID__';"),
                    'display' => [
                            'm' => ['label' => $genderMale, 'var' => $genderVar],
                            'f' => ['label' => $genderFemale, 'var' => $genderVar],
                            'unknown' => ['label' => $genderUnknown, 'var' => $genderVar],
                    ]
            ];
        }

        ob_start();
        ?>
        <div class="flex items-center gap-2" x-cloak @click.stop>
            <?php if ($showLabel): ?>
                <span class="text-xs text-muted-foreground"><?= Base::h($previewPrefix) ?>:</span>
            <?php endif; ?>
            <?php
            $triggerHtml = '<span class="flex min-w-0 items-center truncate">';
            if (!$showLabel) {
                $triggerHtml .= '<span class="text-secondary mr-1">' . Base::h($previewPrefix) . ':</span>';
            }
            $triggerHtml .= '<span class="flex items-center gap-1">';
            $i = 0;
            foreach ($slots as $slot) {
                if (!isset($slot['display'])) continue;
                if ($i > 0) $triggerHtml .= '<span> · </span>';
                $triggerHtml .= '<span class="inline-block">';
                foreach ($slot['display'] as $val => $info) {
                    $triggerHtml .= '<span x-show="' . $info['var'] . ' === \'' . $val . '\'">' . Base::h($info['label']) . '</span>';
                }
                $triggerHtml .= '</span>';
                $i++;
            }
            $triggerHtml .= '</span>';
            $triggerHtml .= '</span>';
            $triggerHtml .= '<svg viewBox="0 0 20 20" fill="currentColor" data-slot="icon" aria-hidden="true" class="size-5 shrink-0 currentColor"><path d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>';

            $contentHtml = '';
            foreach ($slots as $slot) {
                $contentHtml .= '<div class="px-4 py-3 font-medium text-sm text-muted-foreground">' . Base::h($slot['label']) . '</div>';
                $contentHtml .= '<div class="px-4 pb-4">' . $slot['content'] . '</div>';
            }

            $resetJs = $genderVar . "='unknown'; " . $applyJs;

            $contentHtml .= '
                <div class="p-4 flex items-center justify-between gap-4 border-t border-divider bg-gray-50/50 dark:bg-white/5 rounded-b-xl">
                    ' . \Components\Button::render($btnUpdatePreview, [
                            'fullWidth' => true,
                            'size' => 'md',
                            'attrs' => [
                                    '@click' => $applyJs . ' document.getElementById(\'' . $popoverId . '\').hidePopover();',
                            ],
                    ]) . '
                </div>';

            echo \Components\Popover::render([
                    'id' => $popoverId,
                    'trigger_html' => $triggerHtml,
                    'trigger_attrs' => [
                            'class' => 'flex items-center justify-between gap-x-2 rounded-md px-3 py-1.25 text-base sm:text-sm/6 hover:bg-gray-50 focus:outline-none dark:hover:bg-white/10'
                    ],
                    'content_html' => $contentHtml,
                    'popover_attrs' => [
                            'anchor' => 'bottom start',
                            'class' => 'w-72'
                    ]
            ]);
            ?>
        </div>
        <?php
        return ob_get_clean();
    }

    /**
     * Render the tools/actions block (right area of the toolbar):
     * device buttons + optional actions dropdown.
     *
     * The returned HTML is wrapped in its own flex container so it can be
     * used both inside the standalone header and teleported to the modal
     * right-toolbar slot.
     */
    private static function renderTools(array $editorDevices, array $actionsDropdown, ?string $module = null, array $options = [], ?PageConfig $pageConfig = null): string
    {
        if (!$pageConfig) {
            $pageConfig = new PageConfig($options['page_mode'] ?? PageConfig::fromRequest()->mode);
        }
        ob_start();
        ?>
        <div class="flex items-center gap-3 opacity-90 hover:opacity-100 shrink-0">

            <?php
            // Zoom controls
            echo \Components\Ui\ScaleControl::render();
            ?>

            <?php
            // Shared pill-style device button group, unified with
            // EmailPreviewer::renderHeaderToolbar design.
            echo \Components\Ui\DevicePreviewer::renderDeviceButtonGroup($editorDevices);
            ?>

            <?php
            // Undo / Redo buttons (Email history controls)
            // --------------------------------------------
            // These trigger the shared undo/redo endpoint which delegates to
            // the legacy tmplmgr_lng history stack and then emits an
            // email_editor:refresh event so the visual editor iframe reloads
            // for the active language. Subject/preheader refresh in the
            // details header is intentionally ignored for now.
            if ($module !== null && $module !== ''):
                // Use partial=1 so the endpoint returns a lightweight snippet
                // suitable for swapping into the autosave status area.
                $historyParams = [
                        'module' => (string)$module,
                        'partial' => 1,
                ];
                if (!empty($options['content_module_id'])) {
                    $historyParams['content_module_id'] = (string)$options['content_module_id'];
                }
                if (!empty($options['maintemplate_id'])) {
                    $historyParams['maintemplate_id'] = (string)$options['maintemplate_id'];
                }
                if (isset($options['newsletter_id']) && $options['newsletter_id'] > 0) {
                    $historyParams['newsletter_id'] = (int)$options['newsletter_id'];
                    $historyParams['page_mode'] = $pageConfig->mode;
                }

                $undoUrl = \Components\Router::url('email_editor_undo_redo', array_merge($historyParams, [
                        'history_action' => 'undo',
                ]));
                $redoUrl = \Components\Router::url('email_editor_undo_redo', array_merge($historyParams, [
                        'history_action' => 'redo',
                ]));
                ?>
                <div class="flex items-center shrink-0">           <?php
                    $undoButtonOptions = [
                            'label' => '',
                            'variant' => 'ghost',
                            'size' => 'sm',
                            'icon' => '<span x-show="!undoRunning" class="p-0.5 [&_svg]:size-4">' . Base::SVG_UNDO . '</span><span x-show="undoRunning" class="p-0.5 [&_svg]:size-4" x-cloak>' . Base::SVG_SPINNER . '</span>',
                            'class' => 'text-muted-foreground p-2 py-2!',
                            'attrs' => [
                                    ':class' => "locked ? '" . Base::DISABLED_CLASSES . "' : 'hover:bg-muted hover:text-foreground'",
                                    ':disabled' => 'locked || undoRunning || redoRunning',
                                    'data-undo-url' => Base::h($undoUrl),
                                    'data-no-global-loader' => '1',
                                    'data-no-local-loader' => '1',
                                    'title' => 'Undo',
                                    '@click' => 'doUndo($el.dataset.undoUrl)'
                            ]
                    ];
                    echo \Components\Button::render($undoButtonOptions);
                    ?>

                    <?php
                    $redoButtonOptions = [
                            'label' => '',
                            'variant' => 'ghost',
                            'size' => 'sm',
                            'icon' => '<span x-show="!redoRunning" class="p-0.5 [&_svg]:size-4">' . Base::SVG_REDO . '</span><span x-show="redoRunning" class="p-0.5 [&_svg]:size-4"  x-cloak>' . Base::SVG_SPINNER . '</span>',
                            'class' => 'text-muted-foreground p-2 py-2!',
                            'attrs' => [
                                    ':class' => "locked ? '" . Base::DISABLED_CLASSES . "' : 'hover:bg-muted hover:text-foreground'",
                                    ':disabled' => 'locked || undoRunning || redoRunning',
                                    'data-redo-url' => Base::h($redoUrl),
                                    'data-no-global-loader' => '1',
                                    'data-no-local-loader' => '1',
                                    'title' => 'Redo',
                                    '@click' => 'doRedo($el.dataset.redoUrl)'
                            ]
                    ];
                    echo \Components\Button::render($redoButtonOptions);
                    ?>
                </div>

            <?php endif; ?>

            <?php
            // Dropdown menu for additional actions (three-dots trigger)
            $hasDropdown = !empty($actionsDropdown) && is_array($actionsDropdown);
            if ($hasDropdown) {
                $ddOpts = $actionsDropdown;

                // Provide a compact three-dots trigger if none given
                if (empty($ddOpts['trigger_html'])) {
                    $ddOpts['trigger_html'] = '⋯';
                }
                if (empty($ddOpts['trigger_tag'])) {
                    $ddOpts['trigger_tag'] = 'button';
                }

                $triggerAttrs = is_array($ddOpts['trigger_attrs'] ?? null) ? $ddOpts['trigger_attrs'] : [];
                $baseTriggerCls = 'px-2 py-1.5 rounded-md hover:bg-gray-100';
                if (empty($triggerAttrs['class'])) {
                    $triggerAttrs['class'] = $baseTriggerCls;
                } else {
                    $triggerAttrs['class'] .= ' ' . $baseTriggerCls;
                }
                if (($ddOpts['trigger_tag'] ?? '') === 'button' && !isset($triggerAttrs['type'])) {
                    $triggerAttrs['type'] = 'button';
                }
                $ddOpts['trigger_attrs'] = $triggerAttrs;

                echo \Components\Dropdown::render($ddOpts);
            } else {
                // Fallback: simple non-dropdown button retaining previous appearance
                echo '<button type="button" class="px-2 py-1.5 rounded-md hover:bg-gray-100">⋯</button>';
            }
            ?>

            <?php if (($options['context'] ?? '') === 'main' && !empty($options['styling_url'])): ?>
                <?php
                echo \Components\Button::render(\mh_lng('BUTTON_STYLING', 'edit styling'), [
                        'variant' => 'primary',
                        'size' => 'sm',
                        'attrs' => [
                                'command' => 'show-modal',
                                'commandfor' => 'editor-inline-modal',
                                'type' => 'legacy',

                                'data-title' => \mh_lng('BUTTON_STYLING', 'edit styling'),
                                'data-size' => 'full-margin',
                                'data-url' => $options['styling_url'],
                                'hx-target' => '#editor-inline-modal-body',
                                'hx-swap' => 'innerHTML',
                        ],
                ]);
                ?>
            <?php endif; ?>

            <?php if (in_array($options['context'] ?? '', ['newsletter', 'automation'])): ?>
                <?php
                $buttonOptions = [
                        'variant' => 'primary',
                        'size' => 'sm',
                        'attrs' => [
                                '@click' => 'toggleLock()',
//                                ':class' => "locked ? 'bg-green-100 text-green-700 hover:bg-green-200 border-2 border-green-300 opacity-100' : 'bg-orange-50 text-orange-600 hover:bg-orange-100 border-2 border-orange-500 opacity-100'",
                                'x-text' => "locked ? '" . mh_lng('BUTTON_NEWSLETTER_UNLOCK', 'edit') . "' : '" . mh_lng('BUTTON_NEWSLETTER_LOCK', 'Set active') . "'"
                        ],
                ];
                echo \Components\Button::render('', $buttonOptions);
                ?>
            <?php endif; ?>
        </div>
        <?php
        return ob_get_clean();
    }

    public static function render(array $options = [], array $langInfos = []): string
    {
        // Resolve page mode and config
        $pageConfig = $options['page_config'] ?? null;
        if (!$pageConfig instanceof PageConfig) {
            $pageConfig = new PageConfig($options['page_mode'] ?? PageConfig::fromRequest()->mode);
        }

        // Determine active language id
        $defaultLanguageId = (int)(\mh_session('languages_id', 1));
        $lngId = (int)($options['lng_id'] ?? $defaultLanguageId);

        // Normalize language infos: prefer provided $langInfos, else fall back to store languages
        $items = [];
        if (!empty($langInfos)) {
            foreach ($langInfos as $li) {
                if (!is_array($li)) continue;
                $id = isset($li['id']) ? (int)$li['id'] : (int)($li['languages_id'] ?? 0);
                if ($id <= 0) continue;
                $text = isset($li['text']) ? (string)$li['text'] : (isset($li['code']) ? strtoupper((string)$li['code']) : ('#' . $id));
                $code = isset($li['code']) ? (string)$li['code'] : (string)($li['languages_code'] ?? '');
                $items[] = [
                        'id' => $id,
                        'text' => $text,
                        'code' => $code,
                        'visualEditorUrl' => isset($li['visualEditorUrl']) ? (string)$li['visualEditorUrl'] : '',
                        'autosaveUrl' => isset($li['autosaveUrl']) ? (string)$li['autosaveUrl'] : '',
                        'subject' => isset($li['subject']) ? (string)$li['subject'] : '',
                        'preheader' => isset($li['preheader']) ? (string)$li['preheader'] : '',
                ];
            }
        }
        if (empty($items)) {
            $langItems = [];
            if (function_exists('mh_get_language_list')) {
                try {
                    $langItems = (array)\mh_get_language_list();
                } catch (\Throwable $e) {
                    $langItems = [];
                }
            }
            foreach ($langItems as $li) {
                if (!is_array($li)) continue;
                $id = isset($li['id']) ? (int)$li['id'] : (int)($li['languages_id'] ?? 0);
                if ($id <= 0) continue;
                $text = isset($li['text']) ? (string)$li['text'] : (isset($li['code']) ? strtoupper((string)$li['code']) : ('#' . $id));
                $code = isset($li['code']) ? (string)$li['code'] : (string)($li['languages_code'] ?? '');
                $items[] = [
                        'id' => $id,
                        'text' => $text,
                        'code' => $code,
                        'visualEditorUrl' => (string)($options['url'] ?? ''),
                        'autosaveUrl' => '',
                        'subject' => '',
                        'preheader' => '',
                ];
            }
            if (empty($items) && $defaultLanguageId > 0) {
                $items[] = [
                        'id' => $defaultLanguageId,
                        'text' => '#' . $defaultLanguageId,
                        'code' => (string)\mh_cfg('DEFAULT_LANGUAGE_CODE', ''),
                        'visualEditorUrl' => (string)($options['url'] ?? ''),
                        'autosaveUrl' => '',
                        'subject' => '',
                        'preheader' => '',
                ];
            }
        }

        // Optional: additional actions dropdown configuration for the toolbar
        // Shape matches \Components\Dropdown::render() options. Example:
        //   'actions_dropdown' => [
        //       'items' => [
        //           ['label' => 'Preview', 'href' => '...'],
        //           ['label' => 'Send test', 'attrs' => ['hx-post' => '...']],
        //       ],
        //       // optional trigger_* overrides
        //   ]
        $actionsDropdown = [];
        if (!empty($options['actions_dropdown']) && is_array($options['actions_dropdown'])) {
            $actionsDropdown = $options['actions_dropdown'];
        }

        $emojiEnabled = mh_cfg('MAILBEEZ_EMOJI_SUPPORT') && in_array(mh_cfg('MH_EDITOR_CONTEXT'), ['module', 'newsletter', 'automation', 'main']);

        // Shared device presets for the editor: used for toolbar buttons
        // and max-width calculations in the inline JS controller.
        $editorDevices = Devices::editorDevices();
        $editorWidths = [];
        $firstEditorId = null;
        foreach ($editorDevices as $devCfg) {
            if (!is_array($devCfg)) {
                continue;
            }
            $id = isset($devCfg['id']) ? (string)$devCfg['id'] : '';
            if ($id === '') {
                continue;
            }
            if ($firstEditorId === null) {
                $firstEditorId = $id;
            }
            $editorWidths[$id] = Devices::editorWidth($id);
        }

        // Default view: prefer desktop when present, otherwise the first
        // configured editor device.
        $defaultEditorView = 'desktop';
        if (!isset($editorWidths[$defaultEditorView])) {
            $defaultEditorView = $firstEditorId ?: 'desktop';
        }

        $editorWidthsJson = json_encode($editorWidths);

        // Optional: when rendered inside a BeezUI modal, allow the editor
        // toolbar areas to be teleported into the modal header containers so
        // that the modal body can focus on the editor canvas.
        $modalId = isset($options['modal_id']) && $options['modal_id'] !== ''
                ? (string)$options['modal_id']
                : '';
        $headerToolbarId = $modalId !== '' ? $modalId . '-header-toolbar' : '';
        $headerToolbarRightId = $modalId !== '' ? $modalId . '-header-toolbar-right' : '';

        // Render the shared toolbar HTML once, then either keep it in-place
        // (standalone editor page) or teleport the individual areas into the
        // modal header using Alpine's x-teleport, mirroring the
        // EmailPreviewer behaviour.

        $teleport = $headerToolbarId !== '';

        $moduleName = isset($options['module']) ? (string)$options['module'] : '';
        $dateRangeText = (string)($options['date_range_text'] ?? '');
        $scheduleEditUrl = (string)($options['schedule_edit_url'] ?? '');
        $toPlaceholder = (string)($options['to_placeholder'] ?? ($options['details']['to_placeholder'] ?? ''));
        $toPlaceholderOptions = $options['to_placeholder_options'] ?? [];
        $fromPlaceholder = (string)($options['from_placeholder'] ?? ($options['details']['from_placeholder'] ?? ''));

        $toolbarHtml = self::renderTopBar($items, $lngId, $editorDevices, $actionsDropdown, $teleport, $moduleName, $options, $pageConfig);

        // Individual area fragments used for teleporting when inside a modal.
        $statusHtml = self::renderStatusBlock();
        $tabsHtml = self::renderLanguageTabs($items, $lngId);
        $toolsHtml = self::renderTools($editorDevices, $actionsDropdown, $moduleName, $options, $pageConfig);

        // Prepare subjects map for Alpine root state
        $subjectsMap = [];
        foreach ($items as $item) {
            $val = (string)($item['subject'] ?? '');
            // Normalize subject: decode entities and strip legacy slashes before JSON encoding
            $subjectsMap[(int)$item['id']] = stripslashes(html_entity_decode($val, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
        }
        $subjectsJson = json_encode($subjectsMap);

        ob_start();
        ?>

        <div id="email-editor-root" class="h-screen flex flex-col "
             x-on:<?= $pageConfig->getEventName('newsletter-locked') ?>.window="locked = true"
             x-on:<?= $pageConfig->getEventName('newsletter-unlocked') ?>.window="locked = false"
             x-data="{
                detailsOpen: true,
                locked: <?= ($options['newsletter_status_locked'] ?? false) ? 'true' : 'false' ?>,
                undoRunning: false,
                redoRunning: false,
                modalId: <?= Base::h(json_encode($modalId), false) ?>,
                activeLngId: <?= (int)$lngId ?>,
                hasSubject: <?= (($options['context'] ?? '') !== 'main') ? 'true' : 'false' ?>,
                subjects: <?= Base::h($subjectsJson, false) ?>,
                focusedSubjectId: null,
                activeGnd: 'unknown',
                loadedGnd: {},
                iframesLoaded: {},
                iframeHeights: {},
                editorWidths: <?= Base::h($editorWidthsJson, false) ?>,

                <?= \Components\Ui\ScaleControl::jsData() ?>,

                get editorStyle() {
                    const s = Number(this.scale) || 1.0;
                    const rawMax = (this.device && this.editorWidths[this.device]) ? this.editorWidths[this.device] : 1024;
                    const targetMax = parseInt(rawMax, 10) || 1024;

                    const h = this.iframeHeights[this.activeLngId] || 0;

                    const transitions = [
                        'transform 200ms ease-in-out',
                        'max-width 200ms ease-in-out',
                        'height 200ms ease-in-out',
                        'margin-bottom 200ms ease-in-out'
                    ];

                    let style = {
                        maxWidth: targetMax + 'px',
                        transform: `scale(${s})`,
                        transformOrigin: 'top center',
                        transition: transitions.join(', ')
                    };

                    if (h > 0) {
                        style.height = h + 'px';
                        if (s !== 1.0) {
                            style.marginBottom = ((s - 1) * h) + 'px';
                        }
                    } else {
                        style.height = '100vh';
                    }

                    return style;
                },

                init() {
                    this.$watch('activeLngId', (newVal) => {
                        if (this.loadedGnd[newVal] !== this.activeGnd) {
                            this.$dispatch('email_editor:refresh', { id: newVal });
                        }
                    });
                    document.addEventListener('htmx:afterRequest', () => {
                        this.undoRunning = false;
                        this.redoRunning = false;
                    });
                },
                titleUnlock: <?= Base::h(json_encode(\mh_lng('MAILBEEZ_NEWSLETTER_UNLOCK_WARNING_TITLE', 'Important')), false) ?>,
                titleLock: <?= Base::h(json_encode(\mh_lng('MAILBEEZ_NEWSLETTER_LOCK_NOTICE_TITLE', 'Information')), false) ?>,
                toggleLock() {
                    const url = <?= Base::h(json_encode($pageConfig->getBaseUrl('email_editor_lock_unlock_modal', [
                     'partial' => 1,
                     'newsletter_id' => (int)($options['newsletter_id'] ?? 0),
                     'campaign_id' => (int)($options['campaign_id'] ?? 0),
                     'flow_id' => (int)($options['flow_id'] ?? 0),
                     'is_follow_up' => ($options['newsletter_is_follow_up'] ?? false) ? 1 : 0,
             ])), false) ?> + '&locked=' + (this.locked ? 1 : 0);

                    const title = this.locked ? this.titleUnlock : this.titleLock;
                    const type = this.locked ? 'warning' : 'info';

                    const btn = document.createElement('button');
                    btn.setAttribute('commandfor', 'confirm-modal');
                    btn.setAttribute('data-title', title);
                    btn.setAttribute('data-type', type);
                    btn.setAttribute('data-size', 'lg');

                    window.MBModal.applyFromTrigger(btn);

                    window.htmx.ajax('GET', url, {
                        target: '#confirm-modal-body',
                        swap: 'innerHTML'
                    });
                },
                device: '<?= Base::h($defaultEditorView) ?>',
                setDevice(mode) {
                    this.device = mode;
                },
                doUndo(actionUrl) {
                    try {
                        if (!actionUrl || this.undoRunning || this.redoRunning) {
                            return;
                        }
                        if (typeof window === 'undefined' || !window.htmx || typeof window.htmx.ajax !== 'function') {
                            return;
                        }

                        this.undoRunning = true;
                        window.htmx.ajax('POST', actionUrl, {
                            target: '#autosave-status',
                            swap: 'innerHTML',
                            values: {
                                lng_id: String(this.activeLngId),
                            },
                            indicator: '#autosave-indicator',
                            headers: {
                                'X-Requested-With': 'XMLHttpRequest'
                            }
                        });
                    } catch (e) {
                        this.undoRunning = false;
                    }
                },
                doRedo(actionUrl) {
                    try {
                        if (!actionUrl || this.undoRunning || this.redoRunning) {
                            return;
                        }
                        if (typeof window === 'undefined' || !window.htmx || typeof window.htmx.ajax !== 'function') {
                            return;
                        }

                        this.redoRunning = true;
                        window.htmx.ajax('POST', actionUrl, {
                            target: '#autosave-status',
                            swap: 'innerHTML',
                            values: {
                                lng_id: String(this.activeLngId),
                            },
                            indicator: '#autosave-indicator',
                            headers: {
                                'X-Requested-With': 'XMLHttpRequest'
                            }
                        });
                    } catch (e) {
                        this.redoRunning = false;
                    }
                },
            }"
        >
            <?php if (in_array($options['context'] ?? '', ['newsletter', 'automation']) && ($options['newsletter_id'] ?? 0) > 0): ?>
                <span hx-get="<?= Base::h($pageConfig->getBaseUrl('email_editor', ['newsletter_id' => (int)($options['newsletter_id'] ?? 0), 'partial' => 'modal_title', 'scroll_hints' => 'false']), false) ?>"
                      hx-trigger="<?= $pageConfig->getEventName('newsletter_item:updated') ?> from:body"
                      hx-target="#<?= ($modalId !== '' ? Base::h($modalId) : \Components\Support\Htmx::resolvePanelId()) ?>-title"
                      hx-swap="innerHTML"
                      class="hidden">
            </span>
            <?php endif; ?>
            <?php
            // Teleport toolbar areas into modal header when a header toolbar target is
            // available. Otherwise, render the combined header directly inside the
            // editor shell.
            if ($teleport) {
                // Move the status block ("left") right after the modal title by
                // teleporting it into the modal header extras container, while the
                // language tabs (center) and tools/actions (right) are placed in the
                // dedicated toolbar slots in the modal header.
                $headerExtrasId = $modalId !== '' ? $modalId . '-header-extras' : '';
                if ($headerExtrasId !== '') {
                    echo '<template x-teleport="#' . Base::h($headerExtrasId) . '">';
                    echo $statusHtml;
                    echo '</template>';
                }
                if ($headerToolbarId !== '') {
                    echo '<template x-teleport="#' . Base::h($headerToolbarId) . '">';
                    echo '<div class="flex items-center gap-4 min-w-0 overflow-hidden">';
                    echo $tabsHtml;
                    echo '</div>';
                    echo '</template>';
                }
                if ($headerToolbarRightId !== '') {
                    echo '<template x-teleport="#' . Base::h($headerToolbarRightId) . '">';
                    echo $toolsHtml;
                    echo '</template>';
                }
            } else {
                echo $toolbarHtml;
            }
            ?>

            <!-- Main -->
            <div class="flex flex-1 gap-2">

                <!-- Context / sidebar -->
                <!--
                                <aside class="w-72 shrink-0 overflow-y-auto h-100vh">
                                    <div class="h-full rounded-lg border border-divider bg-elevated flex items-center justify-center text-sm text-muted-foreground">
                                        Context section<br>placeholder
                                    </div>
                                </aside>
                -->

                <!-- Content -->
                <main class="flex-1 rounded-lg border border-divider bg-elevated mb-2">

                    <!-- Collapsible email details (one section per language, toggled with Alpine) -->
                    <?php foreach ($items as $info): $id = (int)$info['id']; ?>
                        <section class="" x-show="activeLngId === <?= $id ?>" x-cloak>
                            <div class="flex items-center justify-between px-6 py-2 border-b border-divider text-sm cursor-pointer"
                                 @click="detailsOpen = !detailsOpen" :aria-expanded="detailsOpen">
                                <?php
                                $labelText = isset($info['text']) ? (string)$info['text'] : ('#' . $id);
                                $code = isset($info['code']) ? (string)$info['code'] : '';
                                $flag = \Components\Form\BaseField::flagIconHtml($code, 'mr-1');
                                ?>
                                <div class="font-medium flex items-center gap-4">
                                    Email details
                                    <div x-show="hasSubject && !subjects[<?= $id ?>]" x-cloak>
                                        <?= self::showSubjectRequiredWarning(); ?>
                                    </div>
                                    <?= self::renderPreviewControls($id) ?>
                                </div>

                                <!-- Toggle chevrons -->
                                <?php
                                echo \Components\Button::render('', [
                                        'variant' => 'ghost',
                                        'size' => 'md',
                                        'icon' =>
                                                '<span x-cloak x-show="detailsOpen">' . Base::SVG_CHEVRON_UP . '</span>' .
                                                '<span x-show="!detailsOpen">' . Base::SVG_CHEVRON_DOWN . '</span>',
                                        'attrs' => [
                                                'aria-label' => 'Toggle email details',
                                        ],
                                ]);
                                ?>
                            </div>

                            <div class="grid grid-cols-6 auto-rows-min px-6 py-1 text-sm border-b border-divider"
                                 x-show="detailsOpen" x-cloak>

                                <?php if ($dateRangeText !== ''): ?>
                                    <!-- DateRange -->
                                    <div class="flex items-center text-on-muted h-10"><?= mh_lng('MAILBEEZ_EDITOR_DATERANGE', 'Date Range') ?>
                                        :
                                    </div>
                                    <div class="col-span-5 flex items-center h-10 px-2 text-secondary">
                                        <div class="flex items-center gap-2 group/daterange">
                                            <span hx-get="<?= Base::h($pageConfig->getBaseUrl('email_editor', ['newsletter_id' => (int)($options['newsletter_id'] ?? 0), 'partial' => 'date_range', 'scroll_hints' => 'false']), false) ?>"
                                                  hx-trigger="<?= $pageConfig->getEventName('newsletter_item:updated') ?> from:body, <?= $pageConfig->getEventName('newsletter_item:expandable:refresh') ?> from:body"
                                                  hx-target="this"
                                                  hx-swap="innerHTML">
                                                <?= Base::h($dateRangeText) ?>
                                            </span>
                                            <?php if ($scheduleEditUrl !== ''): ?>
                                                <button type="button"
                                                        @click.stop
                                                        class="opacity-0 group-hover/daterange:opacity-100 transition-opacity p-1 hover:bg-muted rounded text-muted-foreground hover:text-foreground"
                                                        command="show-modal"
                                                        commandfor="editor-inline-modal"
                                                        data-title="<?= Base::h(mh_lng('MAILBEEZ_EDIT_SCHEDULE', 'Edit Schedule'), false) ?>"
                                                        data-size="4xl-sm"
                                                        hx-get="<?= Base::h($scheduleEditUrl, false) ?>"
                                                        hx-target="#editor-inline-modal-body"
                                                        hx-swap="innerHTML"
                                                        data-no-global-loader="1"
                                                        data-no-local-loader="1"
                                                        :disabled="locked"
                                                        :class="locked ? 'cursor-not-allowed' : ''"
                                                        title="<?= Base::h(mh_lng('MAILBEEZ_EDIT_SCHEDULE', 'Edit Schedule'), false) ?>">
                                                    <?= Base::SVG_EDIT ?>
                                                </button>
                                            <?php endif; ?>
                                        </div>
                                    </div>
                                <?php endif; ?>

                                <!-- To -->
                                <div class="flex items-center text-on-muted h-10 <?php if ($dateRangeText !== ''): ?>border-t border-divider<?php endif; ?>"><?= mh_lng('MAILBEEZ_EDITOR_TO', 'To') ?>
                                    :
                                </div>
                                <div class="col-span-5 flex items-center h-10 px-2 text-secondary <?php if ($dateRangeText !== ''): ?>border-t border-divider<?php endif; ?>">
                                    <?php if (!empty($toPlaceholderOptions)): ?>
                                        <?php
                                        $toItems = [];
                                        foreach ($toPlaceholderOptions as $opt) {
                                            $toItems[] = [
                                                    'label' => $opt['label'],
                                                    'attrs' => [
                                                            'hx-get' => $opt['url'],
                                                            'hx-target' => $modalId !== '' ? '#' . $modalId . '-body' : 'body',
                                                            'hx-swap' => 'innerHTML',
                                                            'class' => $opt['active'] ? 'font-bold bg-gray-50 dark:bg-white/5' : null
                                                    ]
                                            ];
                                        }
                                        echo \Components\Dropdown::render([
                                                'items' => $toItems,
                                                'trigger_html' => '<span>' . Base::h($toPlaceholder) . '</span><svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="-ml-1 size-5 text-gray-400 dark:text-gray-500"><path d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd"></path></svg>',
                                                'trigger_attrs' => [
                                                        'class' => 'inline-flex items-center gap-x-1.5 text-secondary font-semibold hover:text-foreground focus:outline-none',
                                                        '@click.stop' => 'null'
                                                ]
                                        ]);
                                        ?>
                                    <?php else: ?>
                                        <span hx-get="<?= Base::h($pageConfig->getBaseUrl('email_editor', ['newsletter_id' => (int)($options['newsletter_id'] ?? 0), 'partial' => 'to_placeholder', 'scroll_hints' => 'false']), false) ?>"
                                              hx-trigger="<?= $pageConfig->getEventName('newsletter_item:updated') ?> from:body"
                                              hx-target="this"
                                              hx-swap="innerHTML">
                                            <?= Base::h($toPlaceholder) ?>
                                        </span>
                                    <?php endif; ?>
                                </div>

                                <?php if (($options['context'] ?? '') !== 'main'): ?>
                                    <!-- Subject -->
                                    <div class="flex items-center text-on-muted h-10 border-t border-divider">
                                        <?php if ($flag !== ''): ?>
                                            <span class="inline-flex items-center gap-1">
                                            <?= $flag ?>
                                        </span>
                                        <?php else: ?>
                                            <!--
                                        <?= Base::h($labelText) ?>
-->
                                        <?php endif; ?>
                                        <?= mh_lng('MAILBEEZ_EDITOR_SUBJECT', 'Subject') ?>:
                                    </div>
                                    <div class="col-span-5 h-10 border-t border-divider"
                                         <?php if ($emojiEnabled): ?>id="subject-row-<?= $id ?>"
                                         data-init="emoji-subject-<?= $id ?>"<?php endif; ?>>
                                        <div class="flex h-full items-center gap-2 group">
                                            <div class="flex-1 h-full flex items-center relative">
                                                <?php
                                                $subjectAttrs = [
                                                        ':disabled' => 'locked',
                                                        'type' => 'text',
                                                        'name' => 'lng_content_subject[' . $id . ']',
                                                        'id' => 'subject-' . $id,
                                                        'value' => isset($info['subject']) ? $info['subject'] : '',
                                                        'x-model' => 'subjects[' . $id . ']',
                                                        '@focus' => 'focusedSubjectId = ' . $id,
                                                        '@blur' => 'focusedSubjectId = null',
                                                        'class' => 'h-8 w-full rounded-md bg-transparent px-2
ring-1 ring-transparent
text-foreground placeholder:text-muted-foreground
focus:ring-2 focus:ring-brand focus:bg-background focus:outline-none',
                                                ];
                                                if (!empty($info['autosaveUrl'])) {
                                                    $subjectAttrs['hx-post'] = (string)$info['autosaveUrl'];
                                                    $subjectAttrs['hx-trigger'] = 'blur changed';
                                                    $subjectAttrs['data-no-global-loader'] = 1;
                                                    // Avoid showing modal/local overlays for lightweight autosave requests
                                                    $subjectAttrs['data-no-local-loader'] = 1;
                                                    $subjectAttrs['hx-target'] = '#autosave-status';
                                                    $subjectAttrs['hx-swap'] = 'innerHTML';
                                                    $subjectAttrs['hx-indicator'] = '#autosave-indicator';

                                                }
                                                echo '<input ' . Base::attrs($subjectAttrs) . ' />';
                                                ?>
                                                <div x-show="!subjects[<?= $id ?>] && focusedSubjectId !== <?= $id ?>"
                                                     x-cloak
                                                     class="absolute left-2 top-1/2 -translate-y-1/2 pointer-events-none">
                                                    <?= self::showSubjectRequiredWarning(); ?>
                                                </div>
                                            </div>

                                            <div class="flex items-center gap-1 opacity-0 pointer-events-none group-focus-within:opacity-100 group-focus-within:pointer-events-auto">
                                                <?php if ($emojiEnabled): ?>
                                                    <button type="button"
                                                            class="px-2 py-0.5 text-xs text-muted-foreground"
                                                            id="emoji-button-subject-<?= $id ?>">
                                                        <?= \mailbeez_editor::mh_icon_emoji(); ?>
                                                    </button>
                                                <?php endif;
                                                ?>
                                                <!--
                                                                                            <button class="rounded-full bg-muted px-2 py-0.5 text-xs text-on-muted hover:bg-muted/80">
                                                                                                First name
                                                                                            </button>
                                                                                            <button class="rounded-full bg-muted px-2 py-0.5 text-xs text-on-muted hover:bg-muted/80">
                                                                                                Country
                                                                                            </button>
                                                -->
                                            </div>
                                        </div>
                                    </div>

                                    <!-- Preview -->
                                    <div class="flex items-center text-on-muted h-10 border-t border-divider">
                                        <?php if ($flag !== ''): ?>
                                            <span class="inline-flex items-center gap-1">
                                            <?= $flag ?>
                                        </span>
                                        <?php else: ?>
                                            <!--
                                        <?= Base::h($labelText) ?>
-->
                                        <?php endif; ?>
                                        <?= mh_lng('MAILBEEZ_EDITOR_PREHEADER', 'Preview text') ?>:
                                    </div>
                                    <div class="col-span-5 h-10 border-t border-divider"
                                         <?php if ($emojiEnabled): ?>id="preheader-row-<?= $id ?>"
                                         data-init="emoji-preheader-<?= $id ?>"<?php endif; ?>>
                                        <div class="flex h-full items-center gap-2 group">
                                            <div class="flex-1 h-full flex items-center">
                                                <?php
                                                $preAttrs = [
                                                        ':disabled' => 'locked',
                                                        'type' => 'text',
                                                        'name' => 'lng_content_preheader[' . $id . ']',
                                                        'id' => 'preheader-' . $id,
                                                        'value' => isset($info['preheader']) ? $info['preheader'] : '',
                                                        'maxlength' => '300',
                                                        'class' => 'h-8 w-full rounded-md bg-transparent px-2
ring-1 ring-transparent
text-foreground placeholder:text-muted-foreground
focus:ring-2 focus:ring-brand focus:bg-background focus:outline-none',
                                                ];
                                                if (!empty($info['autosaveUrl'])) {
                                                    $preAttrs['hx-post'] = (string)$info['autosaveUrl'];
                                                    $preAttrs['hx-trigger'] = 'blur changed';
                                                    $preAttrs['data-no-global-loader'] = 1;
                                                    // Avoid showing modal/local overlays for lightweight autosave requests
                                                    $preAttrs['data-no-local-loader'] = 1;
                                                    $preAttrs['hx-target'] = '#autosave-status';
                                                    $preAttrs['hx-swap'] = 'innerHTML';
                                                    $preAttrs['hx-indicator'] = '#autosave-indicator';
                                                }
                                                echo '<input ' . Base::attrs($preAttrs) . ' />';
                                                ?>

                                            </div>

                                            <div class="flex items-center gap-1 opacity-0 pointer-events-none group-focus-within:opacity-100 group-focus-within:pointer-events-auto">
                                                <?php if ($emojiEnabled): ?>
                                                    <button type="button"
                                                            class="emoji-button px-2 py-0.5 text-xs text-muted-foreground"
                                                            id="emoji-button-preheader-<?= $id ?>">
                                                        <?= \mailbeez_editor::mh_icon_emoji(); ?>
                                                    </button>
                                                <?php endif; ?>
                                            </div>
                                        </div>
                                    </div>
                                <?php endif; ?>

                                <!-- From -->
                                <div class="flex items-center text-on-muted h-10 border-t border-divider"><?= mh_lng('MAILBEEZ_EDITOR_FROM', 'From') ?>
                                    :
                                </div>
                                <div class="col-span-5 flex items-center h-10 px-2 text-secondary border-t border-divider">
                                    <?= $fromPlaceholder ?>
                                </div>
                            </div>
                        </section>
                    <?php endforeach; ?>

                    <!-- Editor area (one iframe per language so switching is instant) -->
                    <?php foreach ($items as $i => $info):
                        $id = (int)$info['id'];
                        $src = (string)($info['visualEditorUrl'] ?? '');
                        if ($src === '' && !empty($options['url'])) {
                            $src = (string)$options['url'];
                        } ?>
                        <section class="flex justify-center bg-canvas"
                                 x-show="activeLngId === <?= $id ?>"
                                 x-cloak>
                            <div class="w-full relative"
                                 data-email-editor-container="1"
                                 :style="editorStyle">

                                <?php if ($src !== ''): ?>
                                    <div class="absolute inset-0 transition-opacity duration-200 ease-out pointer-events-none"
                                         :class="iframesLoaded[<?= $id ?>] ? 'opacity-0' : 'opacity-100'">
                                        <?= Base::skeleton() ?>
                                    </div>

                                    <iframe
                                            x-init="setTimeout(() => {
                                                try {
                                                    var url = new URL($el.dataset.src, window.location.href);
                                                    if (activeGnd) url.searchParams.set('gnd', activeGnd);
                                                    $el.src = url.toString();
                                                    loadedGnd[<?= $id ?>] = activeGnd;
                                                } catch (e) {
                                                    $el.src = $el.dataset.src;
                                                }
                                            }, <?= $i * 500 ?>)"
                                            @load="iframesLoaded[<?= $id ?>] = true"
                                            class="transition-opacity duration-200 ease-out"
                                            :class="{
                                                'pointer-events-none opacity-80': locked,
                                                'invisible absolute inset-0 opacity-0 pointer-events-none': !iframesLoaded[<?= $id ?>],
                                                'opacity-100': iframesLoaded[<?= $id ?>] && !locked
                                            }"
                                            id="editor_<?= $id ?>"
                                            name="editor_<?= $id ?>"
                                            frameborder="0"
                                            scrolling="no"
                                            data-lng-id="<?= $id ?>"
                                            data-src="<?= Base::h($src, false) ?>"
                                            style="width:100%; height:100vh; border:0; display:block;"
                                    ></iframe>
                                <?php else: ?>
                                    <div class="p-6 text-sm text-muted-foreground">
                                        No editor URL provided for this language.
                                    </div>
                                <?php endif; ?>

                            </div>
                        </section>
                    <?php endforeach; ?>

                </main>
            </div>
        </div>

        <script>
            (function () {
                // One-time wiring: listen for cross-frame HTMX events asking the
                // EmailEditor to refresh the visual preview iframe for a given
                // language, and listen for resize messages coming from the
                // legacy visual editor iframes so we can auto-adjust their
                // height.
                //
                // Refresh events are dispatched via Components\Support\Htmx::parentRefresh()
                // with an event name like "email_editor:refresh" and detail
                // payload such as { lng_id: "2" }.
                //
                // Resize events are posted from the iframe via
                //   parent.postMessage({ height: document.body.scrollHeight, lng_id: '2' }, '*');
                // and are handled via a single, global, idempotent listener.
                try {
                    console.log('__mbEmailEditorRefreshInit__', window.__mbEmailEditorRefreshInit__);

                    function handleSaving(ev) {
                        try {
                            var indicator = document.getElementById('autosave-indicator');
                            if (indicator) {
                                indicator.classList.add('htmx-request');
                            }
                        } catch (e) {
                        }
                    }

                    function handleSaved(ev) {
                        try {
                            var indicator = document.getElementById('autosave-indicator');
                            if (indicator) {
                                indicator.classList.remove('htmx-request');
                            }

                            var status = document.getElementById('autosave-status');
                            if (status) {


                                var cache_timestamp_out = new Date();
                                var label = status.dataset.labelSaved || 'All changes saved';
                                // status.innerHTML = '<span x-data="{ date: new Date(\'' + cache_timestamp_out.toISOString() + '\')  }" x-timeago.seconds="date">' + label + '</span>';
                                status.innerHTML = label;
                            }
                        } catch (e) {
                        }
                    }

                    function handleRefresh(ev) {
                        console.log('handleRefresh', ev);
                        try {
                            // Signal "saved" immediately when a refresh is requested (e.g. from modal close)
                            handleSaved(ev);

                            var detail = ev && ev.detail ? ev.detail : {};
                            var lngId = detail && (detail.lng_id || detail.lngId || detail.id);

                            // lng_id=0 or all=true indicates all languages should be refreshed
                            var isAll = detail && (detail.all === true || detail.all === "true" || lngId === 0 || lngId === "0");
                            if (!isAll && !lngId && !detail.subjects && !detail.preheaders) return;

                            // When undo/redo or template selection provides updated subject/preheader
                            // in the event detail, propagate them into the corresponding input fields.
                            try {
                                if (detail && typeof detail === 'object') {
                                    // 1. Multiple languages (map of lng_id => value)
                                    if (detail.subjects && typeof detail.subjects === 'object') {
                                        for (var sId in detail.subjects) {
                                            var sVal = detail.subjects[sId];
                                            var sInput = document.getElementById('subject_' + sId) || document.getElementById('subject-' + sId);
                                            if (sInput && 'value' in sInput) {
                                                sInput.value = sVal;
                                                sInput.dispatchEvent(new Event('input'));
                                            }
                                        }
                                    }
                                    if (detail.preheaders && typeof detail.preheaders === 'object') {
                                        for (var pId in detail.preheaders) {
                                            var pVal = detail.preheaders[pId];
                                            var pInput = document.getElementById('preheader_' + pId) || document.getElementById('preheader-' + pId);
                                            if (pInput && 'value' in pInput) {
                                                pInput.value = pVal;
                                                pInput.dispatchEvent(new Event('input'));
                                            }
                                        }
                                    }

                                    // 2. Single language fallback (legacy undo/redo support)
                                    if (!isAll && lngId) {
                                        var subject = Object.prototype.hasOwnProperty.call(detail, 'subject') ? detail.subject : null;
                                        var preheader = Object.prototype.hasOwnProperty.call(detail, 'preheader') ? detail.preheader : null;

                                        if (typeof subject === 'string') {
                                            var subjInput = document.getElementById('subject_' + lngId) || document.getElementById('subject-' + lngId);
                                            if (subjInput && 'value' in subjInput) {
                                                subjInput.value = subject;
                                                subjInput.dispatchEvent(new Event('input'));
                                            }
                                        }

                                        if (typeof preheader === 'string') {
                                            var preInput = document.getElementById('preheader_' + lngId) || document.getElementById('preheader-' + lngId);
                                            if (preInput && 'value' in preInput) {
                                                preInput.value = preheader;
                                                preInput.dispatchEvent(new Event('input'));
                                            }
                                        }
                                    }
                                }
                            } catch (eHdr) { /* no-op: header update best-effort */
                            }

                            var iframes = isAll
                                ? document.querySelectorAll('iframe[id^="editor_"]')
                                : (lngId ? [document.getElementById('editor_' + lngId)] : []);

                            for (var i = 0; i < iframes.length; i++) {
                                var iframe = iframes[i];
                                if (!iframe || !iframe.getAttribute) continue;

                                var src = iframe.getAttribute('src');
                                if (!src) continue;

                                // Rebuild URL and append a cache-busting parameter so
                                // the latest template changes are reflected immediately.
                                try {
                                    var url;
                                    try {
                                        url = new URL(src, window.location && window.location.href ? window.location.href : undefined);
                                        url.searchParams.set('_rnd', String(Date.now()));

                                        // Ensure the latest gender from Alpine state is included
                                        try {
                                            var root = document.getElementById('email-editor-root');
                                            if (root && window.Alpine) {
                                                var gnd = Alpine.$data(root).activeGnd;
                                                var iframeLngId = iframe.getAttribute('data-lng-id') || lngId;
                                                if (gnd) {
                                                    url.searchParams.set('gnd', gnd);
                                                    if (iframeLngId) {
                                                        Alpine.$data(root).loadedGnd[iframeLngId] = gnd;
                                                    }
                                                }
                                                if (iframeLngId) {
                                                    Alpine.$data(root).iframesLoaded[iframeLngId] = false;
                                                }
                                            }
                                        } catch (eGnd) {
                                        }

                                        iframe.setAttribute('src', url.toString());
                                    } catch (eUrl) {
                                        // Fallback: simple query-string manipulation if URL() is not available
                                        var sep = src.indexOf('?') !== -1 ? '&' : '?';
                                        var rndStr = '_rnd=' + String(Date.now());

                                        // Try to include gender in fallback too
                                        var gndStr = '';
                                        try {
                                            var rootFallback = document.getElementById('email-editor-root');
                                            if (rootFallback && window.Alpine) {
                                                var dataFallback = Alpine.$data(rootFallback);
                                                var gndFallback = dataFallback.activeGnd;
                                                var iframeLngIdFallback = iframe.getAttribute('data-lng-id') || lngId;
                                                if (gndFallback) {
                                                    gndStr = '&gnd=' + gndFallback;
                                                    if (iframeLngIdFallback) {
                                                        dataFallback.loadedGnd[iframeLngIdFallback] = gndFallback;
                                                    }
                                                }
                                                if (iframeLngIdFallback) {
                                                    dataFallback.iframesLoaded[iframeLngIdFallback] = false;
                                                }
                                            }
                                        } catch (eGndFallback) {
                                        }

                                        iframe.setAttribute('src', src + sep + rndStr + gndStr);
                                    }
                                } catch (e2) { /* no-op */
                                }
                            }
                        } catch (eOuter) { /* no-op */
                        }
                    }

                    function handleResizeMessage(event) {
                        try {
                            if (!event) return;
                            var data = event.data;
                            if (!data) return;

                            // Accept either an object payload or a JSON string.
                            if (typeof data === 'string') {
                                try {
                                    data = JSON.parse(data);
                                } catch (eJson) {
                                    return;
                                }
                            }

                            if (!data || typeof data !== 'object') return;
                            if (typeof data.height === 'undefined') return;

                            var rawHeight = data.height;
                            var height = parseInt(rawHeight, 10);
                            if (!height || isNaN(height)) return;

                            var lngId = data.lng_id || data.lngId || data.id || null;
                            var iframe = null;

                            if (lngId !== null && lngId !== undefined && lngId !== '') {
                                iframe = document.getElementById('editor_' + lngId);
                            }

                            // Fallback: match by event.source when lng_id is
                            // not available or the element cannot be found.
                            if (!iframe && event.source && document && document.querySelectorAll) {
                                var iframes = document.querySelectorAll('iframe[id^="editor_"]');
                                for (var i = 0; i < iframes.length; i++) {
                                    try {
                                        if (iframes[i].contentWindow === event.source) {
                                            iframe = iframes[i];
                                            break;
                                        }
                                    } catch (eMatch) { /* no-op */
                                    }
                                }
                            }

                            if (!iframe) return;

                            if (!lngId) {
                                lngId = iframe.getAttribute('data-lng-id');
                            }

                            try {
                                iframe.style.height = String(height) + 'px';
                                var root = document.getElementById('email-editor-root');
                                if (root && window.Alpine && lngId) {
                                    Alpine.$data(root).iframeHeights[lngId] = height;
                                }
                            } catch (eSet) { /* no-op */
                            }
                        } catch (eOuterResize) { /* no-op */
                        }
                    }

                    // Install global listeners only once per window so that
                    // multiple EmailEditor instances (for different steps
                    // or HTMX swaps) can coexist while sharing the same
                    // refresh/resize handlers. The per-instance view toggle
                    // wiring is *not* guarded and runs on every render so
                    // that newly injected toolbars always receive the
                    // correct click handling.
                    if (!window.__mbEmailEditorRefreshInit__) {
                        window.__mbEmailEditorRefreshInit__ = true;

                        if (document && document.body && document.body.addEventListener) {
                            document.body.addEventListener('email_editor:refresh', handleRefresh);
                            document.body.addEventListener('email_editor:saving', handleSaving);
                            document.body.addEventListener('email_editor:saved', handleSaved);
                        }

                        if (window && window.addEventListener) {
                            window.addEventListener('message', handleResizeMessage, false);
                        }
                    }
                } catch (eInit) {
                    // Silently ignore; editor still works without auto-refresh
                    // or auto-resize when the hooks cannot be installed.
                }
            })();
        </script>
        <?php
        return ob_get_clean();
    }
}
