<?php
namespace Components\Resources;

use Components\Support\Palette;

/**
 * Generic, reusable list/table presentation for simple CRUD collections.
 * All inline HTML is captured with output buffering and returned as a string.
 *
 * Options:
 * - title: string heading (optional)
 * - items: array of associative arrays (each item at least has id/name; configurable via keys below)
 * - id_key: string key for id in items (default 'id')
 * - name_key: string key for name/title in items (default 'name')
 * - status_key: string key for status (optional)
 * - outer_attrs: string raw attributes to inject on the outermost container (e.g., id="..." hx-swap-oob="...")
 * - empty_text: string to show when items is empty
 * - panel_type: 'modal' | 'drawer' (default 'modal') — controls default targets for actions
 * - panel_id: element id of the modal/drawer to target (default 'config-modal' for modal, 'config-drawer' for drawer)
 * - panel_target_id: id of the content container inside the panel (default: panel_id + '-body')
 * - new_action: array with:
 *     - label: string button label
 *     - url: string href/hx-get URL (can contain {id} placeholder; unused for new)
 *     - attrs: array of extra attributes [key => value]
 *     - variant: 'primary'|'secondary' (affects classes)
 * - row_actions: array of actions like new_action but {id} in url will be replaced
 *     - For modal/drawer actions, attributes typically include [ 'command' => 'show-modal', 'commandfor' => 'config-modal'|'config-drawer', 'hx-target' => '#config-*-body', 'hx-swap' => 'innerHTML', 'hx-get' => 'URL' ]
 *     - You may also pass 'hx_get' key as shorthand for setting hx-get
 * - menu_model_class: string FQCN of a model that can provide CRUD menu items (optional)
 * - menu_self_route: string router name to include in menu item context (optional)
 * - menu_label: string label for the per-row dropdown trigger (default: UI_ACTIONS/Actions)
 *
 * Optional icon per row (before the name):
 * - icon (array, defaults for all rows) with keys:
 *     - svg: string raw SVG markup (required to show icon)
 *     - shape: 'round'|'square' (default 'round')
 *     - color: palette token (blue|indigo|orange|yellow|green|gray|purple|red|pink|brand) or Tailwind utilities
 *     - variant: 'tint'|'solid' (default 'tint') — tint uses bg-100 + text-600; solid uses bg-500 + text-white
 *     - size: 'sm'|'md' (default 'md')
 * - Alternatively, per-item overrides:
 *     - item['icon_svg'] or item['icon']['svg']
 *     - item['icon_shape'] or item['icon']['shape']
 *     - item['icon_color'] or item['icon']['color']
 *     - item['icon_variant'] or item['icon']['variant']
 *     - item['icon_size'] or item['icon']['size']
 */
class ResourceList
{
    public static function render(array $options): string
    {
        $title = (string)($options['title'] ?? '');
        $items = is_array($options['items'] ?? null) ? $options['items'] : [];
        $idKey = (string)($options['id_key'] ?? 'id');
        $nameKey = (string)($options['name_key'] ?? 'name');
        $statusKey = (string)($options['status_key'] ?? 'status');
        $outerAttrs = trim((string)($options['outer_attrs'] ?? ''));
        $emptyText = (string)($options['empty_text'] ?? 'No entries found.');
        $newAction = is_array($options['new_action'] ?? null) ? $options['new_action'] : null;
        $rowActions = is_array($options['row_actions'] ?? null) ? $options['row_actions'] : [];

        // Optional per-row menu provider (model-based)
        $menuModelClass = (string)($options['menu_model_class'] ?? ($options['model_class'] ?? ''));
        $menuSelfRoute = (string)($options['menu_self_route'] ?? ($options['self_route'] ?? ''));
        $menuLabel = (string)($options['menu_label'] ?? \mh_lng('UI_ACTIONS','Actions'));

        // Panel configuration (modal or drawer)
        $panelType = (string)($options['panel_type'] ?? 'modal');
        $panelId = (string)($options['panel_id'] ?? ($panelType === 'drawer' ? 'config-drawer' : 'config-modal'));
        $panelTargetId = (string)($options['panel_target_id'] ?? ($panelId . '-body'));

        ob_start();
        ?>
        <div <?= $outerAttrs; ?> class="rounded-lg border border-gray-200 bg-white p-4 dark:border-white/10 dark:bg-slate-900">
            <?= self::renderHeader($title, $newAction, $panelId, $panelTargetId); ?>
            <?php if (empty($items)): ?>
                <div class="text-sm text-muted"><?php echo \Components\Base::h($emptyText); ?></div>
            <?php else: ?>
                <div class="divide-y divide-gray-200 dark:divide-white/10">
                    <?php foreach ($items as $it): ?>
                        <?= self::renderItemRow(
                            $it,
                            $options,
                            $idKey,
                            $nameKey,
                            $statusKey,
                            $panelId,
                            $panelTargetId,
                            $menuModelClass,
                            $menuSelfRoute,
                            $menuLabel,
                            $rowActions
                        ); ?>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </div>
        <?php
        return ob_get_clean();
    }

    private static function btnClasses(string $variant): string
    {
        if ($variant === 'primary') {
            return 'inline-flex items-center rounded-md bg-primary px-2.5 py-1 text-xs font-medium text-white hover:bg-primary/90';
        }
        return 'inline-flex items-center rounded-md border border-gray-200 bg-white px-2.5 py-1 text-xs font-medium text-gray-700 hover:bg-gray-50 dark:border-white/10 dark:bg-slate-800 dark:text-gray-200';
    }

    private static function renderAttrs(array $attrs): string
    {
        $out = '';
        foreach ($attrs as $k => $v) {
            if ($v === null || $v === false) { continue; }
            if ($k === 'hx_get') { $k = 'hx-get'; }
            $out .= ' ' . htmlspecialchars((string)$k, ENT_QUOTES, 'UTF-8') . '="' . htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8') . '"';
        }
        return $out;
    }

    private static function addDrawerParam(string $url, string $panelId): string
    {
        // Append drawer=1 when targeting a drawer panel and param is absent
        $isDrawer = (substr($panelId, -6) === 'drawer');
        if (!$isDrawer) { return $url; }
        if ($url === '' || stripos($url, 'drawer=') !== false) { return $url; }
        $sep = (strpos($url, '?') === false) ? '?' : '&';
        return $url . $sep . 'drawer=1';
    }

    private static function normalizePanelAttrs(array $attrs, string $panelId, string $panelTargetId): array
    {
        if (!isset($attrs['command'])) { $attrs['command'] = 'show-modal'; }
        if (!isset($attrs['commandfor'])) { $attrs['commandfor'] = $panelId; }
        if (!isset($attrs['hx-target'])) { $attrs['hx-target'] = '#' . $panelTargetId; }
        if (!isset($attrs['hx-swap'])) { $attrs['hx-swap'] = 'innerHTML'; }
        // If hx-get already present and panel is a drawer, ensure drawer=1 is included
        if (isset($attrs['hx-get'])) {
            $attrs['hx-get'] = self::addDrawerParam((string)$attrs['hx-get'], $panelId);
        }
        return $attrs;
    }

    private static function renderHeader(string $title, ?array $newAction, string $panelId, string $panelTargetId): string
    {
        ob_start();
        ?>
        <div class="flex items-center justify-between mb-3">
            <div class="text-sm font-semibold text-gray-900 dark:text-gray-100"><?php echo \Components\Base::h($title); ?></div>
            <?php if ($newAction):
                $naAttrs = $newAction['attrs'] ?? [];
                $naAttrs = self::normalizePanelAttrs($naAttrs, $panelId, $panelTargetId);
                if (isset($newAction['url'])) { $naAttrs['hx-get'] = self::addDrawerParam((string)$newAction['url'], $panelId); }
                $variant = (string)($newAction['variant'] ?? 'primary');
                ?>
                <button type="button" class="inline-flex items-center rounded-md bg-primary px-3 py-1.5 text-xs font-medium text-white hover:bg-primary/90"<?= self::renderAttrs($naAttrs); ?>>
                    <?php echo \Components\Base::h((string)($newAction['label'] ?? 'New')); ?>
                </button>
            <?php endif; ?>
        </div>
        <?php
        return ob_get_clean();
    }

    private static function computeIconHtml(array $options, array $it): string
    {
        // Icon config (global defaults + per-item overrides)
        $iconOpt = $options['icon'] ?? null;
        $iconDefaults = is_array($iconOpt) ? $iconOpt : (is_string($iconOpt) ? ['svg' => $iconOpt] : []);
        $itIconOpt = $it['icon'] ?? null;
        $itIcon = is_array($itIconOpt) ? $itIconOpt : (is_string($itIconOpt) ? ['svg' => $itIconOpt] : []);
        $iconSvg = (string)($it['icon_svg'] ?? ($itIcon['svg'] ?? ($iconDefaults['svg'] ?? '')));
        $iconShape = strtolower((string)($it['icon_shape'] ?? ($itIcon['shape'] ?? ($iconDefaults['shape'] ?? 'round'))));
        $iconVariant = strtolower((string)($it['icon_variant'] ?? ($itIcon['variant'] ?? ($iconDefaults['variant'] ?? 'tint'))));
        $iconColor = (string)($it['icon_color'] ?? ($itIcon['color'] ?? ($iconDefaults['color'] ?? '')));
        $iconSize = strtolower((string)($it['icon_size'] ?? ($itIcon['size'] ?? ($iconDefaults['size'] ?? 'md'))));
        // Compute size classes
        $wrapSz = ($iconSize === 'sm') ? 'size-8 p-1.5' : 'size-10 p-2';
        $svgSz = ($iconSize === 'sm') ? 'size-6' : 'size-6';
        $shapeCls = ($iconShape === 'square') ? 'rounded-lg' : 'rounded-full';
        $baseWrapCls = (($iconShape === 'round') ? 'relative ' : '') . 'flex items-center justify-center shrink-0 ' . $shapeCls . ' ' . $wrapSz;
        if ($iconShape === 'square') { $baseWrapCls .= ' shadow-sm'; }
        // Resolve color classes
        $iconColor = trim($iconColor);
        $isUtility = $iconColor !== '' && (preg_match('/\b(bg|text|from|to|via|fill|stroke|ring|border)-/i', $iconColor) || strpos($iconColor, ' ') !== false);
        if ($isUtility) {
            $colorCls = $iconColor;
        } else {
            if ($iconVariant === 'solid') {
                $bgDot = Palette::dotBg($iconColor !== '' ? $iconColor : 'gray', 'gray');
                $colorCls = trim($bgDot . ' text-white');
            } else {
                $colorCls = Palette::classes($iconColor !== '' ? $iconColor : 'gray', 'gray');
            }
        }
        $iconWrapCls = trim($baseWrapCls . ' ' . $colorCls);
        // Inject a size class into the SVG if possible
        $iconSvgHtml = $iconSvg;
        if ($iconSvgHtml !== '') {
            if (strpos($iconSvgHtml, '<svg') !== false) {
                if (strpos($iconSvgHtml, 'class=') !== false) {
                    $iconSvgHtml = preg_replace('/class=\"([^\"]*)\"/i', 'class="$1 ' . htmlspecialchars($svgSz, ENT_QUOTES, 'UTF-8') . '"', $iconSvgHtml, 1);
                } else {
                    $iconSvgHtml = preg_replace('/<svg\b/i', '<svg class="' . htmlspecialchars($svgSz, ENT_QUOTES, 'UTF-8') . '"', $iconSvgHtml, 1);
                }
            }
        }
        if ($iconSvgHtml === '') { return ''; }
        return '<div class="' . htmlspecialchars($iconWrapCls, ENT_QUOTES, 'UTF-8') . '">' . $iconSvgHtml . '</div>';
    }

    private static function resolveMenuItems(string $menuModelClass, string $menuSelfRoute, int $id, array $it, string $panelId, string $panelTargetId): array
    {
        $menuItems = [];
        if ($menuModelClass !== '') {
            try {
                $ctx = [
                    'mode'      => 'list',
                    'id'        => $id,
                    'values'    => $it,
                    'selfRoute' => $menuSelfRoute,
                ];
                if (class_exists($menuModelClass)) {
                    if (method_exists($menuModelClass, 'getCrudMenuItems')) {
                        /** @phpstan-ignore-next-line */
                        $menu = $menuModelClass::getCrudMenuItems($ctx);
                        if (is_array($menu)) { $menuItems = $menu; }
                    } elseif (method_exists($menuModelClass, 'crudMenuItems')) {
                        /** @phpstan-ignore-next-line */
                        $menu = $menuModelClass::crudMenuItems($ctx);
                        if (is_array($menu)) { $menuItems = $menu; }
                    } else {
                        // Try instance method fallback if the model supports it
                        try {
                            $inst = new $menuModelClass($it);
                            if (method_exists($inst, 'getCrudMenuItems')) {
                                $menu = $inst->getCrudMenuItems($ctx);
                                if (is_array($menu)) { $menuItems = $menu; }
                            } else {
                                // Static property fallback
                                $rc = new \ReflectionClass($menuModelClass);
                                $defaults = $rc->getDefaultProperties();
                                $prop = $defaults['crudMenu'] ?? ($defaults['menu'] ?? null);
                                if (is_array($prop)) { $menuItems = $prop; }
                            }
                        } catch (\Throwable $e) { /* ignore */ }
                    }
                }
            } catch (\Throwable $e) { /* ignore */ }
        }

        if (!empty($menuItems)) {
            foreach ($menuItems as $miIdx => $mi) {
                if (!is_array($mi)) continue;
                $hrefI = isset($mi['href']) ? (string)$mi['href'] : '';
                $attrsI = is_array($mi['attrs'] ?? null) ? $mi['attrs'] : [];
                if ($hrefI === '') {
                    $attrsI = self::normalizePanelAttrs($attrsI, $panelId, $panelTargetId);
                }
                if (isset($attrsI['hx_get']) && !isset($attrsI['hx-get'])) {
                    $attrsI['hx-get'] = $attrsI['hx_get'];
                    unset($attrsI['hx_get']);
                }
                $menuItems[$miIdx]['attrs'] = $attrsI;
            }
        }
        return $menuItems;
    }

    private static function renderRowActions(array $rowActions, int $id, string $panelId, string $panelTargetId): string
    {
        ob_start();
        foreach ($rowActions as $action):
            $aAttrs = $action['attrs'] ?? [];
            if (isset($action['url'])) {
                $url = (string)$action['url'];
                $repl = (string)$id;
                $url = str_replace('{id}', $repl, $url);
                $url = str_ireplace('%7Bid%7D', $repl, $url);
                $aAttrs['hx-get'] = self::addDrawerParam($url, $panelId);
            }
            $aAttrs = self::normalizePanelAttrs($aAttrs, $panelId, $panelTargetId);
            $variant = (string)($action['variant'] ?? 'secondary');
            ?>
            <button type="button" class="<?php echo self::btnClasses($variant); ?>"<?= self::renderAttrs($aAttrs); ?>>
                <?php echo \Components\Base::h((string)($action['label'] ?? 'Action')); ?>
            </button>
            <?php
        endforeach;
        return ob_get_clean();
    }

    private static function renderItemRow(
        array $it,
        array $options,
        string $idKey,
        string $nameKey,
        string $statusKey,
        string $panelId,
        string $panelTargetId,
        string $menuModelClass,
        string $menuSelfRoute,
        string $menuLabel,
        array $rowActions
    ): string {
        $id = (int)($it[$idKey] ?? 0);
        $name = htmlspecialchars((string)($it[$nameKey] ?? ''), ENT_QUOTES, 'UTF-8');
        $status = (string)($it[$statusKey] ?? '');
        $isActive = ($status !== '');
        $iconHtmlChunk = self::computeIconHtml($options, $it);

        // Resolve model-defined CRUD menu items for this row
        $menuItems = self::resolveMenuItems($menuModelClass, $menuSelfRoute, $id, $it, $panelId, $panelTargetId);

        ob_start();
        ?>
        <div class="py-2 flex items-center justify-between gap-3">
            <div class="flex items-center gap-3 min-w-0">
                <?php echo $iconHtmlChunk; ?>
                <div class="min-w-0">
                    <div class="text-sm font-medium text-gray-900 dark:text-gray-100 truncate"><?php echo $name; ?></div>
                    <div class="text-xs text-gray-500 dark:text-gray-400">#<?php echo $id; ?><?php echo $isActive ? ' · Active' : ''; ?></div>
                </div>
            </div>
            <div class="flex items-center gap-2 shrink-0">
                <?php if (!empty($menuItems)) {
                    echo \Components\Dropdown::render([
                        'label' => $menuLabel,
                        'items' => $menuItems,
                        'trigger_attrs' => [
                            'class' => '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'
                        ],
                    ]);
                } ?>
                <?= self::renderRowActions($rowActions, $id, $panelId, $panelTargetId); ?>
            </div>
        </div>
        <?php
        return ob_get_clean();
    }
}
