// Store IDE : onglets (ouverture/dedup/fermeture), dock terminaux, activité, réconciliation.
import { describe, it, expect, beforeEach } from 'vitest';
import { createPinia, setActivePinia } from 'pinia';
import { useIdeStore, tabId, wtKey } from '../src/stores/ide';

function fakeStorage() {
  const m = new Map<string, string>();
  return {
    getItem: (k: string) => (m.has(k) ? (m.get(k) as string) : null),
    setItem: (k: string, v: string) => void m.set(k, v),
    removeItem: (k: string) => void m.delete(k),
    clear: () => m.clear(),
  };
}

describe('store IDE', () => {
  beforeEach(() => {
    (globalThis as unknown as { localStorage: unknown }).localStorage = fakeStorage();
    setActivePinia(createPinia());
  });

  it('openFile ouvre un onglet, fixe actif + contexte', () => {
    const ide = useIdeStore();
    ide.openFile('r1', '/wt/a', 'src/x.ts');
    expect(ide.editorTabs).toHaveLength(1);
    expect(ide.activeTabId).toBe(tabId('r1', '/wt/a', 'src/x.ts'));
    expect(ide.activeContext).toEqual({ repoId: 'r1', wtPath: '/wt/a' });
    expect(ide.activeTab?.file).toBe('src/x.ts');
  });

  it('openFile est idempotent (pas de doublon), refocalise l onglet existant', () => {
    const ide = useIdeStore();
    ide.openFile('r1', '/wt/a', 'x.ts');
    ide.openFile('r1', '/wt/b', 'y.ts');
    ide.openFile('r1', '/wt/a', 'x.ts'); // ré-ouverture
    expect(ide.editorTabs).toHaveLength(2);
    expect(ide.activeTabId).toBe(tabId('r1', '/wt/a', 'x.ts'));
  });

  it('closeTab recalcule l onglet actif sur le voisin', () => {
    const ide = useIdeStore();
    ide.openFile('r', '/w', 'a');
    ide.openFile('r', '/w', 'b');
    ide.openFile('r', '/w', 'c'); // actif = c
    ide.setActiveTab(tabId('r', '/w', 'b')); // actif = b (index 1)
    ide.closeTab(tabId('r', '/w', 'b'));
    expect(ide.editorTabs).toHaveLength(2);
    // le voisin qui a glissé à l index 1 est c
    expect(ide.activeTabId).toBe(tabId('r', '/w', 'c'));
  });

  it('closeTab sur le dernier onglet vide l actif', () => {
    const ide = useIdeStore();
    ide.openFile('r', '/w', 'a');
    ide.closeTab(tabId('r', '/w', 'a'));
    expect(ide.editorTabs).toHaveLength(0);
    expect(ide.activeTabId).toBeNull();
  });

  it('closeTabsForWorktree retire tous les onglets d un worktree', () => {
    const ide = useIdeStore();
    ide.openFile('r', '/w1', 'a');
    ide.openFile('r', '/w1', 'b');
    ide.openFile('r', '/w2', 'c');
    ide.closeTabsForWorktree('r', '/w1');
    expect(ide.editorTabs.map((t) => t.file)).toEqual(['c']);
  });

  it('moveTab réordonne', () => {
    const ide = useIdeStore();
    ide.openFile('r', '/w', 'a');
    ide.openFile('r', '/w', 'b');
    ide.openFile('r', '/w', 'c');
    ide.moveTab(0, 2); // a -> fin
    expect(ide.editorTabs.map((t) => t.file)).toEqual(['b', 'c', 'a']);
  });

  it('setTabDirty alimente hasUnsaved / isDirty', () => {
    const ide = useIdeStore();
    ide.openFile('r', '/w', 'a');
    const id = tabId('r', '/w', 'a');
    expect(ide.hasUnsaved).toBe(false);
    ide.setTabDirty(id, true);
    expect(ide.isDirty(id)).toBe(true);
    expect(ide.hasUnsaved).toBe(true);
    ide.setTabDirty(id, false);
    expect(ide.hasUnsaved).toBe(false);
  });

  it('dock : openTerminal ajoute + focus + ouvre le dock ; sans doublon', () => {
    const ide = useIdeStore();
    ide.openTerminal('s1');
    ide.openTerminal('s2');
    ide.openTerminal('s1'); // déjà présent
    expect(ide.dockSessionIds).toEqual(['s1', 's2']);
    expect(ide.activeDockSessionId).toBe('s1');
    expect(ide.bottomVisible).toBe(true);
  });

  it('dock : closeTerminal recalcule l actif et referme si vide', () => {
    const ide = useIdeStore();
    ide.openTerminal('s1');
    ide.openTerminal('s2');
    ide.focusTerminal('s2');
    ide.closeTerminal('s2');
    expect(ide.dockSessionIds).toEqual(['s1']);
    expect(ide.activeDockSessionId).toBe('s1');
    ide.closeTerminal('s1');
    expect(ide.dockSessionIds).toEqual([]);
    expect(ide.activeDockSessionId).toBeNull();
    expect(ide.bottomVisible).toBe(false);
  });

  it('toggleActivity : re-clic sur la vue active replie le panneau', () => {
    const ide = useIdeStore();
    expect(ide.activeActivity).toBe('explorer');
    expect(ide.leftVisible).toBe(true);
    ide.toggleActivity('explorer'); // même vue, ouverte -> repli
    expect(ide.leftVisible).toBe(false);
    ide.toggleActivity('git'); // autre vue -> ouvre
    expect(ide.activeActivity).toBe('git');
    expect(ide.leftVisible).toBe(true);
  });

  it('pruneDeadResources retire onglets/terminaux morts ; dirty gardé pour les conservés, nettoyé pour les retirés', () => {
    const ide = useIdeStore();
    ide.openFile('r', '/w1', 'a'); // worktree connu -> gardé
    ide.openFile('r', '/w2', 'b'); // worktree inconnu -> retiré
    ide.setTabDirty(tabId('r', '/w1', 'a'), true);
    ide.setTabDirty(tabId('r', '/w2', 'b'), true);
    ide.openTerminal('sLive');
    ide.openTerminal('sDead');

    ide.pruneDeadResources(new Set(['sLive']), new Set([wtKey('r', '/w1')]));

    expect(ide.editorTabs.map((t) => t.wtPath)).toEqual(['/w1']);
    expect(ide.dockSessionIds).toEqual(['sLive']); // sDead retiré
    expect(ide.isDirty(tabId('r', '/w1', 'a'))).toBe(true); // onglet gardé : dirty conservé
    expect(ide.isDirty(tabId('r', '/w2', 'b'))).toBe(false); // onglet retiré : dirty nettoyé
    expect(ide.hasUnsaved).toBe(true); // il reste l'onglet /w1 modifié
  });
});
