<?php
// components/Dropdown.php
namespace Components;

/**
 * Reusable Dropdown builder using Tailwind Plus Elements (<el-dropdown>/<el-menu>).
 *
 * Options (all optional unless stated otherwise):
 * - items: list<array{
 *     label?: string,
 *     href?: string,
 *     attrs?: array<string, string|int|float|bool|null>,
 *     icon?: string,   // raw HTML SVG/emoji string rendered before label
 *     divider?: bool,  // when true, renders a visual separator line
 *   }>
 *   Note: when an item has `divider => true`, the optional `attrs` key applies to the divider element itself
 *   (e.g. ['class' => 'my-2']).
 * - trigger_html: string  // raw HTML content for the trigger element; if omitted, a default "Options" button is rendered
 * - trigger_tag: 'button'|'a'|'div' (default: 'button')
 * - trigger_attrs: array<string, string|int|float|bool|null>
 * - wrapper_class: string (default: 'inline-block')  // applied to <el-dropdown>
 * - wrapper_attrs: array<string, string|int|float|bool|null>  // extra attrs on <el-dropdown>
 * - anchor: string (default: 'bottom start')         // passed to <el-menu anchor="...">
 * - popover: string|bool (default: 'manual')         // value for popover attribute; true sets empty attribute
 * - menu_class: string (default: example-based classes)
 * - menu_attrs: array<string, string|int|float|bool|null>
 */
class Dropdown extends Base
{
    /** Render dropdown with provided options */
    public static function render(array $opts = []): string
    {
        $items = is_array($opts['items'] ?? null) ? $opts['items'] : [];

        // --- Clean up redundant dividers (leading, trailing, consecutive) ---
        $cleanedItems = [];
        $lastWasDivider = true; // Assume leading divider to skip it
        foreach ($items as $mi) {
            if (!is_array($mi)) {
                continue;
            }
            $isDivider = !empty($mi['divider']);
            if ($isDivider) {
                if (!$lastWasDivider) {
                    $cleanedItems[] = $mi;
                }
                $lastWasDivider = true;
            } else {
                $cleanedItems[] = $mi;
                $lastWasDivider = false;
            }
        }
        // Remove trailing divider
        if (!empty($cleanedItems) && !empty($cleanedItems[count($cleanedItems) - 1]['divider'])) {
            array_pop($cleanedItems);
        }
        $items = $cleanedItems;

        $triggerHtml = (string)($opts['trigger_html'] ?? '');
        $triggerTag = (string)($opts['trigger_tag'] ?? 'button');
        $triggerAttrs = is_array($opts['trigger_attrs'] ?? null) ? $opts['trigger_attrs'] : [];
        $wrapperClass = trim((string)($opts['wrapper_class'] ?? 'inline-block'));
        $wrapperAttrs = is_array($opts['wrapper_attrs'] ?? null) ? $opts['wrapper_attrs'] : [];
        $anchor = (string)($opts['anchor'] ?? 'bottom start');
        $popover = $opts['popover'] ?? 'manual';
        $menuClassDefault = 'min-w-32 origin-top-right py-1 rounded-2xl bg-white shadow-lg outline-1 outline-black/5 transition transition-discrete [--anchor-gap:--spacing(2)] data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in dark:divide-white/10 dark:bg-gray-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10';
        $menuClass = trim((string)($opts['menu_class'] ?? $menuClassDefault));
        $menuAttrs = is_array($opts['menu_attrs'] ?? null) ? $opts['menu_attrs'] : [];
        // Default classes for visual divider lines between items
        $dividerClass = trim((string)($opts['divider_class'] ?? 'my-1 h-px bg-divider'));

        // If no custom trigger HTML provided, render a default styled button like the example
        if ($triggerHtml === '') {
            $caret = '<svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="-mr-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>';
            $label = isset($opts['label']) ? self::h((string)$opts['label']) : 'Options';
            $defaultBtnCls = 'inline-flex w-full justify-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs inset-ring-1 inset-ring-gray-300 hover:bg-gray-50 dark:bg-white/10 dark:text-white dark:shadow-none dark:inset-ring-white/5 dark:hover:bg-white/20';
            $btnCls = $defaultBtnCls;
            if (!empty($triggerAttrs['class'])) {
                $btnCls .= ' ' . (string)$triggerAttrs['class'];
                unset($triggerAttrs['class']);
            }
            $triggerHtml = '<span>' . $label . '</span>' . $caret;
            // Ensure type attribute for button trigger
            if ($triggerTag === 'button' && !isset($triggerAttrs['type'])) {
                $triggerAttrs['type'] = 'button';
            }
            $triggerAttrs['class'] = $btnCls;
        }

        // Build menu items
        $groups = [];
        $currentGroup = '';
        $menuItemsHtml = '';
        foreach ($items as $idx => $mi) {
            if (!is_array($mi)) { continue; }
            if (isset($mi['content'])) {
                $menuItemsHtml .= (string)$mi['content'];
                continue;
            }
            if (!empty($mi['divider'])) {
                $attrsDiv = is_array($mi['attrs'] ?? null) ? $mi['attrs'] : [];
                $divCls = $dividerClass;
                if (!empty($attrsDiv['class'])) {
                    $divCls .= ' ' . (string)$attrsDiv['class'];
                    unset($attrsDiv['class']);
                }
                $attrsDiv['class'] = $divCls;
                $attrStrDiv = self::attrs($attrsDiv);
                // Use a plain div as a non-interactive visual separator, hidden from assistive tech
                $menuItemsHtml .= '<div ' . $attrStrDiv . ' aria-hidden="true"></div>';
                $currentGroup = '';
                continue;
            }
            $labelI = isset($mi['label']) ? self::h((string)$mi['label']) : '';
            $hrefI = isset($mi['href']) ? (string)$mi['href'] : '';
            $attrsI = is_array($mi['attrs'] ?? null) ? $mi['attrs'] : [];
            $iconI = isset($mi['icon']) && is_string($mi['icon']) ? (string)$mi['icon'] : '';
            $endIconI = isset($mi['end_icon']) && is_string($mi['end_icon']) ? (string)$mi['end_icon'] : '';

            // add a bit more space on the right for better visual balance
            $baseItemCls = 'group/item relative flex items-center pl-4 pr-0 py-2 text-left text-sm text-gray-700 focus:bg-gray-100 focus:text-gray-900 focus:outline-hidden dark:text-gray-300 dark:focus:bg-white/5 dark:focus:text-white';
            // Detect disabled/aria-disabled and reflect visually
            $isDisabled = false;
            if (array_key_exists('disabled', $attrsI)) {
                $d = $attrsI['disabled'];
                $isDisabled = ($d === true) || (is_string($d) && in_array(strtolower($d), ['1','true','disabled','yes'], true));
            }
            if (!$isDisabled && array_key_exists('aria-disabled', $attrsI)) {
                $ad = $attrsI['aria-disabled'];
                $isDisabled = ($ad === true) || (is_string($ad) && in_array(strtolower($ad), ['1','true','disabled','yes'], true));
            }
            if ($isDisabled) {
                // Make disabled state obvious in both light & dark themes
                // Align text color with icon color for consistency
                $baseItemCls .= '  cursor-not-allowed pointer-events-none text-gray-300! dark:text-gray-600! select-none';
                // Ensure anchors are not focusable/clickable when disabled
                $attrsI['tabindex'] = '-1';
                if (!isset($attrsI['aria-disabled'])) { $attrsI['aria-disabled'] = 'true'; }
            }
            if (!empty($attrsI['class'])) {
                $baseItemCls .= ' ' . (string)$attrsI['class'];
                unset($attrsI['class']);
            }
            // Default type for button items
            if ($hrefI === '' && !isset($attrsI['type'])) {
                $attrsI['type'] = 'button';
            }
            // Default ARIA semantics for menu items unless overridden
            if (!isset($attrsI['role'])) { $attrsI['role'] = 'menuitem'; }
            if (!isset($attrsI['tabindex'])) { $attrsI['tabindex'] = '-1'; }
            $attrStrI = self::attrs($attrsI);

            // Icon handling: wrap and use CSS for sizing/gray classes
            $iconHtml = '';
            if ($iconI !== '') {
                $iconCls = 'mr-2 size-4! text-gray-400 group-focus/item:text-gray-500 dark:text-gray-500 dark:group-focus/item:text-white';
                if ($isDisabled) {
                    // Desaturate icon further in disabled state
                    $iconCls = 'mr-2 size-4! text-gray-300 dark:text-gray-600';
                }
                $iconHtml = '<span class="' . self::h($iconCls . ' [&_svg]:size-4') . '">' . $iconI . '</span>';
            }

            $endIconHtml = '';
            if ($endIconI !== '') {
                $endIconCls = 'absolute right-3 size-4! text-gray-400 group-focus/item:text-gray-500 dark:text-gray-500 dark:group-focus/item:text-white';
                if ($isDisabled) {
                    $endIconCls = 'absolute right-3 size-4! text-gray-300 dark:text-gray-600';
                }
                $endIconHtml = '<span class="' . self::h($endIconCls . ' [&_svg]:size-4') . '">' . $endIconI . '</span>';
            }

            // onmousedown="event.stopPropagation(); this.click();"
            // necessary when using inside a modal

            if ($hrefI !== '') {
                // Allow passing extra attributes (e.g. target="_blank") for link items as well.
                $attrsA = $attrsI;
                $attrsA['href'] = $hrefI;
                $attrsA['class'] = $baseItemCls;

                // If this link is actually a JS action trigger (modal/HTMX), prevent default navigation
                // so we don't fire/cancel document requests while still letting the command run.
                $isActionLink = false;
                if (array_key_exists('command', $attrsA) || array_key_exists('commandfor', $attrsA)) {
                    $isActionLink = true;
                } else {
                    foreach ($attrsA as $k => $_v) {
                        if (is_string($k) && strncmp($k, 'hx-', 3) === 0) {
                            $isActionLink = true;
                            break;
                        }
                    }
                }

                $appendHandler = static function ($existing, string $add): string {
                    $existing = is_string($existing) ? trim($existing) : '';
                    if ($existing === '') {
                        return $add;
                    }
                    $existing = rtrim($existing);
                    if (substr($existing, -1) !== ';') {
                        $existing .= ';';
                    }
                    return $existing . ' ' . $add;
                };

                // Stop bubbling so parent containers (cards/rows) don't also handle the click.
                $attrsA['onpointerdown'] = $appendHandler($attrsA['onpointerdown'] ?? '', 'event.stopPropagation();');
                $attrsA['onmousedown'] = $appendHandler($attrsA['onmousedown'] ?? '', 'event.stopPropagation();');

                $clickJs = 'event.stopPropagation();';
                if ($isActionLink) {
                    // Allow modifier-click to keep native open-in-new-tab behavior.
                    $clickJs = 'if(!(event.metaKey||event.ctrlKey||event.shiftKey||event.altKey)) { event.preventDefault(); } ' . $clickJs;
                }
                $attrsA['onclick'] = $appendHandler($attrsA['onclick'] ?? '', $clickJs);

                $attrStrA = self::attrs($attrsA);
                $menuItemsHtml .= '<a ' . $attrStrA . '>' . $iconHtml . '<span class="flex-1 truncate pr-10">' . $labelI . '</span>' . $endIconHtml . '</a>';
            } else {
                // Ensure click does not bubble; default type is already set to button above
                $menuItemsHtml .= '<button ' . $attrStrI . ' class="w-full ' . self::h($baseItemCls) . '" onmousedown="event.stopPropagation();" onclick="event.stopPropagation();">' . $iconHtml . '<span class="flex-1 truncate pr-10">' . $labelI . '</span>' . $endIconHtml . '</button>';
            }
        }

        // Attributes for wrapper and menu
        $wrapperAttrStr = self::attrs(array_merge(['class' => $wrapperClass], $wrapperAttrs));
        $menuAttrs['anchor'] = $anchor;
        if ($popover === true) {
            $menuAttrs['popover'] = true;
        } elseif (is_string($popover) && $popover !== '') {
            $menuAttrs['popover'] = $popover;
        } else {
            // default: manual
            $menuAttrs['popover'] = 'manual';
        }
        // Merge class last to allow overrides via menu_attrs['class'] if provided
        $menuClassMerge = $menuClass;
        if (!empty($menuAttrs['class'])) {
            $menuClassMerge .= ' ' . (string)$menuAttrs['class'];
        }
        $menuAttrs['class'] = $menuClassMerge;
        $menuAttrStr = self::attrs($menuAttrs);

        // Build trigger element
        $triggerAttrStr = self::attrs($triggerAttrs);
        $triggerTag = in_array($triggerTag, ['a','button','div'], true) ? $triggerTag : 'button';

        return <<<HTML
<el-dropdown {$wrapperAttrStr}>
  <{$triggerTag}{$triggerAttrStr}>{$triggerHtml}</{$triggerTag}>
  <el-menu {$menuAttrStr}>
    {$menuItemsHtml}
  </el-menu>
</el-dropdown>
HTML;
    }
}
