// Point d'entrée de l'extension : câble auth ↔ REST ↔ WS ↔ store ↔ surfaces natives (arbres, terminaux,
// status bar, notifications, workspace). C'est le SEUL gros consommateur de l'API vscode avec les vues ;
// toute la logique réutilisable (protocole, état) vit dans des modules purs testables.
import * as vscode from 'vscode';
import { Auth } from './auth/auth.js';
import { RestClient } from './api/rest-client.js';
import { ArbWsClient } from './api/ws-client.js';
import { wsSocketFactory } from './api/ws-socket.js';
import { Store } from './state/store.js';
import { SessionTerminalManager } from './terminal/session-pty.js';
import { ReposTreeProvider } from './views/repos-tree.js';
import { GroupsTreeProvider } from './views/groups-tree.js';
import { StatusBar } from './status-bar.js';
import { WaitingNotifier } from './notifications.js';
import { WorkspaceMapper } from './workspace.js';
import { answerSession, registerCommands } from './commands.js';

function readUrl(): string {
  return vscode.workspace.getConfiguration('arboretum').get<string>('url', 'http://127.0.0.1:7317');
}
function readShowExternal(): boolean {
  return vscode.workspace.getConfiguration('arboretum').get<boolean>('showExternalSessions', false);
}
function readShowArchived(): boolean {
  return vscode.workspace.getConfiguration('arboretum').get<boolean>('showArchivedSessions', false);
}
function readNotify(): boolean {
  return vscode.workspace.getConfiguration('arboretum').get<boolean>('notifyOnWaiting', true);
}

async function setConnectedContext(connected: boolean): Promise<void> {
  await vscode.commands.executeCommand('setContext', 'arboretum.connected', connected);
}

export async function activate(context: vscode.ExtensionContext): Promise<void> {
  const log = vscode.window.createOutputChannel('Arboretum');
  context.subscriptions.push(log);

  const auth = new Auth(context.secrets);
  const store = new Store();
  store.showExternalSessions = readShowExternal();
  store.showArchivedSessions = readShowArchived();
  const rest = new RestClient({ baseUrl: readUrl() });

  const reseed = async (): Promise<void> => {
    try {
      await store.seed(rest);
    } catch (err) {
      log.appendLine(`seed failed: ${(err as Error).message}`);
    }
  };

  const statusBar = new StatusBar(store);
  context.subscriptions.push(statusBar);

  const ws = new ArbWsClient({
    socketFactory: wsSocketFactory,
    onSession: (e) => store.applySession(e),
    onWorktree: (e) => store.applyWorktree(e),
    onGroup: (e) => store.applyGroup(e),
    onSessionArchived: (id) => store.archiveSession(id),
    onReady: () => void reseed(),
    onStatus: (s) => {
      const connected = s === 'open';
      statusBar.setConnected(connected);
      void setConnectedContext(connected);
    },
    log: (m) => log.appendLine(m),
  });

  const terminals = new SessionTerminalManager(ws);
  context.subscriptions.push({ dispose: () => terminals.dispose() });

  const reposProvider = new ReposTreeProvider(store);
  const groupsProvider = new GroupsTreeProvider(store);
  const reposView = vscode.window.createTreeView('arboretum.repos', { treeDataProvider: reposProvider, showCollapseAll: true });
  const groupsView = vscode.window.createTreeView('arboretum.groups', { treeDataProvider: groupsProvider, showCollapseAll: true });
  context.subscriptions.push(reposView, groupsView);

  const workspace = new WorkspaceMapper(store, reposView, reposProvider);
  context.subscriptions.push(workspace);

  const signIn = async (): Promise<void> => {
    const token = await vscode.window.showInputBox({
      prompt: `Arboretum token for ${rest.url}`,
      password: true,
      ignoreFocusOut: true,
      placeHolder: 'arb_…  (printed once on first daemon start, or created in Settings → Tokens)',
    });
    if (!token) return;
    // valide le token avant de le stocker (sonde isolée pour ne pas polluer le client principal).
    const probe = new RestClient({ baseUrl: rest.url, token });
    try {
      await probe.me();
    } catch (err) {
      void vscode.window.showErrorMessage(`Arboretum: sign in failed: ${(err as Error).message}`);
      return;
    }
    await auth.storeToken(token);
    rest.setToken(token);
    ws.configure(rest.url, token);
    ws.connect();
    void vscode.window.showInformationMessage('Arboretum: connected.');
  };

  const signOut = async (): Promise<void> => {
    await auth.clearToken();
    rest.setToken(null);
    ws.disconnect();
    store.clear();
    await setConnectedContext(false);
    void vscode.window.showInformationMessage('Arboretum: signed out.');
  };

  registerCommands(context, { store, rest, ws, terminals, workspace, signIn, signOut, reseed });

  // Notifications : ouvrir le terminal ou répondre Yes/No (réutilise le helper transient-attach).
  const notifier = new WaitingNotifier(store, {
    notifyEnabled: () => readNotify(),
    openTerminal: (session) => terminals.open(session, 'interactive'),
    answer: (session, action) => {
      void answerSession({ store, rest, ws, terminals, workspace, signIn, signOut, reseed }, session, action).catch((err: Error) =>
        log.appendLine(`answer failed: ${err.message}`),
      );
    },
  });
  context.subscriptions.push(notifier);

  // Réglages à chaud : URL (reconfigure REST+WS), sessions externes (refiltre), notif (lu à la volée).
  context.subscriptions.push(
    vscode.workspace.onDidChangeConfiguration(async (e) => {
      if (e.affectsConfiguration('arboretum.url')) {
        rest.setBaseUrl(readUrl());
        const token = await auth.getToken();
        if (token) ws.configure(rest.url, token);
      }
      if (e.affectsConfiguration('arboretum.showExternalSessions')) {
        store.setShowExternalSessions(readShowExternal());
      }
      if (e.affectsConfiguration('arboretum.showArchivedSessions')) {
        store.setShowArchivedSessions(readShowArchived());
        await reseed(); // recharge la liste avec/sans les sessions archivées (includeArchived)
      }
    }),
  );

  // Connexion initiale si un token est déjà mémorisé.
  await setConnectedContext(false);
  const token = await auth.getToken();
  if (token) {
    rest.setToken(token);
    ws.configure(rest.url, token);
    ws.connect();
  }

  context.subscriptions.push({ dispose: () => ws.disconnect() });
}

export function deactivate(): void {
  // tout est enregistré dans context.subscriptions → disposé par VS Code.
}
