import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';

import { useCall } from '../context/CallContext';
import { codeToShortcut } from '../lib/globalShortcut';
import {
  DEFAULT_PREFS,
  getPrefs,
  listSounds,
  type SoundboardEntry,
  type SoundboardPrefs,
  subscribeSoundboardChanges,
} from '../lib/soundboardStorage';
import { ChevronDownIcon, XIcon } from './icons';

interface Props {
  onClose: () => void;
}

// In-call popover that lists every stored sound grouped by category. Click a
// pad to play through the active pipeline. Hotkey-badge shows the bound
// accelerator (if any). Master + monitor sliders adjust the pipeline gains.
export function SoundboardPanel({ onClose }: Props) {
  const { t } = useTranslation(['app']);
  const {
    playSoundboard,
    stopSoundboard,
    activeSoundboardIds,
    setSoundboardMasterGain,
    setSoundboardMonitorGain,
  } = useCall();
  const [entries, setEntries] = useState<SoundboardEntry[]>([]);
  const [prefs, setPrefs] = useState<SoundboardPrefs>(() => ({ ...DEFAULT_PREFS }));
  const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
  const [query, setQuery] = useState('');
  const closeRef = useRef(onClose);
  closeRef.current = onClose;

  const refresh = useCallback(async () => {
    try {
      const [all, p] = await Promise.all([listSounds(), getPrefs()]);
      setEntries(all);
      setPrefs(p);
    } catch (err: unknown) {
      console.warn('soundboard panel refresh failed', err);
    }
  }, []);

  useEffect(() => {
    void refresh();
    const unsub = subscribeSoundboardChanges(() => {
      void refresh();
    });
    return unsub;
  }, [refresh]);

  // Close on Esc — tapping outside is handled by the trigger's parent.
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') closeRef.current();
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);

  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return entries;
    return entries.filter(
      (e) =>
        e.name.toLowerCase().includes(q) ||
        (e.category ?? '').toLowerCase().includes(q),
    );
  }, [entries, query]);

  const grouped = useMemo(() => {
    const map = new Map<string, SoundboardEntry[]>();
    for (const e of filtered) {
      const key = e.category ?? '';
      const arr = map.get(key);
      if (arr) arr.push(e);
      else map.set(key, [e]);
    }
    return map;
  }, [filtered]);

  function toggleCategory(key: string): void {
    setCollapsed((prev) => ({ ...prev, [key]: !prev[key] }));
  }

  return (
    <div
      role="dialog"
      aria-label={t('app:soundboard.panel_title', { defaultValue: 'Soundboard' })}
      className="flex w-[360px] max-h-[70vh] flex-col overflow-hidden rounded-2xl border border-line bg-surface-3 shadow-2xl"
    >
      <header className="flex items-center justify-between border-b border-line px-4 py-2.5">
        <h3 className="font-display text-sm font-semibold text-fg">
          {t('app:soundboard.panel_title', { defaultValue: 'Soundboard' })}
        </h3>
        <button
          type="button"
          onClick={onClose}
          aria-label={t('app:soundboard.panel_close', { defaultValue: 'Schließen' })}
          className="flex h-7 w-7 cursor-pointer items-center justify-center rounded-md text-fg-muted transition hover:bg-surface-2 hover:text-fg"
        >
          <XIcon className="h-4 w-4" />
        </button>
      </header>

      {entries.length > 0 && (
        <div className="border-b border-line px-3 pb-2 pt-2">
          <input
            type="search"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder={t('app:soundboard.panel_search', { defaultValue: 'Suche…' })}
            className="w-full rounded-md border border-line bg-surface-2 px-3 py-1.5 text-xs text-fg placeholder-fg-muted focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/30"
          />
        </div>
      )}

      <div className="min-h-0 flex-1 overflow-y-auto p-3">
        {entries.length === 0 ? (
          <p className="py-6 text-center text-xs text-fg-muted">
            {t('app:soundboard.panel_empty', {
              defaultValue:
                'Keine Sounds gespeichert. Füge welche in den Einstellungen hinzu.',
            })}
          </p>
        ) : filtered.length === 0 ? (
          <p className="py-6 text-center text-xs text-fg-muted">
            {t('app:soundboard.panel_no_matches', {
              defaultValue: 'Keine Treffer.',
            })}
          </p>
        ) : (
          <div className="flex flex-col gap-3">
            {Array.from(grouped.entries()).map(([key, bucket]) => (
              <CategorySection
                key={key || '__uncat'}
                categoryKey={key}
                entries={bucket}
                collapsed={collapsed[key] ?? false}
                activeIds={activeSoundboardIds}
                onToggle={() => toggleCategory(key)}
                onActivate={(id) => {
                  if (activeSoundboardIds.has(id)) {
                    stopSoundboard(id);
                  } else {
                    void playSoundboard(id);
                  }
                }}
              />
            ))}
          </div>
        )}
      </div>

      <footer className="flex flex-col gap-2 border-t border-line bg-surface-2 px-4 py-3">
        <VolumeSlider
          label={t('app:soundboard.panel_master', { defaultValue: 'Master' })}
          value={prefs.masterGain}
          onChange={(v) => {
            setPrefs((p) => ({ ...p, masterGain: v }));
            void setSoundboardMasterGain(v);
          }}
        />
        <VolumeSlider
          label={t('app:soundboard.panel_monitor', { defaultValue: 'Mithören' })}
          value={prefs.monitorGain}
          onChange={(v) => {
            setPrefs((p) => ({ ...p, monitorGain: v }));
            void setSoundboardMonitorGain(v);
          }}
        />
        <button
          type="button"
          onClick={() => stopSoundboard()}
          className="mt-1 inline-flex cursor-pointer items-center justify-center rounded-md border border-line bg-surface-3 px-3 py-1.5 text-[11px] font-semibold text-fg transition hover:brightness-95"
        >
          {t('app:soundboard.panel_stop_all', { defaultValue: 'Alle stoppen' })}
        </button>
      </footer>
    </div>
  );
}

interface CategorySectionProps {
  categoryKey: string;
  entries: SoundboardEntry[];
  collapsed: boolean;
  activeIds: ReadonlySet<string>;
  onToggle: () => void;
  onActivate: (id: string) => void;
}

function CategorySection({
  categoryKey,
  entries,
  collapsed,
  activeIds,
  onToggle,
  onActivate,
}: CategorySectionProps) {
  const { t } = useTranslation(['app']);
  const label =
    categoryKey === ''
      ? t('app:soundboard.category_uncategorized', { defaultValue: '(Ohne Kategorie)' })
      : categoryKey;
  return (
    <section>
      <button
        type="button"
        onClick={onToggle}
        className="mb-1.5 flex w-full cursor-pointer items-center justify-between gap-2 text-left text-[10px] font-semibold uppercase tracking-[0.1em] text-fg-muted"
      >
        <span>
          {label} · {entries.length}
        </span>
        <ChevronDownIcon
          className={'h-3 w-3 transition ' + (collapsed ? '-rotate-90' : '')}
        />
      </button>
      {!collapsed && (
        <div className="grid grid-cols-2 gap-1.5">
          {entries.map((entry) => (
            <SoundPad
              key={entry.id}
              entry={entry}
              active={activeIds.has(entry.id)}
              onActivate={() => onActivate(entry.id)}
            />
          ))}
        </div>
      )}
    </section>
  );
}

interface PadProps {
  entry: SoundboardEntry;
  active: boolean;
  onActivate: () => void;
}

function SoundPad({ entry, active, onActivate }: PadProps) {
  const { t } = useTranslation(['app']);
  const hotkey = entry.hotkey ? codeToShortcut(entry.hotkey) : null;
  const base =
    'group relative flex min-h-[54px] cursor-pointer flex-col justify-center gap-0.5 rounded-md border px-2.5 py-2 text-left text-xs font-medium transition focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/40';
  const toneClass = active
    ? 'border-rose-500 bg-rose-500/20 text-fg hover:brightness-110'
    : 'border-line bg-surface-2 text-fg hover:border-accent hover:bg-surface-3';
  return (
    <button
      type="button"
      onClick={onActivate}
      className={`${base} ${toneClass}`}
      title={
        active
          ? t('app:soundboard.pad_stop', { defaultValue: 'Stoppen' })
          : entry.name
      }
      aria-pressed={active}
    >
      <span className="flex items-center gap-1.5 truncate">
        {active && (
          <span
            aria-hidden="true"
            className="h-2 w-2 shrink-0 rounded-full bg-rose-500 animate-live-dot"
          />
        )}
        <span className="truncate">{entry.name}</span>
      </span>
      <div className="flex items-center justify-between gap-1.5">
        {hotkey ? (
          <span className="inline-flex w-fit items-center rounded border border-line bg-surface-3 px-1 py-0.5 font-mono text-[9px] text-fg-muted">
            {hotkey}
          </span>
        ) : (
          <span aria-hidden="true" />
        )}
        {active && (
          <span className="text-[9px] font-semibold uppercase tracking-[0.08em] text-rose-500 dark:text-rose-300">
            {t('app:soundboard.pad_stop', { defaultValue: 'Stoppen' })}
          </span>
        )}
      </div>
    </button>
  );
}

function VolumeSlider({
  label,
  value,
  onChange,
}: {
  label: string;
  value: number;
  onChange: (v: number) => void;
}) {
  return (
    <label className="flex items-center gap-3 text-[11px] text-fg">
      <span className="w-16 shrink-0 text-fg-muted">{label}</span>
      <input
        type="range"
        min={0}
        max={1}
        step={0.02}
        value={value}
        onChange={(e) => onChange(Number(e.target.value))}
        className="accent-accent flex-1"
      />
      <span className="w-9 shrink-0 text-right tabular-nums text-fg-muted">
        {Math.round(value * 100)}%
      </span>
    </label>
  );
}
