#!/usr/bin/env node
import { readFileSync, writeSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { loadConfig, type Config } from './config.js';
import { openDb, type Db } from './db/index.js';
import { buildApp } from './app.js';
import { readClaudeHome } from './core/claude-settings.js';
import { runInstall, runUninstall, runStatus, printUsage, printTokenBanner } from './cli/install.js';

const pkg = JSON.parse(
  readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'),
) as { version: string };

/**
 * Applique l'override `claude_home` (réglage UI) sur la config runtime, sauf si --claude-home a été
 * passé explicitement (le flag gagne). Lu une seule fois au démarrage → un changement via l'UI prend
 * effet au redémarrage du daemon (comme l'intervalle de scan). Mute `config`.
 */
function applyClaudeHomeOverride(config: Config, db: Db): void {
  if (config.claudeHomeFromFlag) return;
  const override = readClaudeHome(db);
  if (!override) return;
  config.claudeHome = override;
  config.claudeProjectsDir = join(override, 'projects');
  config.claudeSessionsDir = join(override, 'sessions');
}

/** Démarre le daemon : écoute HTTP, scan des sessions, drain propre au SIGTERM/SIGINT. */
export async function runDaemon(config: Config): Promise<void> {
  const db = openDb(config.dbPath);
  applyClaudeHomeOverride(config, db); // override claude_home (réglage UI) avant de câbler les services
  const { app, auth, manager, discovery, sessionArchive, repoDiscovery, worktrees, fsWatcher } = buildApp(config, db, pkg.version);

  const bootstrapToken = auth.ensureBootstrapToken();
  await app.listen({ port: config.port, host: config.bind });
  discovery.start(); // scan initial + rafraîchissement périodique des sessions découvertes
  sessionArchive.start(); // archivage auto des sessions terminées (rétention configurable)
  worktrees.armMainCheckoutWatchers(); // P11 : temps réel du checkout principal de chaque repo
  if (config.autoDiscover) repoDiscovery.start(); // découverte auto des repos : scan au boot + re-scan périodique

  const url = `http://${config.bind === '0.0.0.0' ? '127.0.0.1' : config.bind}:${config.port}`;
  app.log.info(`Arboretum v${pkg.version} : ${url}`);
  if (bootstrapToken) {
    // Affiché une seule fois : le hash seul est stocké.
    printTokenBanner(bootstrapToken, url);
  } else if (config.printToken) {
    console.log('Tokens are stored hashed and cannot be re-printed. Create a new one from Settings (or reset the db).');
  }

  // Handshake pour un shell embarquant le daemon (app de bureau) : si ARBORETUM_EMIT_TOKEN_FD est
  // fourni, on mint un token frais et on l'écrit en JSON sur ce descripteur (pipe stdio privé
  // parent<->enfant). Additif : sans cette variable, comportement strictement inchangé. Le token ne
  // transite jamais par le renderer et n'est pas affiché.
  const emitFd = process.env.ARBORETUM_EMIT_TOKEN_FD;
  if (emitFd) {
    try {
      const token = auth.createToken('desktop');
      writeSync(Number(emitFd), `${JSON.stringify({ token, url })}\n`);
    } catch (err) {
      app.log.warn(`token handshake failed: ${err instanceof Error ? err.message : String(err)}`);
    }
  }

  let shuttingDown = false;
  const shutdown = (signal: string): void => {
    if (shuttingDown) return;
    shuttingDown = true;
    app.log.info(`${signal} received: draining sessions then exiting`);
    discovery.stop();
    sessionArchive.stop();
    repoDiscovery.stop();
    void fsWatcher.closeAll(); // libère les descripteurs des watchers FS
    manager.shutdown();
    setTimeout(() => {
      void app.close().then(() => process.exit(0));
    }, 1000).unref();
  };
  process.on('SIGINT', () => shutdown('SIGINT'));
  process.on('SIGTERM', () => shutdown('SIGTERM'));
}

// Routeur de sous-commandes. On inspecte argv[0] AVANT loadConfig (parseArgs strict throw sur
// un positionnel inconnu). Aucune sous-commande (ou un flag en tête) → daemon : rétrocompat stricte
// de `arboretum`, `arboretum --port 8080`, `npx @johanleroy/git-arboretum --allow-origin …`.
async function main(): Promise<void> {
  const argv = process.argv.slice(2);
  const cmd = argv[0];
  switch (cmd) {
    case 'install':
      return runInstall(argv.slice(1));
    case 'uninstall':
      return runUninstall(argv.slice(1));
    case 'status':
      return runStatus(argv.slice(1));
    case 'serve':
      return runDaemon(loadConfig(argv.slice(1)));
    case 'help':
    case '--help':
    case '-h':
      printUsage(pkg.version);
      return;
    default:
      if (cmd && !cmd.startsWith('-')) {
        console.error(`Unknown command: ${cmd}\n`);
        printUsage(pkg.version);
        process.exit(1);
      }
      return runDaemon(loadConfig(argv));
  }
}

main().catch((err) => {
  console.error(err instanceof Error ? err.message : err);
  process.exit(1);
});
