<?php
// components/Form/BaseField.php
namespace Components\Form;

abstract class BaseField extends \Components\Base {
    protected static function helpHtml(?string $help, ?string $id = null): string {
        if (!$help || trim($help) === '') { return ''; }
        // Delegate to Base::h with limited HTML allowance so this behavior is shared across all outputs
        $allowedTags = '<br><br/><b><strong><i><em><u><a>';
        $safe = \Components\Base::h($help, ['allow_html' => true, 'allowed_tags' => $allowedTags]);
        // Strip tags for tooltip to avoid showing raw HTML tags in the title attribute
        $plain = \Components\Base::h(strip_tags((string)$help), false);
        $idAttr = $id ? ' id="' . self::h($id) . '"' : '';
        return <<<HTML
<p{$idAttr} class="mt-1 text-xs text-secondary/70 dark:text-secondary line-clamp-3" title="{$plain}">{$safe}</p>
HTML;
    }

    protected static function errorsHtml(array $errors): string {
        if (empty($errors)) return '';
        $errEsc = self::h(implode(' ', $errors));
        return <<<HTML
<p class="mt-1 text-sm text-red-600 dark:text-red-400">{$errEsc}</p>
HTML;
    }

    protected static function clientErrorHtml(string $name): string
    {
        $nameEsc = self::h($name);
        return <<<HTML
<p class="mt-1 text-sm/6 text-red-600 dark:text-red-400" x-show="typeof clientErrors !== 'undefined' && clientErrors && clientErrors['{$nameEsc}'] && clientErrors['{$nameEsc}'].length" x-text="(typeof clientErrors !== 'undefined' && clientErrors && clientErrors['{$nameEsc}'] && clientErrors['{$nameEsc}'].length) ? clientErrors['{$nameEsc}'][0] : ''"></p>
HTML;
    }

    public static function fieldBaseClass(bool $hasError): string {
        // Legacy helper retained for components that still use it
        return 'mt-1 block w-full rounded border ' . ($hasError ? 'border-red-300 focus:border-red-500' : 'border-gray-300 focus:border-brand');
    }

    public static function labelClass(string $extra = ''): string {
        return 'block text-sm font-medium text-gray-900 dark:text-white' . ($extra ? ' ' . $extra : '');
    }

    /**
     * Render a standard label with support for truncation and tooltips for long text.
     */
    public static function renderLabel(string $id, string $label, string $badge = '', array $opts = []): string {
        $labelEsc = self::h($label);
        $titleAttr = ' title="' . $labelEsc . '"';
        $extraClass = ($opts['label_class'] ?? '') . ' truncate';

        // In horizontal layout, add a small top padding to align label text with the text inside the input
        if (self::isHorizontalLayout($opts['layout'] ?? '') && empty($opts['skip_label_pt'])) {
            $extraClass .= ' md:pt-1.5';
        }

        $labelCls = self::labelClass($extraClass);
        $idAttr = $id ? ' for="' . self::h($id) . '"' : '';
        $tag = $id ? 'label' : 'span';
        return <<<HTML
<{$tag}{$idAttr} class="{$labelCls}"{$titleAttr}>{$labelEsc}{$badge}</{$tag}>
HTML;
    }

    public static function inputClass(bool $hasError = false, bool $isReadonly = false): string {
        $base = 'block w-full rounded-md py-1.5 text-base text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 sm:text-sm/6 dark:text-white dark:ring-white/10 dark:placeholder:text-gray-500';

        if ($isReadonly) {
            $base .= ' bg-gray-50 cursor-default dark:bg-white/5';
            $error = '';
        } else {
            $base .= ' bg-white dark:bg-white/5 focus:outline-2 focus:-outline-offset-2';
            $error = $hasError ? ' ring-red-300 focus:outline-red-600 dark:ring-red-500/40 dark:focus:outline-red-500' : ' focus:outline-brand dark:focus:outline-brand';
        }
        return $base . $error;
    }

    public static function groupClass(bool $hasError = false, bool $isReadonly = false): string {
        $base = 'flex min-w-0 items-center rounded-md px-3 ring-1 ring-inset ring-gray-300 dark:ring-white/10';

        if ($isReadonly) {
            $base .= ' bg-gray-50 cursor-default dark:bg-white/5';
            $error = '';
        } else {
            $base .= ' bg-white dark:bg-white/5 focus-within:outline-2 focus-within:-outline-offset-2';
            $error = $hasError ? ' ring-red-300 focus-within:outline-red-600 dark:ring-red-500/40 dark:focus-within:outline-red-500' : ' focus-within:outline-brand dark:focus-within:outline-brand-500';
        }
        return $base . $error;
    }

    public static function radioCardClass(): string {
        return 'group relative flex items-start gap-4 rounded-lg border border-gray-300 bg-white has-checked:bg-brand/5 p-4 has-checked:outline-2 has-checked:-outline-offset-2 has-checked:outline-brand has-focus-visible:outline-3 has-focus-visible:-outline-offset-1 has-disabled:border-gray-400 has-disabled:bg-gray-200 has-disabled:opacity-25 dark:border-white/10 dark:bg-gray-800/50 dark:has-checked:bg-brand/10 dark:has-checked:outline-brand dark:has-disabled:border-white/10 dark:has-disabled:bg-gray-800';
    }

    public static function cardClass(): string {
        return self::radioCardClass();
    }


    /**
     * Map a language code (e.g. en, en_US) to a flag-icons country code (e.g. gb, us)
     */
    protected static function flagCountryFromLang(string $code): ?string {
        $c = strtolower(trim($code));
        if ($c === '') return null;
        // If code contains region (en_US, pt-BR), prefer region part as the country code
        if (strpos($c, '-') !== false || strpos($c, '_') !== false) {
            $parts = preg_split('/[-_]/', $c);
            $region = end($parts);
            if ($region && preg_match('/^[a-z]{2}$/', $region)) return $region;
        }
        // Common language -> country fallbacks
        $map = [
            'en' => 'gb', // default to GB for generic English
            'english' => 'gb',
            'de' => 'de',
            'german' => 'de',
            'fr' => 'fr',
            'french' => 'fr',
            'es' => 'es',
            'spanish' => 'es',
            'it' => 'it',
            'italian' => 'it',
            'pt' => 'pt',
            'portuguese' => 'pt',
            'nl' => 'nl',
            'dutch' => 'nl',
            'da' => 'dk',
            'sv' => 'se',
            'fi' => 'fi',
            'no' => 'no',
            'nn' => 'no',
            'nb' => 'no',
            'pl' => 'pl',
            'cs' => 'cz',
            'sk' => 'sk',
            'ro' => 'ro',
            'hu' => 'hu',
            'ru' => 'ru',
            'uk' => 'ua',
            'tr' => 'tr',
            'el' => 'gr',
            'zh' => 'cn',
            'ja' => 'jp',
            'ko' => 'kr',
            'ar' => 'sa',
            'he' => 'il',
            'th' => 'th',
            'vi' => 'vn',
            'id' => 'id',
            'ms' => 'my', // Malay -> Malaysia
        ];
        if (isset($map[$c])) return $map[$c];
        // If code itself is a valid-looking 2-letter country, use it
        if (preg_match('/^[a-z]{2}$/', $c)) return $c;
        return null;
    }

    /**
     * Return span HTML for a flag icon given a language code. Returns empty string if not resolvable.
     *
     * @param string|null $langCode
     * @param string $extraClasses
     * @param string $type 'default' (3x4) or 'circle' (1x1 square)
     * @return string
     */
    public static function flagIconHtml(?string $langCode, string $extraClasses = '', string $type = 'default'): string {
        $cc = $langCode ? self::flagCountryFromLang($langCode) : null;
        if (!$cc) return '';

        $isCircle = $type === 'circle';
        // Always use fi and fi-<cc> as base. If circle, add fis modifier for 1x1 square
        $cls = 'fi fi-' . self::h($cc) . ($isCircle ? ' fis' : '') . ($extraClasses ? (' ' . trim($extraClasses)) : '');
        $rounding = $isCircle ? 'rounded-full' : 'rounded-xs';

        // Check if size or alignment are already provided in extraClasses
        $hasSize = preg_match('/\b(text|size)-/', $extraClasses);
        $hasAlign = preg_match('/\balign-/', $extraClasses);

        $defaultSize = $hasSize ? '' : ' text-sm';
        $defaultAlign = $hasAlign ? '' : ' align-[-0.125em]';

        // flag-icons scales with font-size; tailwind text-sm gives a nice size and align a bit with text
        return <<<HTML
<span class="{$cls}{$defaultSize}{$defaultAlign} {$rounding}"></span>
HTML;
    }
    /**
     * Check if the layout is a horizontal layout.
     *
     * @param string $layout
     * @return bool
     */
    public static function isHorizontalLayout(string $layout): bool {
        return in_array($layout, [
            'columns', 'label-wide', 'wide-label', 'columns-3', 'label-2/3',
            'label-3/4', 'columns-4', 'label-1/3', 'label-1/4'
        ]);
    }

    /**
     * Render a read-only value in a box that matches the style of form inputs (like RichSelect).
     * This ensures visual consistency between active selection triggers and locked preset values.
     */
    public static function renderValueBox(string $content, array $opts = []): string
    {
        $hasError = !empty($opts['errors']);
        $size = $opts['size'] ?? 'md';
        $isReadonly = $opts['readonly'] ?? false;
        $tag = $opts['tag'] ?? 'div';
        $attrs = $opts['attrs'] ?? '';

        $triggerClass = $opts['trigger_class'] ?? '';
        if ($triggerClass === '') {
            $triggerClass = self::inputClass($hasError, $isReadonly);
            $triggerClass = str_replace('block', 'flex items-center justify-between gap-x-2', $triggerClass);

            // Adjust padding for different sizes to match inputClass defaults (which are py-1.5)
            if ($size === 'xs') {
                $triggerClass = str_replace('py-1.5', 'py-0.5', $triggerClass);
            } elseif ($size === 'sm') {
                $triggerClass = str_replace('py-1.5', 'py-1', $triggerClass);
            } elseif ($size === 'lg') {
                $triggerClass = str_replace('py-1.5', 'py-2', $triggerClass);
            }

            if (!empty($opts['no_border'])) {
                $triggerClass = preg_replace('/(?:dark:)?ring-\S+/', '', $triggerClass);
                $triggerClass = str_replace(['shadow-sm', 'rounded-md'], '', $triggerClass);
            }
            if (!empty($opts['no_background'])) {
                $triggerClass = preg_replace('/(?:dark:)?bg-\S+/', '', $triggerClass);
                $triggerClass .= ' bg-transparent';
            }

            $triggerClass .= ' px-3';
            if (!$isReadonly) {
                $triggerClass .= ' hover:bg-gray-50 focus-visible:outline-2 focus-visible:outline-brand-500 dark:hover:bg-white/10';
            }
        }

        $rightContent = $opts['right_content'] ?? '';

        return <<<HTML
<{$tag} class="{$triggerClass}" {$attrs}>
  <span class="flex min-w-0 items-center gap-x-2 truncate">
    {$content}
  </span>
  {$rightContent}
</{$tag}>
HTML;
    }

    /**
     * Render compact i18n rows with default language visible and other languages collapsible.
     */
    protected static function renderCompactI18nRows(string $defaultRowHtml, string $otherRowsHtml, int $otherCount): string
    {
        if ($otherRowsHtml === '' || $otherCount <= 0) {
            return $defaultRowHtml;
        }

        $toggleLabelTpl = (string) self::lng('UI_COUPON_MODAL_OTHER_LANGUAGES_COUNT', 'Other languages (%d)');
        $toggleLabel = self::h(sprintf($toggleLabelTpl, $otherCount));
        $chevron = \Components\Base::SVG_CHEVRON_DOWN;

        return <<<HTML
<div x-data="{ expanded: false }" class="flex flex-col gap-2">
  {$defaultRowHtml}
  <div class="flex justify-end">
    <button type="button" @click="expanded = !expanded" class="inline-flex items-center gap-1 text-xs font-medium text-secondary/50 hover:text-secondary transition-colors">
      <span>{$toggleLabel}</span>
      <span class="inline-flex transition-transform duration-200" :class="expanded ? 'rotate-180' : ''">{$chevron}</span>
    </button>
  </div>
  <div x-show="expanded" x-collapse.duration.300ms x-cloak class="flex flex-col gap-2">
    {$otherRowsHtml}
  </div>
</div>
HTML;
    }

    /**
     * Common field container that supports both a standard vertical layout and
     * a horizontal layout controlled via layout option.
     *
     * Supported horizontal layouts:
     * - columns (default): 50/50 split
     * - label-wide (aliases: label-2/3, wide-label, columns-3): 2/3 label, 1/3 input
     * - label-3/4 (alias: columns-4): 3/4 label, 1/4 input
     * - label-1/3: 1/3 label, 2/3 input
     * - label-1/4: 1/4 label, 3/4 input
     *
     * Other layouts:
     * - short: vertical, 50% width on desktop
     * - vertical/full-width/stacked: standard vertical layout
     */
    public static function renderContainer(string $labelHtml, string $inputInner, string $helpHtml, string $errorHtml, string $clientErr, array $opts = []): string {
        if (!empty($opts['no_container'])) {
            return $inputInner . $helpHtml . $errorHtml . $clientErr;
        }

        $summaryHtml = $opts['summary_html'] ?? '';
        if ($summaryHtml !== '' && ($opts['is_locked_preset'] ?? false)) {
            $editLabel = \mh_lng('UI_EDIT', 'edit');
            $editButton = <<<HTML
<button type="button" @click="locked = false" class="text-sm font-semibold text-brand-600 hover:text-brand-500 dark:text-brand-400 dark:hover:text-brand-300 shrink-0">
  {$editLabel}
</button>
HTML;
            $boxedSummary = self::renderValueBox($summaryHtml, [
                'right_content' => $editButton,
                'readonly'      => true,
                'size'          => $opts['size'] ?? 'md',
                'no_border'     => true,
                'no_background' => true,
            ]);

            $inputInner = <<<HTML
<div x-data="{ locked: true }" class="w-full">
  <div x-show="locked">
    {$boxedSummary}
  </div>
  <div x-show="!locked" x-cloak>
    {$inputInner}
  </div>
</div>
HTML;
        }

        $layout = (string)($opts['layout'] ?? 'stacked');
        $containerClass = (string)($opts['container_class'] ?? '');
        $containerAttrs = (string)($opts['container_attrs'] ?? '');
        $noLabel = $opts['no_label'] ?? false;
        $inputWrapperClass = $noLabel ? '' : 'mt-2';
        $alignItems = (string)($opts['align_items'] ?? (self::isHorizontalLayout($layout) ? 'items-start' : 'items-center'));

        if (self::isHorizontalLayout($layout)) {
            // Horizontal layout: label + description in left col, input in right col.
            // Responsive: collapses to 1 column on small screens.
            $helpContainer = $helpHtml !== '' ? '<div class="mt-0.5">' . $helpHtml . '</div>' : '';

            if ($layout === 'label-3/4' || $layout === 'columns-4') {
                $colSpanLabel = 'md:col-span-3';
                $colSpanInput = 'md:col-span-1';
                $gridCols = 'md:grid-cols-4';
            } elseif ($layout === 'label-1/4') {
                $colSpanLabel = 'md:col-span-1';
                $colSpanInput = 'md:col-span-3';
                $gridCols = 'md:grid-cols-4';
            } elseif ($layout === 'label-wide' || $layout === 'wide-label' || $layout === 'columns-3' || $layout === 'label-2/3') {
                $colSpanLabel = 'md:col-span-2';
                $colSpanInput = 'md:col-span-1';
                $gridCols = 'md:grid-cols-3';
            } elseif ($layout === 'label-1/3') {
                $colSpanLabel = 'md:col-span-1';
                $colSpanInput = 'md:col-span-2';
                $gridCols = 'md:grid-cols-3';
            } else {
                $colSpanLabel = '';
                $colSpanInput = '';
                $gridCols = 'md:grid-cols-2';
            }

            return <<<HTML
<div class="{$containerClass} grid grid-cols-1 {$gridCols} gap-x-4 gap-y-1 {$alignItems}" {$containerAttrs}>
  <div class="min-w-0 {$colSpanLabel}">
    {$labelHtml}
    {$helpContainer}
  </div>
  <div class="{$inputWrapperClass} md:mt-0 min-w-0 {$colSpanInput}">
    {$inputInner}
    {$errorHtml}
    {$clientErr}
  </div>
</div>
HTML;
        }

        if ($layout === 'short') {
            // Vertical layout but only 50% width on md+ screens.
            return <<<HTML
<div class="{$containerClass} w-full md:w-1/2 min-w-0" {$containerAttrs}>
{$labelHtml}
<div class="{$inputWrapperClass}">
  {$inputInner}
</div>
{$helpHtml}
{$errorHtml}
{$clientErr}
</div>
HTML;
        }

        // Standard vertical layout: used for 'vertical', 'full-width', 'stacked', or fallback.
        return <<<HTML
<div class="{$containerClass} min-w-0" {$containerAttrs}>
{$labelHtml}
<div class="{$inputWrapperClass}">
  {$inputInner}
</div>
{$helpHtml}
{$errorHtml}
{$clientErr}
</div>
HTML;
    }
}
