import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
  ELECTRICAL_SYMBOL_DEFS,
  type ElectricalSymbolDef,
} from '../symbols/electrical';
import styles from './item-picker.module.css';

interface ElectricalPaletteProps {
  readonly selectedIndex: number | null;
  readonly onSelect: (index: number) => void;
}

interface IndexedDef {
  readonly def: ElectricalSymbolDef;
  readonly index: number;
}

interface CategoryGroup {
  readonly category: string;
  readonly items: readonly IndexedDef[];
}

/**
 * UI metadata per electrical category. The `category` field on
 * ElectricalSymbolDef drives this — adding a new category requires only
 * a new entry here and the matching i18n key in the locale file.
 */
const CATEGORY_META: Record<string, { icon: string; key: string; order: number }> = {
  outlet: { icon: '\u26A1', key: 'electrical.outlets', order: 0 },
  switch: { icon: '\u{1F50C}', key: 'electrical.switches', order: 1 },
  junction: { icon: '\u2B1C', key: 'electrical.junction', order: 2 },
  light: { icon: '\u{1F4A1}', key: 'electrical.lights', order: 3 },
  cable: { icon: '\u{1F517}', key: 'electrical.cable', order: 4 },
};

type CategoryFilter = string | 'all';

export function ElectricalPalette({ selectedIndex, onSelect }: ElectricalPaletteProps) {
  const { t, i18n } = useTranslation();
  const [search, setSearch] = useState('');
  const [activeCategory, setActiveCategory] = useState<CategoryFilter>('all');

  const handleSelect = useCallback(
    (index: number) => {
      onSelect(index);
    },
    [onSelect],
  );

  // Pre-compute the indexed list once. Indexes here MUST match positions in
  // ELECTRICAL_SYMBOL_DEFS so the picker continues to satisfy the
  // EditorCanvas contract: `ELECTRICAL_SYMBOL_DEFS[selectedElectricalIndex]`.
  const allIndexed = useMemo<readonly IndexedDef[]>(
    () => ELECTRICAL_SYMBOL_DEFS.map((def, index) => ({ def, index })),
    [],
  );

  // Distinct categories ordered by CATEGORY_META.order, falling back to
  // insertion order for unknown categories.
  const orderedCategories = useMemo<readonly string[]>(() => {
    const seen = new Set<string>();
    for (const item of allIndexed) {
      seen.add(item.def.category);
    }
    return Array.from(seen).sort((a, b) => {
      const oa = CATEGORY_META[a]?.order ?? 999;
      const ob = CATEGORY_META[b]?.order ?? 999;
      return oa - ob;
    });
  }, [allIndexed]);

  const visibleGroups = useMemo<readonly CategoryGroup[]>(() => {
    const trimmed = search.trim().toLowerCase();
    const matches = (item: IndexedDef): boolean => {
      if (activeCategory !== 'all' && item.def.category !== activeCategory) return false;
      if (!trimmed) return true;
      return (
        item.def.label.toLowerCase().includes(trimmed) ||
        item.def.type.toLowerCase().includes(trimmed) ||
        (item.def.variant?.toLowerCase().includes(trimmed) ?? false)
      );
    };

    const buckets = new Map<string, IndexedDef[]>();
    for (const item of allIndexed) {
      if (!matches(item)) continue;
      const list = buckets.get(item.def.category) ?? [];
      list.push(item);
      buckets.set(item.def.category, list);
    }

    return orderedCategories.flatMap((category) => {
      const items = buckets.get(category);
      return items && items.length > 0 ? [{ category, items }] : [];
    });
  }, [allIndexed, orderedCategories, search, activeCategory]);

  const categoryLabel = (category: string): string => {
    const meta = CATEGORY_META[category];
    if (!meta) return category;
    return i18n.exists(meta.key) ? t(meta.key) : category;
  };

  const totalVisible = visibleGroups.reduce((acc, g) => acc + g.items.length, 0);

  return (
    <div className={styles.palette}>
      <div className={styles.header}>{t('electrical.title')}</div>

      <div className={styles.searchRow}>
        <span className={styles.searchIcon} aria-hidden>
          {'\u{1F50D}'}
        </span>
        <input
          className={styles.searchInput}
          type="text"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          placeholder={
            i18n.exists('electrical.searchPlaceholder')
              ? t('electrical.searchPlaceholder')
              : 'Search electrical…'
          }
          aria-label={
            i18n.exists('electrical.searchPlaceholder')
              ? t('electrical.searchPlaceholder')
              : 'Search electrical'
          }
        />
        {search && (
          <button
            type="button"
            className={styles.searchClear}
            onClick={() => setSearch('')}
            aria-label="Clear search"
          >
            {'\u00D7'}
          </button>
        )}
      </div>

      <div className={styles.categoryChips}>
        <CategoryChip
          label={i18n.exists('furnitureCategory.all') ? t('furnitureCategory.all') : 'All'}
          active={activeCategory === 'all'}
          onClick={() => setActiveCategory('all')}
        />
        {orderedCategories.map((category) => {
          const meta = CATEGORY_META[category];
          const icon = meta?.icon ?? '';
          return (
            <CategoryChip
              key={category}
              label={`${icon} ${categoryLabel(category)}`}
              active={activeCategory === category}
              onClick={() => setActiveCategory(category)}
            />
          );
        })}
      </div>

      <div className={styles.scrollArea}>
        {totalVisible === 0 ? (
          <div className={styles.empty}>
            {i18n.exists('electrical.noResults') ? t('electrical.noResults') : 'No matches'}
          </div>
        ) : (
          visibleGroups.map((group) => {
            const meta = CATEGORY_META[group.category];
            const icon = meta?.icon ?? '';
            return (
              <div key={group.category} className={styles.section}>
                <div className={styles.sectionHeader}>
                  <span aria-hidden>{icon}</span>
                  <span>{categoryLabel(group.category)}</span>
                  <span className={styles.sectionCount}>{group.items.length}</span>
                </div>
                <div className={styles.itemGrid}>
                  {group.items.map(({ def, index }) => (
                    <button
                      key={index}
                      type="button"
                      className={[
                        styles.itemBtn,
                        selectedIndex === index ? styles.itemBtnActive : '',
                      ].join(' ')}
                      onClick={() => handleSelect(index)}
                      title={def.label}
                    >
                      <span className={styles.itemIcon}>{icon}</span>
                      <span className={styles.itemLabel}>{def.label}</span>
                    </button>
                  ))}
                </div>
              </div>
            );
          })
        )}
      </div>
    </div>
  );
}

interface CategoryChipProps {
  readonly label: string;
  readonly active: boolean;
  readonly onClick: () => void;
}

function CategoryChip({ label, active, onClick }: CategoryChipProps) {
  return (
    <button
      type="button"
      className={[styles.categoryChip, active ? styles.categoryChipActive : ''].join(' ')}
      onClick={onClick}
    >
      {label}
    </button>
  );
}
