import { useCallback, useEffect, useMemo, useState } from 'react';
import type { AgentAction, AgentView } from '@stream-control/shared';
import { api, getToken } from './api';
import { useRealtime } from './useRealtime';
import { Login } from './components/Login';
import { AgentCard } from './components/AgentCard';
import { AgentSettings } from './components/AgentSettings';
import { LogPanel } from './components/LogPanel';

interface Toast {
  message: string;
  tone: 'info' | 'error';
}

export function App() {
  const [authenticated, setAuthenticated] = useState(() => Boolean(getToken()));
  const [selection, setSelection] = useState<Set<string>>(new Set());
  const [settingsFor, setSettingsFor] = useState<AgentView | null>(null);
  const [toast, setToast] = useState<Toast | null>(null);
  const [newAgentToken, setNewAgentToken] = useState<{ name: string; token: string } | null>(null);

  const onUnauthorized = useCallback(() => setAuthenticated(false), []);
  const { agents, logs, connected } = useRealtime(authenticated, onUnauthorized);

  const notify = useCallback((message: string, tone: 'info' | 'error' = 'info') => {
    setToast({ message, tone });
  }, []);

  useEffect(() => {
    if (!toast) return;
    const timer = window.setTimeout(() => setToast(null), 4000);
    return () => window.clearTimeout(timer);
  }, [toast]);

  // La fiche ouverte doit refléter les mises à jour temps réel.
  const openAgent = useMemo(
    () => (settingsFor ? (agents.find((agent) => agent.id === settingsFor.id) ?? null) : null),
    [agents, settingsFor],
  );

  const online = agents.filter((agent) => agent.online);
  const recording = agents.filter((agent) => agent.status.recording);

  const runCommand = useCallback(
    async (id: string, action: AgentAction, params?: Record<string, unknown>) => {
      try {
        await api.command(id, action, params);
      } catch (err) {
        notify(err instanceof Error ? err.message : 'Commande en échec', 'error');
      }
    },
    [notify],
  );

  const runBulk = useCallback(
    async (action: AgentAction) => {
      const targets = selection.size > 0 ? [...selection] : online.map((agent) => agent.id);
      if (targets.length === 0) {
        notify('Aucun agent sélectionné', 'error');
        return;
      }
      try {
        const { results } = await api.bulk(targets, action);
        const failures = results.filter((result) => !result.ok);
        if (failures.length === 0) notify(`${results.length} agent(s) : commande envoyée`);
        else
          notify(
            `${results.length - failures.length}/${results.length} OK — ${failures[0]?.error ?? ''}`,
            'error',
          );
      } catch (err) {
        notify(err instanceof Error ? err.message : 'Commande groupée en échec', 'error');
      }
    },
    [notify, online, selection],
  );

  function toggleSelect(id: string) {
    setSelection((current) => {
      const next = new Set(current);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }

  async function addAgent() {
    const name = prompt("Nom du nouvel agent (ex. vm-rec-01)")?.trim();
    if (!name) return;
    try {
      const result = await api.createAgent({ name });
      setNewAgentToken({ name, token: result.token });
    } catch (err) {
      notify(err instanceof Error ? err.message : 'Création impossible', 'error');
    }
  }

  function logout() {
    api.logout();
    setAuthenticated(false);
  }

  if (!authenticated) {
    return <Login onSuccess={() => setAuthenticated(true)} />;
  }

  return (
    <div className="app">
      <header className="topbar">
        <div className="brand">
          <span className={`dot ${connected ? 'ok' : 'offline'}`} />
          <h1>Stream Control</h1>
          <span className="muted small">
            {online.length}/{agents.length} en ligne · {recording.length} en enregistrement
          </span>
        </div>

        <div className="topbar-actions">
          <button className="primary" onClick={() => void runBulk('record.start')}>
            ● Enregistrer{selection.size > 0 ? ` (${selection.size})` : ' tout'}
          </button>
          <button className="danger" onClick={() => void runBulk('record.stop')}>
            ■ Arrêter{selection.size > 0 ? ` (${selection.size})` : ' tout'}
          </button>
          <button className="ghost" onClick={() => void runBulk('obs.connect')}>
            Reconnecter OBS
          </button>
          <button className="ghost" onClick={() => void addAgent()}>
            + Agent
          </button>
          <button className="ghost" onClick={logout}>
            Quitter
          </button>
        </div>
      </header>

      {!connected && (
        <div className="banner warn">Flux temps réel interrompu — reconnexion en cours…</div>
      )}

      {newAgentToken && (
        <div className="banner info">
          <div>
            Agent « {newAgentToken.name} » créé. Jeton (affiché une seule fois) :{' '}
            <code className="token">{newAgentToken.token}</code>
          </div>
          <button className="ghost" onClick={() => setNewAgentToken(null)}>
            Fermer
          </button>
        </div>
      )}

      <main className="grid">
        {agents.length === 0 && (
          <div className="empty">
            <h2>Aucun agent enregistré</h2>
            <p className="muted">
              Crée un agent ici pour obtenir un jeton, ou démarre un agent avec le jeton
              d'enrôlement : il apparaîtra automatiquement.
            </p>
          </div>
        )}
        {agents.map((agent) => (
          <AgentCard
            key={agent.id}
            agent={agent}
            selected={selection.has(agent.id)}
            onToggleSelect={toggleSelect}
            onCommand={runCommand}
            onOpenSettings={setSettingsFor}
          />
        ))}
      </main>

      <LogPanel logs={logs} />

      {openAgent && (
        <AgentSettings
          agent={openAgent}
          onClose={() => setSettingsFor(null)}
          onCommand={runCommand}
          notify={notify}
        />
      )}

      {toast && <div className={`toast ${toast.tone}`}>{toast.message}</div>}
    </div>
  );
}
