import { useState, useEffect, useMemo, useCallback } from 'react';

/* ══════════════════════════════════════════════════════════════════
   TYPES
   ══════════════════════════════════════════════════════════════════ */

type Sound = {
  fileName: string;
  name: string;
  folder?: string;
  relativePath?: string;
};

type SoundsResponse = {
  items: Sound[];
  total: number;
  folders: Array<{ key: string; name: string; count: number }>;
};

interface AdminPanelProps {
  onClose: () => void;
  onLogout: () => void;
}

/* ══════════════════════════════════════════════════════════════════
   API HELPERS
   ══════════════════════════════════════════════════════════════════ */

const SB_API = '/api/soundboard';

async function fetchAllSounds(): Promise<SoundsResponse> {
  const url = new URL(`${SB_API}/sounds`, window.location.origin);
  url.searchParams.set('folder', '__all__');
  url.searchParams.set('fuzzy', '0');
  const res = await fetch(url.toString());
  if (!res.ok) throw new Error('Fehler beim Laden der Sounds');
  return res.json();
}

async function apiAdminDelete(paths: string[]): Promise<void> {
  const res = await fetch(`${SB_API}/admin/sounds/delete`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include',
    body: JSON.stringify({ paths }),
  });
  if (!res.ok) throw new Error('Loeschen fehlgeschlagen');
}

async function apiAdminRename(from: string, to: string): Promise<string> {
  const res = await fetch(`${SB_API}/admin/sounds/rename`, {
    method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include',
    body: JSON.stringify({ from, to }),
  });
  if (!res.ok) throw new Error('Umbenennen fehlgeschlagen');
  const data = await res.json();
  return data?.to as string;
}

function apiUploadFile(
  file: File,
  onProgress: (pct: number) => void,
): Promise<string> {
  return new Promise((resolve, reject) => {
    const form = new FormData();
    form.append('files', file);
    const xhr = new XMLHttpRequest();
    xhr.open('POST', `${SB_API}/upload`);
    xhr.upload.onprogress = e => {
      if (e.lengthComputable) onProgress(Math.round((e.loaded / e.total) * 100));
    };
    xhr.onload = () => {
      if (xhr.status === 200) {
        try {
          const data = JSON.parse(xhr.responseText);
          resolve(data.files?.[0]?.name ?? file.name);
        } catch { resolve(file.name); }
      } else {
        try { reject(new Error(JSON.parse(xhr.responseText).error)); }
        catch { reject(new Error(`HTTP ${xhr.status}`)); }
      }
    };
    xhr.onerror = () => reject(new Error('Netzwerkfehler'));
    xhr.send(form);
  });
}

/* ══════════════════════════════════════════════════════════════════
   COMPONENT
   ══════════════════════════════════════════════════════════════════ */

type AdminTab = 'soundboard' | 'streaming' | 'game-library';

export default function AdminPanel({ onClose, onLogout }: AdminPanelProps) {
  const [activeTab, setActiveTab] = useState<AdminTab>('soundboard');

  // ── Toast ──
  const [toast, setToast] = useState<{ msg: string; type: 'info' | 'error' } | null>(null);
  const notify = useCallback((msg: string, type: 'info' | 'error' = 'info') => {
    setToast({ msg, type });
    setTimeout(() => setToast(null), 3000);
  }, []);

  // ── Escape key ──
  useEffect(() => {
    const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [onClose]);

  /* ════════════════════════════════════════════════════════════════
     SOUNDBOARD ADMIN STATE
     ════════════════════════════════════════════════════════════════ */
  const [sbSounds, setSbSounds] = useState<Sound[]>([]);
  const [sbLoading, setSbLoading] = useState(false);
  const [sbQuery, setSbQuery] = useState('');
  const [sbSelection, setSbSelection] = useState<Record<string, boolean>>({});
  const [sbRenameTarget, setSbRenameTarget] = useState('');
  const [sbRenameValue, setSbRenameValue] = useState('');
  const [sbUploadProgress, setSbUploadProgress] = useState<number | null>(null);

  const soundKey = useCallback((s: Sound) => s.relativePath ?? s.fileName, []);

  const loadSbSounds = useCallback(async () => {
    setSbLoading(true);
    try {
      const d = await fetchAllSounds();
      setSbSounds(d.items || []);
    } catch (e: any) {
      notify(e?.message || 'Sounds konnten nicht geladen werden', 'error');
    } finally {
      setSbLoading(false);
    }
  }, [notify]);

  // Load on first tab switch
  const [sbLoaded, setSbLoaded] = useState(false);
  useEffect(() => {
    if (activeTab === 'soundboard' && !sbLoaded) {
      setSbLoaded(true);
      void loadSbSounds();
    }
  }, [activeTab, sbLoaded, loadSbSounds]);

  const sbFiltered = useMemo(() => {
    const q = sbQuery.trim().toLowerCase();
    if (!q) return sbSounds;
    return sbSounds.filter(s => {
      const key = soundKey(s).toLowerCase();
      return s.name.toLowerCase().includes(q)
        || (s.folder || '').toLowerCase().includes(q)
        || key.includes(q);
    });
  }, [sbQuery, sbSounds, soundKey]);

  const sbSelectedPaths = useMemo(() =>
    Object.keys(sbSelection).filter(k => sbSelection[k]),
  [sbSelection]);

  const sbSelectedVisibleCount = useMemo(() =>
    sbFiltered.filter(s => !!sbSelection[soundKey(s)]).length,
  [sbFiltered, sbSelection, soundKey]);

  const sbAllVisibleSelected = sbFiltered.length > 0 && sbSelectedVisibleCount === sbFiltered.length;

  function sbToggleSelection(path: string) {
    setSbSelection(prev => ({ ...prev, [path]: !prev[path] }));
  }

  function sbStartRename(sound: Sound) {
    setSbRenameTarget(soundKey(sound));
    setSbRenameValue(sound.name);
  }

  function sbCancelRename() {
    setSbRenameTarget('');
    setSbRenameValue('');
  }

  async function sbSubmitRename() {
    if (!sbRenameTarget) return;
    const baseName = sbRenameValue.trim().replace(/\.(mp3|wav)$/i, '');
    if (!baseName) {
      notify('Bitte einen gueltigen Namen eingeben', 'error');
      return;
    }
    try {
      await apiAdminRename(sbRenameTarget, baseName);
      notify('Sound umbenannt');
      sbCancelRename();
      await loadSbSounds();
    } catch (e: any) {
      notify(e?.message || 'Umbenennen fehlgeschlagen', 'error');
    }
  }

  async function sbDeletePaths(paths: string[]) {
    if (paths.length === 0) return;
    try {
      await apiAdminDelete(paths);
      notify(paths.length === 1 ? 'Sound geloescht' : `${paths.length} Sounds geloescht`);
      setSbSelection({});
      sbCancelRename();
      await loadSbSounds();
    } catch (e: any) {
      notify(e?.message || 'Loeschen fehlgeschlagen', 'error');
    }
  }

  async function sbUpload(file: File) {
    setSbUploadProgress(0);
    try {
      await apiUploadFile(file, pct => setSbUploadProgress(pct));
      notify(`"${file.name}" hochgeladen`);
      await loadSbSounds();
    } catch (e: any) {
      notify(e?.message || 'Upload fehlgeschlagen', 'error');
    } finally {
      setSbUploadProgress(null);
    }
  }

  /* ════════════════════════════════════════════════════════════════
     STREAMING ADMIN STATE
     ════════════════════════════════════════════════════════════════ */
  const [stAvailableChannels, setStAvailableChannels] = useState<Array<{ channelId: string; channelName: string; guildId: string; guildName: string }>>([]);
  const [stNotifyConfig, setStNotifyConfig] = useState<Array<{ channelId: string; channelName: string; guildId: string; guildName: string; events: string[] }>>([]);
  const [stConfigLoading, setStConfigLoading] = useState(false);
  const [stConfigSaving, setStConfigSaving] = useState(false);
  const [stNotifyStatus, setStNotifyStatus] = useState<{ online: boolean; botTag: string | null }>({ online: false, botTag: null });

  const loadStreamingConfig = useCallback(async () => {
    setStConfigLoading(true);
    try {
      const [statusResp, chResp, cfgResp] = await Promise.all([
        fetch('/api/notifications/status'),
        fetch('/api/notifications/channels', { credentials: 'include' }),
        fetch('/api/notifications/config', { credentials: 'include' }),
      ]);
      if (statusResp.ok) {
        const d = await statusResp.json();
        setStNotifyStatus(d);
      }
      if (chResp.ok) {
        const chData = await chResp.json();
        setStAvailableChannels(chData.channels || []);
      }
      if (cfgResp.ok) {
        const cfgData = await cfgResp.json();
        setStNotifyConfig(cfgData.channels || []);
      }
    } catch { /* silent */ }
    finally { setStConfigLoading(false); }
  }, []);

  const [stLoaded, setStLoaded] = useState(false);
  useEffect(() => {
    if (activeTab === 'streaming' && !stLoaded) {
      setStLoaded(true);
      void loadStreamingConfig();
    }
  }, [activeTab, stLoaded, loadStreamingConfig]);

  const stToggleEvent = useCallback((channelId: string, channelName: string, guildId: string, guildName: string, event: string) => {
    setStNotifyConfig(prev => {
      const existing = prev.find(c => c.channelId === channelId);
      if (existing) {
        const hasEvent = existing.events.includes(event);
        const newEvents = hasEvent
          ? existing.events.filter(e => e !== event)
          : [...existing.events, event];
        if (newEvents.length === 0) {
          return prev.filter(c => c.channelId !== channelId);
        }
        return prev.map(c => c.channelId === channelId ? { ...c, events: newEvents } : c);
      } else {
        return [...prev, { channelId, channelName, guildId, guildName, events: [event] }];
      }
    });
  }, []);

  const stIsEnabled = useCallback((channelId: string, event: string): boolean => {
    const ch = stNotifyConfig.find(c => c.channelId === channelId);
    return ch?.events.includes(event) ?? false;
  }, [stNotifyConfig]);

  const stSaveConfig = useCallback(async () => {
    setStConfigSaving(true);
    try {
      await fetch('/api/notifications/config', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ channels: stNotifyConfig }),
        credentials: 'include',
      });
      notify('Konfiguration gespeichert');
    } catch {
      notify('Speichern fehlgeschlagen', 'error');
    } finally {
      setStConfigSaving(false);
    }
  }, [stNotifyConfig, notify]);

  /* ════════════════════════════════════════════════════════════════
     GAME LIBRARY ADMIN STATE
     ════════════════════════════════════════════════════════════════ */
  const [glProfiles, setGlProfiles] = useState<any[]>([]);
  const [glLoading, setGlLoading] = useState(false);

  const loadGlProfiles = useCallback(async () => {
    setGlLoading(true);
    try {
      const resp = await fetch('/api/game-library/admin/profiles', { credentials: 'include' });
      if (resp.ok) {
        const d = await resp.json();
        setGlProfiles(d.profiles || []);
      }
    } catch { /* silent */ }
    finally { setGlLoading(false); }
  }, []);

  const [glLoaded, setGlLoaded] = useState(false);
  useEffect(() => {
    if (activeTab === 'game-library' && !glLoaded) {
      setGlLoaded(true);
      void loadGlProfiles();
    }
  }, [activeTab, glLoaded, loadGlProfiles]);

  const glDeleteProfile = useCallback(async (profileId: string, displayName: string) => {
    if (!confirm(`Profil "${displayName}" wirklich komplett loeschen? (Alle verknuepften Daten werden entfernt)`)) return;
    try {
      const resp = await fetch(`/api/game-library/admin/profile/${profileId}`, {
        method: 'DELETE',
        credentials: 'include',
      });
      if (resp.ok) {
        notify('Profil geloescht');
        loadGlProfiles();
      }
    } catch {
      notify('Loeschen fehlgeschlagen', 'error');
    }
  }, [loadGlProfiles, notify]);

  /* ════════════════════════════════════════════════════════════════
     TAB CONFIG
     ════════════════════════════════════════════════════════════════ */

  const tabs: { id: AdminTab; icon: string; label: string }[] = [
    { id: 'soundboard', icon: '\uD83C\uDFB5', label: 'Soundboard' },
    { id: 'streaming', icon: '\uD83D\uDCFA', label: 'Streaming' },
    { id: 'game-library', icon: '\uD83C\uDFAE', label: 'Game Library' },
  ];

  /* ════════════════════════════════════════════════════════════════
     RENDER
     ════════════════════════════════════════════════════════════════ */

  return (
    <div className="ap-overlay" onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="ap-modal">
        {/* ── Sidebar ── */}
        <div className="ap-sidebar">
          <div className="ap-sidebar-title">{'\u2699\uFE0F'} Admin</div>
          <nav className="ap-nav">
            {tabs.map(t => (
              <button
                key={t.id}
                className={`ap-nav-item ${activeTab === t.id ? 'active' : ''}`}
                onClick={() => setActiveTab(t.id)}
              >
                <span className="ap-nav-icon">{t.icon}</span>
                <span className="ap-nav-label">{t.label}</span>
              </button>
            ))}
          </nav>
          <button className="ap-logout-btn" onClick={onLogout}>
            {'\uD83D\uDD12'} Abmelden
          </button>
        </div>

        {/* ── Content ── */}
        <div className="ap-content">
          <div className="ap-header">
            <h2 className="ap-title">
              {tabs.find(t => t.id === activeTab)?.icon}{' '}
              {tabs.find(t => t.id === activeTab)?.label}
            </h2>
            <button className="ap-close" onClick={onClose}>{'\u2715'}</button>
          </div>

          <div className="ap-body">
            {/* ═══════════════════ SOUNDBOARD TAB ═══════════════════ */}
            {activeTab === 'soundboard' && (
              <div className="ap-tab-content">
                <div className="ap-toolbar">
                  <input
                    type="text"
                    className="ap-search"
                    value={sbQuery}
                    onChange={e => setSbQuery(e.target.value)}
                    placeholder="Nach Name, Ordner oder Pfad filtern..."
                  />
                  <button
                    className="ap-btn ap-btn-outline"
                    onClick={() => { void loadSbSounds(); }}
                    disabled={sbLoading}
                  >
                    {'\u21BB'} Aktualisieren
                  </button>
                </div>

                {/* Upload */}
                <label className="ap-upload-zone">
                  <input
                    type="file"
                    accept=".mp3,.wav"
                    style={{ display: 'none' }}
                    onChange={e => {
                      const file = e.target.files?.[0];
                      if (file) sbUpload(file);
                      e.target.value = '';
                    }}
                  />
                  {sbUploadProgress !== null ? (
                    <span className="ap-upload-progress">Upload: {sbUploadProgress}%</span>
                  ) : (
                    <span className="ap-upload-text">{'\u2B06\uFE0F'} Datei hochladen (MP3 / WAV)</span>
                  )}
                </label>

                {/* Bulk actions */}
                <div className="ap-bulk-row">
                  <label className="ap-select-all">
                    <input
                      type="checkbox"
                      checked={sbAllVisibleSelected}
                      onChange={e => {
                        const checked = e.target.checked;
                        const next = { ...sbSelection };
                        sbFiltered.forEach(s => { next[soundKey(s)] = checked; });
                        setSbSelection(next);
                      }}
                    />
                    <span>Alle sichtbaren ({sbSelectedVisibleCount}/{sbFiltered.length})</span>
                  </label>
                  <button
                    className="ap-btn ap-btn-danger"
                    disabled={sbSelectedPaths.length === 0}
                    onClick={async () => {
                      if (!window.confirm(`Wirklich ${sbSelectedPaths.length} Sound(s) loeschen?`)) return;
                      await sbDeletePaths(sbSelectedPaths);
                    }}
                  >
                    {'\uD83D\uDDD1\uFE0F'} Ausgewaehlte loeschen
                  </button>
                </div>

                {/* Sound list */}
                <div className="ap-list-wrap">
                  {sbLoading ? (
                    <div className="ap-empty">Lade Sounds...</div>
                  ) : sbFiltered.length === 0 ? (
                    <div className="ap-empty">Keine Sounds gefunden.</div>
                  ) : (
                    <div className="ap-list">
                      {sbFiltered.map(sound => {
                        const key = soundKey(sound);
                        const editing = sbRenameTarget === key;
                        return (
                          <div className="ap-item" key={key}>
                            <label className="ap-item-check">
                              <input
                                type="checkbox"
                                checked={!!sbSelection[key]}
                                onChange={() => sbToggleSelection(key)}
                              />
                            </label>
                            <div className="ap-item-main">
                              <div className="ap-item-name">{sound.name}</div>
                              <div className="ap-item-meta">
                                {sound.folder ? `Ordner: ${sound.folder}` : 'Root'}
                                {' \u00B7 '}
                                {key}
                              </div>
                              {editing && (
                                <div className="ap-rename-row">
                                  <input
                                    className="ap-rename-input"
                                    value={sbRenameValue}
                                    onChange={e => setSbRenameValue(e.target.value)}
                                    onKeyDown={e => {
                                      if (e.key === 'Enter') void sbSubmitRename();
                                      if (e.key === 'Escape') sbCancelRename();
                                    }}
                                    placeholder="Neuer Name..."
                                    autoFocus
                                  />
                                  <button className="ap-btn ap-btn-primary ap-btn-sm" onClick={() => { void sbSubmitRename(); }}>
                                    Speichern
                                  </button>
                                  <button className="ap-btn ap-btn-outline ap-btn-sm" onClick={sbCancelRename}>
                                    Abbrechen
                                  </button>
                                </div>
                              )}
                            </div>
                            {!editing && (
                              <div className="ap-item-actions">
                                <button className="ap-btn ap-btn-outline ap-btn-sm" onClick={() => sbStartRename(sound)}>
                                  Umbenennen
                                </button>
                                <button
                                  className="ap-btn ap-btn-danger ap-btn-sm"
                                  onClick={async () => {
                                    if (!window.confirm(`Sound "${sound.name}" loeschen?`)) return;
                                    await sbDeletePaths([key]);
                                  }}
                                >
                                  Loeschen
                                </button>
                              </div>
                            )}
                          </div>
                        );
                      })}
                    </div>
                  )}
                </div>
              </div>
            )}

            {/* ═══════════════════ STREAMING TAB ═══════════════════ */}
            {activeTab === 'streaming' && (
              <div className="ap-tab-content">
                <div className="ap-toolbar">
                  <span className="ap-status-badge">
                    <span className={`ap-status-dot ${stNotifyStatus.online ? 'online' : ''}`} />
                    {stNotifyStatus.online
                      ? <>Bot online: <b>{stNotifyStatus.botTag}</b></>
                      : <>Bot offline</>}
                  </span>
                  <button
                    className="ap-btn ap-btn-outline"
                    onClick={() => { void loadStreamingConfig(); }}
                    disabled={stConfigLoading}
                  >
                    {'\u21BB'} Aktualisieren
                  </button>
                </div>

                {stConfigLoading ? (
                  <div className="ap-empty">Lade Kanaele...</div>
                ) : stAvailableChannels.length === 0 ? (
                  <div className="ap-empty">
                    {stNotifyStatus.online
                      ? 'Keine Text-Kanaele gefunden. Bot hat moeglicherweise keinen Zugriff.'
                      : 'Bot ist nicht verbunden. Bitte DISCORD_TOKEN_NOTIFICATIONS konfigurieren.'}
                  </div>
                ) : (
                  <>
                    <p className="ap-hint">
                      Waehle die Kanaele, in die Benachrichtigungen gesendet werden sollen:
                    </p>
                    <div className="ap-channel-list">
                      {stAvailableChannels.map(ch => (
                        <div key={ch.channelId} className="ap-channel-row">
                          <div className="ap-channel-info">
                            <span className="ap-channel-name">#{ch.channelName}</span>
                            <span className="ap-channel-guild">{ch.guildName}</span>
                          </div>
                          <div className="ap-channel-toggles">
                            <label className={`ap-toggle ${stIsEnabled(ch.channelId, 'stream_start') ? 'active' : ''}`}>
                              <input
                                type="checkbox"
                                checked={stIsEnabled(ch.channelId, 'stream_start')}
                                onChange={() => stToggleEvent(ch.channelId, ch.channelName, ch.guildId, ch.guildName, 'stream_start')}
                              />
                              {'\uD83D\uDD34'} Stream Start
                            </label>
                            <label className={`ap-toggle ${stIsEnabled(ch.channelId, 'stream_end') ? 'active' : ''}`}>
                              <input
                                type="checkbox"
                                checked={stIsEnabled(ch.channelId, 'stream_end')}
                                onChange={() => stToggleEvent(ch.channelId, ch.channelName, ch.guildId, ch.guildName, 'stream_end')}
                              />
                              {'\u23F9\uFE0F'} Stream Ende
                            </label>
                          </div>
                        </div>
                      ))}
                    </div>
                    <div className="ap-save-row">
                      <button
                        className="ap-btn ap-btn-primary"
                        onClick={stSaveConfig}
                        disabled={stConfigSaving}
                      >
                        {stConfigSaving ? 'Speichern...' : '\uD83D\uDCBE Speichern'}
                      </button>
                    </div>
                  </>
                )}
              </div>
            )}

            {/* ═══════════════════ GAME LIBRARY TAB ═══════════════════ */}
            {activeTab === 'game-library' && (
              <div className="ap-tab-content">
                <div className="ap-toolbar">
                  <span className="ap-status-badge">
                    <span className="ap-status-dot online" />
                    Eingeloggt als Admin
                  </span>
                  <button
                    className="ap-btn ap-btn-outline"
                    onClick={() => { void loadGlProfiles(); }}
                    disabled={glLoading}
                  >
                    {'\u21BB'} Aktualisieren
                  </button>
                </div>

                {glLoading ? (
                  <div className="ap-empty">Lade Profile...</div>
                ) : glProfiles.length === 0 ? (
                  <div className="ap-empty">Keine Profile vorhanden.</div>
                ) : (
                  <div className="ap-profile-list">
                    {glProfiles.map((p: any) => (
                      <div key={p.id} className="ap-profile-row">
                        <img className="ap-profile-avatar" src={p.avatarUrl} alt={p.displayName} />
                        <div className="ap-profile-info">
                          <span className="ap-profile-name">{p.displayName}</span>
                          <span className="ap-profile-details">
                            {p.steamName && <span className="ap-platform-badge steam">Steam: {p.steamGames}</span>}
                            {p.gogName && <span className="ap-platform-badge gog">GOG: {p.gogGames}</span>}
                            <span className="ap-profile-total">{p.totalGames} Spiele</span>
                          </span>
                        </div>
                        <button
                          className="ap-btn ap-btn-danger ap-btn-sm"
                          onClick={() => glDeleteProfile(p.id, p.displayName)}
                        >
                          {'\uD83D\uDDD1\uFE0F'} Entfernen
                        </button>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            )}
          </div>
        </div>

        {/* ── Toast ── */}
        {toast && (
          <div className={`ap-toast ${toast.type}`}>
            {toast.type === 'error' ? '\u274C' : '\u2705'} {toast.msg}
          </div>
        )}
      </div>
    </div>
  );
}
