<?php

namespace Components\Form;

use Components\Data\LegacyStoreData;

class CustomerGroupMultiple extends BaseField
{
    private const DEFAULT_DATA_SOURCE_CLASS = LegacyStoreData::class;
    private const DEFAULT_DATA_SOURCE_METHOD = 'getCustomerGroupData';

    /**
     * @return array<string, string>
     */
    public static function getOptions(array $opts = []): array
    {
        [$sourceClass, $sourceMethod] = static::resolveDataProvider($opts);

        $data = static::fetchProviderData($sourceClass, $sourceMethod);
        if ($data === null && ($sourceClass !== self::DEFAULT_DATA_SOURCE_CLASS || $sourceMethod !== self::DEFAULT_DATA_SOURCE_METHOD)) {
            $data = static::fetchProviderData(self::DEFAULT_DATA_SOURCE_CLASS, self::DEFAULT_DATA_SOURCE_METHOD);
        }

        if ($data === null) {
            return [];
        }

        return static::normalizeOptions($data);
    }

    /**
     * @param array<string, mixed> $opts
     * @return array{0:string,1:string}
     */
    protected static function resolveDataProvider(array $opts): array
    {
        $sourceClass = self::DEFAULT_DATA_SOURCE_CLASS;
        $sourceMethod = self::DEFAULT_DATA_SOURCE_METHOD;

        if (isset($opts['customer_group_provider']) && is_string($opts['customer_group_provider'])) {
            $provider = trim((string)$opts['customer_group_provider']);
            if ($provider !== '' && strpos($provider, '::') !== false) {
                [$providerClass, $providerMethod] = explode('::', $provider, 2);
                $providerClass = trim($providerClass);
                $providerMethod = trim($providerMethod);
                if ($providerClass !== '' && $providerMethod !== '') {
                    return [$providerClass, $providerMethod];
                }
            }
        }

        if (isset($opts['customer_group_source']) && is_string($opts['customer_group_source'])) {
            $candidateClass = trim((string)$opts['customer_group_source']);
            if ($candidateClass !== '') {
                $sourceClass = $candidateClass;
            }
        }

        if (isset($opts['customer_group_method']) && is_string($opts['customer_group_method'])) {
            $candidateMethod = trim((string)$opts['customer_group_method']);
            if ($candidateMethod !== '') {
                $sourceMethod = $candidateMethod;
            }
        }

        return [$sourceClass, $sourceMethod];
    }

    /**
     * @return array<int|string, mixed>|null
     */
    protected static function fetchProviderData(string $sourceClass, string $sourceMethod): ?array
    {
        if (!class_exists($sourceClass) || !is_callable([$sourceClass, $sourceMethod])) {
            return null;
        }

        try {
            $data = $sourceClass::$sourceMethod();
        } catch (\Throwable $e) {
            return null;
        }

        return is_array($data) ? $data : [];
    }

    /**
     * @param array<int|string, mixed> $data
     * @return array<string, string>
     */
    protected static function normalizeOptions(array $data): array
    {
        $options = [];

        foreach ($data as $key => $row) {
            if (is_array($row)) {
                $groupId = trim((string)($row['group_id'] ?? $row['id'] ?? $row['value'] ?? ''));
                $groupName = trim((string)($row['group_name'] ?? $row['name'] ?? $row['label'] ?? $row['text'] ?? ''));
                if ($groupId === '' && !is_int($key)) {
                    $groupId = trim((string)$key);
                }
                if ($groupId !== '' && $groupName !== '') {
                    $options[$groupId] = $groupName;
                }
                continue;
            }

            if (!is_scalar($row)) {
                continue;
            }

            $groupId = trim((string)$key);
            $groupName = trim((string)$row);
            if ($groupId === '' || $groupName === '') {
                continue;
            }
            $options[$groupId] = $groupName;
        }

        return $options;
    }

    public static function render(string $name, string $label, $value = [], array $opts = []): string
    {
        $errors = $opts['errors'] ?? [];
        $help = $opts['help'] ?? '';

        if (is_string($value)) {
            $value = array_filter($value === '' ? [] : array_map('trim', explode(',', $value)), 'strlen');
        }
        $selectedRaw = array_map('strval', is_array($value) ? $value : []);
        $selected = [];
        foreach ($selectedRaw as $selectedId) {
            if (!preg_match('/^\d+$/', $selectedId)) {
                continue;
            }
            $selected[] = (string)$selectedId;
        }

        $nameEsc = self::h($name);
        $modelField = (strlen($name) >= 2 && substr($name, -2) === '[]') ? substr($name, 0, -2) : $name;
        $modelFieldJson = json_encode((string)$modelField, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        if (!is_string($modelFieldJson) || $modelFieldJson === '') {
            $modelFieldJson = '""';
        }
        $uid = substr(md5($name . '|' . uniqid((string)mt_rand(), true)), 0, 12);
        $groupAttr = self::h('cgm-group-' . $uid);
        $allId = self::h('cgm-all-' . $uid);
        $allChecked = empty($selected) ? ' checked' : '';

        $items = static::getOptions($opts);
        $count = count($items);
        $scrollClass = $count > 5 ? ' max-h-48 overflow-y-auto pr-1' : '';
        $allTextEsc = self::h((string)mh_lng('TEXT_ALL', 'All'));

        $checkboxes = <<<HTML
<div class="flex items-center gap-2 py-1">
  <input id="{$allId}" type="checkbox" name="{$nameEsc}[]" value="__all__" class="rounded border-gray-300 text-brand focus:ring-brand"{$allChecked}/>
  <label for="{$allId}" class="text-sm font-medium text-gray-700">{$allTextEsc}</label>
</div>
HTML;
        if (empty($items)) {
            $checkboxes .= '<div class="text-sm opacity-70">' . static::h(mh_lng('TEXT_NONE', 'No items')) . '</div>';
        } else {
            foreach ($items as $id => $itemLabel) {
                $id = (string)$id;
                $idAttr = self::h(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $name . '-' . $id));
                $checked = in_array((string)$id, $selected, true) ? ' checked' : '';
                $valueEsc = self::h((string)$id);
                $textEsc = self::h((string)$itemLabel);
                $checkboxes .= <<<HTML
<div class="flex items-center gap-2 py-1">
  <input id="{$idAttr}" type="checkbox" name="{$nameEsc}[]" value="{$valueEsc}" data-cgm-group="{$groupAttr}" class="rounded border-gray-300 text-brand focus:ring-brand"{$checked}/>
  <label for="{$idAttr}" class="text-sm text-gray-700">{$textEsc}</label>
</div>
HTML;
            }
        }

        $script = <<<HTML
<script>
(function () {
  var allCheckbox = document.getElementById('{$allId}');
  if (!allCheckbox) return;
  var groupCheckboxes = document.querySelectorAll('input[type="checkbox"][data-cgm-group="{$groupAttr}"]');

  function anyGroupChecked() {
    for (var i = 0; i < groupCheckboxes.length; i++) {
      if (groupCheckboxes[i].checked) {
        return true;
      }
    }
    return false;
  }

  function syncAllFromGroups() {
    allCheckbox.checked = !anyGroupChecked();
  }

  function selectedValues() {
    var values = [];
    for (var i = 0; i < groupCheckboxes.length; i++) {
      if (!groupCheckboxes[i].checked) {
        continue;
      }
      var value = (groupCheckboxes[i].value || '').trim();
      if (value !== '') {
        values.push(value);
      }
    }
    return values;
  }

  function syncModel() {
    var detail = {};
    detail.name = {$modelFieldJson};
    detail.value = selectedValues();

    var ev;
    try {
      ev = new CustomEvent('mb-field-sync', { bubbles: true, detail: detail });
    } catch (e) {
      ev = document.createEvent('CustomEvent');
      ev.initCustomEvent('mb-field-sync', true, false, detail);
    }
    allCheckbox.dispatchEvent(ev);
  }

  for (var i = 0; i < groupCheckboxes.length; i++) {
    groupCheckboxes[i].addEventListener('change', function () {
      syncAllFromGroups();
      syncModel();
    });
  }

  allCheckbox.addEventListener('change', function () {
    if (allCheckbox.checked) {
      for (var i = 0; i < groupCheckboxes.length; i++) {
        groupCheckboxes[i].checked = false;
      }
      syncModel();
      return;
    }

    if (!anyGroupChecked()) {
      allCheckbox.checked = true;
    }

    syncModel();
  });

  syncAllFromGroups();
  syncModel();
})();
</script>
HTML;

        $helpHtml = self::helpHtml($help);
        $errorHtml = self::errorsHtml($errors);
        $clientErr = self::clientErrorHtml($name);
        $badge = $opts['label_badge_html'] ?? '';

        $labelHtml = '';
        if (!($opts['no_label'] ?? false)) {
            $labelHtml = self::renderLabel('', $label, $badge, $opts);
        }

        $inputInner = <<<HTML
<div class="rounded border border-gray-200 p-2{$scrollClass}">
  {$checkboxes}
</div>
{$script}
HTML;

        return self::renderContainer($labelHtml, $inputInner, $helpHtml, $errorHtml, $clientErr, $opts);
    }
}
