// ============================================================================
//  app.jsx — Racine de l'application CléPass.
//  Gère :
//    - le démarrage (vérifie la session, choisit auth/unlock/login),
//    - l'écran d'authentification,
//    - le coffre déchiffré localement et sa sauvegarde côté serveur,
//    - le verrouillage manuel et automatique,
//    - les modals d'ajout/édition (chargées depuis modals.jsx).
// ============================================================================

const { useState, useEffect, useMemo, useRef, useCallback } = React;

// Filtres latéraux : constants (pas dans le coffre, c'est l'UI).
const FILTERS = [
  { id: 'all',       label: 'Tous',          icon: 'all',         accent: '#6366F1', count: 0 },
  { id: 'passwords', label: 'Mots de passe', icon: 'key',         accent: '#06B6D4', count: 0 },
  { id: 'totp',      label: '2FA',           icon: 'shield',      accent: '#F59E0B', count: 0 },
  { id: 'wifi',      label: 'Wi-Fi',         icon: 'wifi',        accent: '#0EA5E9', count: 0 },
  { id: 'passkeys',  label: 'Passkeys',      icon: 'fingerprint', accent: '#10B981', count: 0 },
  { id: 'favorites', label: 'Favoris',       icon: 'star',        accent: '#EC4899', count: 0 },
  { id: 'security',  label: 'Sécurité',      icon: 'alert',       accent: '#EF4444', count: 0 },
  { id: 'shared',    label: 'Partagés',      icon: 'share',       accent: '#8B5CF6', count: 0 },
];

// Calcul des compteurs dynamiques pour filtres et dossiers.
function withCounts(filters, folders, entries) {
  const f = filters.map(x => ({ ...x }));
  f[0].count = entries.filter(e => e.folderId !== 'trash').length;
  f[1].count = entries.filter(e => e.type !== 'wifi').reduce((s, e) => s + (e.accounts?.length || 0), 0);
  f[2].count = entries.reduce((s, e) => s + (e.totp ? e.totp.length : 0), 0);
  f[3].count = entries.filter(e => e.type === 'wifi').length;
  f[4].count = 0;
  f[5].count = entries.filter(e => e.favorite).length;
  f[6].count = entries.filter(e => e.strength && e.strength !== 'strong').length;
  f[7].count = entries.filter(e => e.shared).length;

  const countFolder = (id) => entries.filter(e => e.folderId === id).length;
  const fld = (folders || []).map(x => ({
    ...x,
    count: countFolder(x.id) + (x.children || []).reduce((s, c) => s + countFolder(c.id), 0),
    children: (x.children || []).map(c => ({ ...c, count: countFolder(c.id) })),
  }));
  return [f, fld];
}

// Normalisation d'une entrée Wi-Fi (cohérent avec data.jsx historique).
function normalizeEntry(e) {
  if (e.type === 'wifi') {
    e.domain = e.ssid;
    if (!e.accounts || e.accounts.length === 0) {
      e.accounts = [{ id: e.id + '-a', label: 'SSID', username: e.ssid, password: e.password, created: '' }];
    }
    if (!e.totp) e.totp = [];
  }
  if (!e.accounts) e.accounts = [];
  if (!e.totp) e.totp = [];
  return e;
}

// Tweaks par défaut (persistés via le canal du design tool ; sans hôte ils
// ne servent qu'à initialiser le state local).
const TWEAK_DEFAULTS = {
  theme: 'dark',
  accent: 'indigo-rose',
  density: 'regular',
  iconStyle: 'monogram',
};

const ACCENTS = {
  'indigo-rose': { from: '#6366F1', to: '#EC4899', solid: '#6366F1' },
  'emerald':     { from: '#10B981', to: '#059669', solid: '#10B981' },
  'amber':       { from: '#F59E0B', to: '#EA580C', solid: '#F59E0B' },
  'violet':      { from: '#8B5CF6', to: '#6366F1', solid: '#8B5CF6' },
  'crimson':     { from: '#EF4444', to: '#DC2626', solid: '#EF4444' },
};

// ─── Délai d'inactivité (verrouillage auto) ─────────────────────────────────
const IDLE_LOCK_MS = 15 * 60 * 1000;  // 15 min sans activité → lock

// ============================================================================
//  Splash — affiché pendant la vérification de session au démarrage
// ============================================================================
function Splash() {
  return (
    <div className="cp-splash">
      <div className="cp-splash-logo">
        <svg width="44" height="44" viewBox="0 0 24 24" fill="none">
          <defs>
            <linearGradient id="splash-lg" x1="0" y1="0" x2="1" y2="1">
              <stop offset="0" stopColor="#6366F1"/>
              <stop offset="1" stopColor="#EC4899"/>
            </linearGradient>
          </defs>
          <circle cx="9" cy="14" r="5" stroke="url(#splash-lg)" strokeWidth="2"/>
          <path d="M12.5 11.5 21 3" stroke="url(#splash-lg)" strokeWidth="2" strokeLinecap="round"/>
        </svg>
      </div>
      <div className="cp-splash-name">CléPass</div>
      <div className="cp-spin cp-spin-lg" />
    </div>
  );
}

// ============================================================================
//  Vault — l'application principale (sidebar + list + detail)
// ============================================================================
function Vault({
  vault, vaultVersion, encKey, sessionInfo,
  onMutate, onLock, onLogout, onChangeMaster,
}) {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [activeFilter, setActiveFilter] = useState('all');
  const [activeFolder, setActiveFolder] = useState(null);
  const [selectedId, setSelectedId] = useState(vault.entries[0]?.id || null);
  const [query, setQuery] = useState('');
  const [expanded, setExpanded] = useState({ perso: true, travail: true });
  const [encOpen, setEncOpen] = useState(false);
  const [modal, setModal] = useState(null); // 'add-password' | 'add-wifi' | 'edit' | null
  const [editTarget, setEditTarget] = useState(null);
  const [saveState, setSaveState] = useState({ status: 'idle', message: '' }); // idle|saving|saved|error

  // Application thème + accent au body.
  useEffect(() => {
    document.body.classList.toggle('dark', t.theme === 'dark');
    document.body.classList.toggle('light', t.theme === 'light');
    const a = ACCENTS[t.accent] || ACCENTS['indigo-rose'];
    document.body.style.setProperty('--accent-from', a.from);
    document.body.style.setProperty('--accent-to', a.to);
    document.body.style.setProperty('--accent', a.solid);
  }, [t.theme, t.accent]);

  // Calcul des filtres/dossiers avec compteurs.
  const [filters, folders] = useMemo(
    () => withCounts(FILTERS, vault.folders, vault.entries),
    [vault.folders, vault.entries]
  );

  // Filtrage de la liste.
  const filtered = useMemo(() => {
    let list = vault.entries.map(normalizeEntry);
    if (activeFolder) {
      list = list.filter(e => e.folderId === activeFolder ||
        (folders.find(f => f.id === activeFolder)?.children || []).some(c => c.id === e.folderId));
    } else {
      switch (activeFilter) {
        case 'passwords': list = list.filter(e => e.type !== 'wifi'); break;
        case 'totp':      list = list.filter(e => e.totp && e.totp.length > 0); break;
        case 'wifi':      list = list.filter(e => e.type === 'wifi'); break;
        case 'favorites': list = list.filter(e => e.favorite); break;
        case 'security':  list = list.filter(e => e.strength && e.strength !== 'strong'); break;
        case 'shared':    list = list.filter(e => e.shared); break;
        case 'passkeys':  list = []; break;
      }
    }
    if (query) {
      const q = query.toLowerCase();
      list = list.filter(e =>
        (e.name || '').toLowerCase().includes(q) ||
        (e.domain || '').toLowerCase().includes(q) ||
        (e.accounts || []).some(a => (a.username || '').toLowerCase().includes(q))
      );
    }
    return list.slice().sort((a, b) => (a.name || '').localeCompare(b.name || ''));
  }, [activeFilter, activeFolder, query, vault.entries, folders]);

  // Auto-sélection si l'entrée courante a disparu.
  useEffect(() => {
    if (filtered.length > 0 && !filtered.some(e => e.id === selectedId)) {
      setSelectedId(filtered[0].id);
    } else if (filtered.length === 0) {
      setSelectedId(null);
    }
  }, [filtered, selectedId]);

  const selected = vault.entries.find(e => e.id === selectedId) || null;
  const titleForView = activeFolder
    ? (folders.find(f => f.id === activeFolder)?.name ||
       folders.flatMap(f => f.children || []).find(c => c.id === activeFolder)?.name ||
       'Dossier')
    : (filters.find(f => f.id === activeFilter)?.label || 'Tous');

  // ─── Verrouillage automatique sur inactivité ─────────────────────────────
  useEffect(() => {
    let timer = null;
    const reset = () => {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => onLock(), IDLE_LOCK_MS);
    };
    const events = ['mousemove', 'keydown', 'click', 'touchstart', 'visibilitychange'];
    events.forEach(ev => window.addEventListener(ev, reset, { passive: true }));
    reset();
    return () => {
      events.forEach(ev => window.removeEventListener(ev, reset));
      if (timer) clearTimeout(timer);
    };
  }, [onLock]);

  // ─── Indicateur de sauvegarde, helper de mutation ─────────────────────────
  const mutate = useCallback(async (newVault) => {
    setSaveState({ status: 'saving', message: 'Chiffrement et sauvegarde…' });
    try {
      await onMutate(newVault);
      setSaveState({ status: 'saved', message: 'Sauvegardé' });
      setTimeout(() => setSaveState(s => s.status === 'saved' ? { status: 'idle', message: '' } : s), 1800);
    } catch (e) {
      setSaveState({ status: 'error', message: e.message || 'Erreur de sauvegarde' });
      throw e;
    }
  }, [onMutate]);

  // ─── CRUD entrées ─────────────────────────────────────────────────────────
  const addEntry = async (entry) => {
    const next = { ...vault, entries: [...(vault.entries || []), entry] };
    await mutate(next);
    setSelectedId(entry.id);
  };
  const updateEntry = (entry) => {
    const next = {
      ...vault,
      entries: (vault.entries || []).map(e => e.id === entry.id ? entry : e),
    };
    return mutate(next);
  };
  const deleteEntry = (entryId) => {
    const next = {
      ...vault,
      entries: (vault.entries || []).filter(e => e.id !== entryId),
    };
    return mutate(next);
  };
  const toggleFavorite = (entryId) => {
    const next = {
      ...vault,
      entries: (vault.entries || []).map(e => e.id === entryId ? { ...e, favorite: !e.favorite } : e),
    };
    return mutate(next);
  };

  // ─── Importer les données de démo (depuis window.CLEPASS_DEMO_DATA) ──────
  const importDemo = async () => {
    if (!window.CLEPASS_DEMO_DATA) return;
    const { ENTRIES, FOLDERS } = window.CLEPASS_DEMO_DATA;
    const next = {
      ...vault,
      entries: ENTRIES.map(e => ({ ...e })),
      folders: FOLDERS.map(f => ({ ...f })),
    };
    await mutate(next);
    setSelectedId(next.entries[0]?.id || null);
  };

  const isEmpty = vault.entries.length === 0;

  const [sidebarOpen, setSidebarOpen] = useState(false);

  return (
    <div className={`cp-app ${selected ? 'is-detail-open' : ''}`}>
      <div className="cp-bg" />

      <header className="cp-header">
        <button
          className="cp-header-burger"
          onClick={() => setSidebarOpen(o => !o)}
          aria-label="Menu"
        >
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
            <path d="M3 6h18M3 12h18M3 18h18"/>
          </svg>
        </button>
        <div className="cp-header-logo">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none">
            <defs><linearGradient id="hdg" x1="0" x2="1" y1="0" y2="1"><stop offset="0" stopColor="#6366F1"/><stop offset="1" stopColor="#EC4899"/></linearGradient></defs>
            <circle cx="9" cy="14" r="5" stroke="url(#hdg)" strokeWidth="2"/>
            <path d="M12.5 11.5 21 3" stroke="url(#hdg)" strokeWidth="2" strokeLinecap="round"/>
            <path d="m18 6 2.5 2.5M15 9l2.5 2.5" stroke="url(#hdg)" strokeWidth="2" strokeLinecap="round"/>
          </svg>
        </div>
        <span className="cp-header-name">CléPass</span>
        <SaveIndicator state={saveState} />
        <div className="cp-header-spacer" />
        <span className="cp-header-email">{sessionInfo.email}</span>
        <button className="cp-header-btn" onClick={onLock} title="Verrouiller">
          <Icon name="lock" size={15} />
        </button>
      </header>

      <main className="cp-main">
        {sidebarOpen && (
          <div className="cp-sidebar-overlay" onClick={() => setSidebarOpen(false)} />
        )}
        <Sidebar
          filters={filters}
          folders={folders}
          activeFilter={activeFilter}
          activeFolder={activeFolder}
          onFilter={(id) => { setActiveFilter(id); setActiveFolder(null); setSidebarOpen(false); }}
          onFolder={(id) => { setActiveFolder(id); setSidebarOpen(false); }}
          expanded={expanded}
          onToggleFolder={(id) => setExpanded(x => ({ ...x, [id]: !x[id] }))}
          dark={t.theme === 'dark'}
          isOpen={sidebarOpen}
        />
        <PasswordList
          entries={filtered}
          selected={selectedId}
          onSelect={setSelectedId}
          query={query}
          onQuery={setQuery}
          count={filtered.length}
          title={titleForView}
          onAdd={() => setModal('add-password')}
          onAddWifi={() => setModal('add-wifi')}
        />
        {isEmpty ? (
          <EmptyVault onAddPassword={() => setModal('add-password')}
                      onAddWifi={() => setModal('add-wifi')}
                      onImportDemo={importDemo} />
        ) : selected && selected.type === 'wifi' ? (
          <WifiDetail
            entry={selected}
            onEncryptionInfo={() => setEncOpen(true)}
            onEdit={() => { setEditTarget(selected); setModal('edit-wifi'); }}
            onDelete={() => deleteEntry(selected.id)}
            onToggleFavorite={() => toggleFavorite(selected.id)}
            onBack={() => setSelectedId(null)}
          />
        ) : selected ? (
          <Detail
            entry={selected}
            onEncryptionInfo={() => setEncOpen(true)}
            onEdit={() => { setEditTarget(selected); setModal('edit-password'); }}
            onDelete={() => deleteEntry(selected.id)}
            onToggleFavorite={() => toggleFavorite(selected.id)}
            onUpdate={updateEntry}
            onBack={() => setSelectedId(null)}
          />
        ) : (
          <Detail entry={null} onEncryptionInfo={() => setEncOpen(true)} />
        )}
      </main>

      <EncryptionModal open={encOpen} onClose={() => setEncOpen(false)} />

      {/* Modals CRUD */}
      {(modal === 'add-password' || modal === 'edit-password') && (
        <EntryModal
          mode={modal === 'add-password' ? 'add' : 'edit'}
          target={modal === 'edit-password' ? editTarget : null}
          folders={vault.folders || []}
          onSave={(entry) => {
            if (modal === 'add-password') return addEntry(entry).then(() => setModal(null));
            return updateEntry(entry).then(() => setModal(null));
          }}
          onCancel={() => { setModal(null); setEditTarget(null); }}
        />
      )}
      {(modal === 'add-wifi' || modal === 'edit-wifi') && (
        <WifiModal
          mode={modal === 'add-wifi' ? 'add' : 'edit'}
          target={modal === 'edit-wifi' ? editTarget : null}
          folders={vault.folders || []}
          onSave={(entry) => {
            if (modal === 'add-wifi') return addEntry(entry).then(() => setModal(null));
            return updateEntry(entry).then(() => setModal(null));
          }}
          onCancel={() => { setModal(null); setEditTarget(null); }}
        />
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Apparence" />
        <TweakRadio label="Thème" value={t.theme}
          options={[{ value: 'dark', label: 'Sombre' }, { value: 'light', label: 'Clair' }]}
          onChange={(v) => setTweak('theme', v)} />
        <TweakSelect label="Couleur d'accent" value={t.accent}
          options={[
            { value: 'indigo-rose', label: 'Indigo → Rose' },
            { value: 'emerald',     label: 'Émeraude' },
            { value: 'amber',       label: 'Ambre' },
            { value: 'violet',      label: 'Violet' },
            { value: 'crimson',     label: 'Crimson' },
          ]}
          onChange={(v) => setTweak('accent', v)} />

        <TweakSection label="Sécurité" />
        <TweakButton label="Voir le chiffrement E2E" onClick={() => setEncOpen(true)} />
        <TweakButton label="Verrouiller" secondary onClick={onLock} />
        <TweakButton label="Changer de mot de passe maître" secondary onClick={onChangeMaster} />
        <TweakButton label="Se déconnecter" secondary onClick={onLogout} />
      </TweaksPanel>
    </div>
  );
}

// ============================================================================
//  Indicateur de sauvegarde dans la titlebar
// ============================================================================
function SaveIndicator({ state }) {
  if (state.status === 'idle') return null;
  return (
    <span className={`cp-save-pill cp-save-${state.status}`}>
      {state.status === 'saving' && <span className="cp-spin cp-spin-sm" />}
      {state.status === 'saved'  && <Icon name="check" size={11} />}
      {state.status === 'error'  && <Icon name="alert" size={11} />}
      <span>{state.message}</span>
    </span>
  );
}

// ============================================================================
//  EmptyVault — état vide accueillant + import démo
// ============================================================================
function EmptyVault({ onAddPassword, onAddWifi, onImportDemo }) {
  return (
    <section className="cp-detail cp-detail-empty">
      <div style={{ maxWidth: 380 }}>
        <div className="cp-empty-ico"><Icon name="key" size={32} /></div>
        <h3>Votre coffre est vide</h3>
        <p style={{ marginBottom: 18 }}>
          Ajoutez un mot de passe ou un Wi-Fi pour commencer. Tout est chiffré
          dans ce navigateur avant d'être envoyé au serveur.
        </p>
        <div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap' }}>
          <button className="cp-btn cp-btn-soft" onClick={onAddPassword}>
            <Icon name="plus" size={13} /><span>Ajouter un mot de passe</span>
          </button>
          <button className="cp-btn" onClick={onAddWifi}>
            <Icon name="wifi" size={13} /><span>Ajouter un Wi-Fi</span>
          </button>
        </div>
        {window.CLEPASS_DEMO_DATA && (
          <div style={{ marginTop: 22 }}>
            <button className="cp-auth-link" onClick={onImportDemo}>
              Charger un coffre de démonstration
            </button>
          </div>
        )}
      </div>
    </section>
  );
}

// ============================================================================
//  App — composant racine, gère le cycle de vie
// ============================================================================
function App() {
  const [phase, setPhase] = useState('checking'); // checking|auth|app
  const [authMode, setAuthMode] = useState('login');
  const [sessionInfo, setSessionInfo] = useState(null);

  // Données déchiffrées (mémoire seulement).
  const [encKey, setEncKey] = useState(null);
  const [vault, setVault] = useState(null);
  const [vaultVersion, setVaultVersion] = useState(0);

  // Vérification de session au démarrage.
  useEffect(() => {
    (async () => {
      try {
        const me = await CPApi.me();
        setSessionInfo({ email: me.email, kdfSalt: me.kdf_salt, kdfParams: me.kdf_params });
        setAuthMode('unlock');
        setPhase('auth');
      } catch (e) {
        setAuthMode('login');
        setPhase('auth');
      }
    })();
  }, []);

  function handleAuthenticated(result) {
    setSessionInfo({ email: result.email, kdfSalt: result.kdfSalt, kdfParams: result.kdfParams });
    setEncKey(result.encKey);
    setVault(result.vault);
    setVaultVersion(result.vaultVersion);
    setPhase('app');
  }

  function handleLock() {
    if (encKey) CPCrypto.wipe(encKey);
    setEncKey(null);
    setVault(null);
    setVaultVersion(0);
    setAuthMode('unlock');
    setPhase('auth');
  }

  async function handleLogout() {
    try { await CPApi.logout(); } catch {}
    if (encKey) CPCrypto.wipe(encKey);
    setEncKey(null);
    setVault(null);
    setVaultVersion(0);
    setSessionInfo(null);
    setAuthMode('login');
    setPhase('auth');
  }

  function handleChangeMaster() {
    // v1 : on annonce et on déconnecte. Le change-password complet sera dans
    // une modal dédiée. Le bouton est présent pour signaler la fonction côté
    // serveur (handler /change-password déjà implémenté).
    alert(
      "Changement de mot de passe maître : pour v1, déconnectez-vous puis " +
      "utilisez un client API ou attendez la modal dédiée."
    );
  }

  // Pousser une nouvelle version du coffre (chiffrement local + PUT /vault).
  const pushVault = useCallback(async (newVault) => {
    if (!encKey) throw new Error('Coffre verrouillé');
    const { blob, iv } = await CPCrypto.encryptJSON(encKey, newVault);
    const r = await CPApi.vaultPut({
      vault_blob: blob,
      vault_iv: iv,
      expected_version: vaultVersion,
    });
    setVault(newVault);
    setVaultVersion(r.vault_version);
  }, [encKey, vaultVersion]);

  if (phase === 'checking') return <Splash />;

  if (phase === 'auth') {
    return <AuthScreen
      initialMode={authMode}
      sessionInfo={sessionInfo}
      onAuthenticated={handleAuthenticated}
      onSwitchAccount={authMode === 'unlock' ? handleLogout : undefined}
    />;
  }

  return (
    <Vault
      vault={vault}
      vaultVersion={vaultVersion}
      encKey={encKey}
      sessionInfo={sessionInfo}
      onMutate={pushVault}
      onLock={handleLock}
      onLogout={handleLogout}
      onChangeMaster={handleChangeMaster}
    />
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
