import { useMemo, useRef, useState } from '@wordpress/element';
import {
  BaseControl,
  Button,
  MenuGroup,
  MenuItem,
  NavigableMenu,
  Popover,
  SearchControl,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';

import type { CurrencyEntry } from '../../currencies';

type CurrencyRichSelectProps = {
  label: string;
  value: string;
  currencies: CurrencyEntry[];
  onChange: (code: string) => void;
  help?: string;
  compact?: boolean;
  availableCodes?: Set<string>;
};

type CurrencyMultiRichSelectProps = {
  label: string;
  values: string[];
  currencies: CurrencyEntry[];
  onChange: (codes: string[]) => void;
  help?: string;
  availableCodes?: Set<string>;
};

type GroupedCurrencies = {
  group: string;
  label: string;
  items: CurrencyEntry[];
};

function useGroupedCurrencies(
  currencies: CurrencyEntry[],
  query: string,
  groupOrder: string[],
  groupLabels: Record<string, string>
): GroupedCurrencies[] {
  const filtered = useMemo(() => {
    const normalizedQuery = query.trim().toLowerCase();
    if (normalizedQuery === '') {
      return currencies;
    }

    return currencies.filter((currency) => {
      return (
        currency.code.toLowerCase().includes(normalizedQuery) ||
        currency.name.toLowerCase().includes(normalizedQuery)
      );
    });
  }, [currencies, query]);

  return useMemo(() => {
    const map = new Map<string, CurrencyEntry[]>();
    for (const currency of filtered) {
      const list = map.get(currency.group) ?? [];
      list.push(currency);
      map.set(currency.group, list);
    }

    const orderedKeys: string[] = [];
    for (const key of groupOrder) {
      if (map.has(key)) {
        orderedKeys.push(key);
      }
    }

    for (const key of map.keys()) {
      if (!orderedKeys.includes(key)) {
        orderedKeys.push(key);
      }
    }

    return orderedKeys.map((group) => ({
      group,
      label: groupLabels[group] || group,
      items: map.get(group) ?? [],
    }));
  }, [filtered, groupLabels, groupOrder]);
}

export function CurrencyRichSelect({
  label,
  value,
  currencies,
  onChange,
  help,
  compact,
  availableCodes,
}: CurrencyRichSelectProps) {
  const [isOpen, setIsOpen] = useState(false);
  const [query, setQuery] = useState('');
  const buttonRef = useRef<HTMLButtonElement | null>(null);

  const ui = window.crtodayEditorCurrenciesUi ?? {};
  const groupLabels = ui.groupLabels ?? {};
  const groupOrder = ui.groupOrder ?? [];
  const flagBaseUrl = ui.flagBaseUrl ?? '';
  const labels = ui.labels ?? {};

  const selected = useMemo(
    () => currencies.find((currency) => currency.code === value),
    [currencies, value]
  );

  const grouped = useGroupedCurrencies(currencies, query, groupOrder, groupLabels);

  const selectedLabel = selected
    ? (compact ? selected.code : `${selected.code} — ${selected.name}`)
    : value;

  return (
    <BaseControl label={label} help={help}>
      <Button
        ref={buttonRef}
        variant="secondary"
        onClick={() => setIsOpen((open) => !open)}
        style={{ width: '100%', justifyContent: 'space-between' }}
        aria-expanded={isOpen}
      >
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: '8px', minWidth: 0 }}>
          {selected?.flag && flagBaseUrl ? (
            <img
              src={`${flagBaseUrl}${selected.flag}.webp`}
              alt=""
              width={18}
              height={18}
              style={{ borderRadius: '2px', objectFit: 'cover', flexShrink: 0 }}
            />
          ) : null}
          <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {selectedLabel}
          </span>
        </span>
        <span aria-hidden="true" style={{ marginLeft: '8px' }}>▾</span>
      </Button>

      {isOpen && buttonRef.current ? (
        <Popover
          anchor={buttonRef.current}
          onClose={() => setIsOpen(false)}
          placement="bottom-start"
          focusOnMount
          shift
        >
          <div style={{ width: '360px', maxWidth: 'min(90vw, 360px)', padding: '8px' }}>
            <SearchControl
              value={query}
              onChange={(next) => setQuery(next)}
              placeholder={labels.searchCurrency ?? ''}
              label={labels.searchCurrency ?? ''}
              hideLabelFromVision
            />

            <div style={{ maxHeight: '320px', overflow: 'auto', marginTop: '8px' }}>
              {grouped.length === 0 ? (
                <div style={{ padding: '8px', color: '#646970' }}>
                  {labels.noResults ?? ''}
                </div>
              ) : (
                <NavigableMenu orientation="vertical">
                  {grouped.map((group) => (
                    <MenuGroup key={group.group} label={group.label}>
                      {group.items.map((currency) => {
                        const isSelected = currency.code === value;
                        const isUnavailable = availableCodes != null && !availableCodes.has(currency.code);

                        return (
                          <MenuItem
                            key={currency.code}
                            disabled={isUnavailable}
                            onClick={() => {
                              if (isUnavailable) return;
                              onChange(currency.code);
                              setIsOpen(false);
                              setQuery('');
                            }}
                          >
                            <span
                              style={{
                                display: 'flex',
                                alignItems: 'center',
                                justifyContent: 'flex-start',
                                gap: '8px',
                                width: '100%',
                                textAlign: 'left',
                                opacity: isUnavailable ? 0.4 : 1,
                              }}
                            >
                              {currency.flag && flagBaseUrl ? (
                                <img
                                  src={`${flagBaseUrl}${currency.flag}.webp`}
                                  alt=""
                                  width={16}
                                  height={16}
                                  style={{ borderRadius: '2px', objectFit: 'cover', flexShrink: 0 }}
                                />
                              ) : null}
                              <span style={{ minWidth: 48, fontFamily: 'monospace', textAlign: 'left' }}>
                                {currency.code}
                              </span>
                              <span
                                style={{
                                  flex: '1 1 auto',
                                  minWidth: 0,
                                  textAlign: 'left',
                                }}
                              >
                                {currency.name}
                              </span>
                              {isSelected ? <span aria-hidden="true">✓</span> : null}
                            </span>
                          </MenuItem>
                        );
                      })}
                    </MenuGroup>
                  ))}
                </NavigableMenu>
              )}
            </div>
          </div>
        </Popover>
      ) : null}
    </BaseControl>
  );
}

export function CurrencyMultiRichSelect({
  label,
  values,
  currencies,
  onChange,
  help,
  availableCodes,
}: CurrencyMultiRichSelectProps) {
  const [isOpen, setIsOpen] = useState(false);
  const [query, setQuery] = useState('');
  const buttonRef = useRef<HTMLButtonElement | null>(null);

  const ui = window.crtodayEditorCurrenciesUi ?? {};
  const groupLabels = ui.groupLabels ?? {};
  const groupOrder = ui.groupOrder ?? [];
  const flagBaseUrl = ui.flagBaseUrl ?? '';
  const labels = ui.labels ?? {};

  const grouped = useGroupedCurrencies(currencies, query, groupOrder, groupLabels);
  const selectedSet = useMemo(() => new Set(values), [values]);

  const selectedLabel = useMemo(() => {
    if (values.length === 0) {
      return labels.noCurrenciesSelected ?? '';
    }

    if (values.length <= 3) {
      return values.join(', ');
    }

    return (labels.selectedCurrenciesPattern ?? '').replace('%d', String(values.length));
  }, [labels.noCurrenciesSelected, labels.selectedCurrenciesPattern, values]);

  const toggleCurrency = (code: string) => {
    if (selectedSet.has(code)) {
      onChange(values.filter((item) => item !== code));
      return;
    }

    onChange([...values, code]);
  };

  return (
    <BaseControl label={label} help={help}>
      <Button
        ref={buttonRef}
        variant="secondary"
        onClick={() => setIsOpen((open) => !open)}
        style={{ width: '100%', justifyContent: 'space-between' }}
        aria-expanded={isOpen}
      >
        <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
          {selectedLabel}
        </span>
        <span aria-hidden="true" style={{ marginLeft: '8px' }}>▾</span>
      </Button>

      {isOpen && buttonRef.current ? (
        <Popover
          anchor={buttonRef.current}
          onClose={() => setIsOpen(false)}
          placement="bottom-start"
          focusOnMount
          shift
        >
          <div style={{ width: '360px', maxWidth: 'min(90vw, 360px)', padding: '8px' }}>
            <SearchControl
              value={query}
              onChange={(next) => setQuery(next)}
              placeholder={labels.searchCurrency ?? ''}
              label={labels.searchCurrency ?? ''}
              hideLabelFromVision
            />

            <div style={{ maxHeight: '320px', overflow: 'auto', marginTop: '8px' }}>
              {grouped.length === 0 ? (
                <div style={{ padding: '8px', color: '#646970' }}>
                  {labels.noResults ?? ''}
                </div>
              ) : (
                <NavigableMenu orientation="vertical">
                  {grouped.map((group) => (
                    <MenuGroup key={group.group} label={group.label}>
                      {group.items.map((currency) => {
                        const isSelected = selectedSet.has(currency.code);
                        const isUnavailable = availableCodes != null && !availableCodes.has(currency.code);

                        return (
                          <MenuItem
                            key={currency.code}
                            disabled={isUnavailable && !isSelected}
                            onClick={() => {
                              if (isUnavailable && !isSelected) return;
                              toggleCurrency(currency.code);
                            }}
                          >
                            <span
                              style={{
                                display: 'flex',
                                alignItems: 'center',
                                justifyContent: 'flex-start',
                                gap: '8px',
                                width: '100%',
                                textAlign: 'left',
                                opacity: isUnavailable ? 0.4 : 1,
                              }}
                            >
                              <span aria-hidden="true" style={{ width: 16, textAlign: 'center' }}>
                                {isSelected && isUnavailable ? '⚠' : isSelected ? '☑' : '☐'}
                              </span>
                              {currency.flag && flagBaseUrl ? (
                                <img
                                  src={`${flagBaseUrl}${currency.flag}.webp`}
                                  alt=""
                                  width={16}
                                  height={16}
                                  style={{ borderRadius: '2px', objectFit: 'cover', flexShrink: 0 }}
                                />
                              ) : null}
                              <span style={{ minWidth: 48, fontFamily: 'monospace', textAlign: 'left' }}>
                                {currency.code}
                              </span>
                              <span
                                style={{
                                  flex: '1 1 auto',
                                  minWidth: 0,
                                  textAlign: 'left',
                                }}
                              >
                                {currency.name}
                              </span>
                            </span>
                          </MenuItem>
                        );
                      })}
                    </MenuGroup>
                  ))}
                </NavigableMenu>
              )}
            </div>
          </div>
        </Popover>
      ) : null}
    </BaseControl>
  );
}
