// Réglages exposés à l'UI (onglet Réglages). Frontière de sécurité CENTRALE : la table `settings`
// contient aussi des SECRETS (server_secret, vapid_private). Ces routes n'exposent QUE des champs
// non sensibles et n'écrivent QUE des clés explicitement allow-listées : jamais les secrets.
import type { FastifyInstance } from 'fastify';
import type { ServerInfo, SettingsResponse, UpdateSettingsRequest } from '@arboretum/shared';
import type { Config } from '../config.js';
import { type Db, setSetting } from '../db/index.js';
import type { PushService } from '../core/push-service.js';
import type { SettingsBus } from '../core/settings-bus.js';
import { recordAudit } from '../core/audit-log.js';
import { diagnoseClaudeBin } from '../core/claude-launcher.js';
import {
  SCAN_INTERVAL_KEY,
  SCAN_ROOTS_KEY,
  normalizeScanIntervalMin,
  normalizeScanRoots,
  readScanIntervalMin,
  readScanRoots,
} from '../core/scan-settings.js';
import {
  CLAUDE_BIN_PATH_KEY,
  CLAUDE_HOME_KEY,
  normalizeClaudeBinPath,
  normalizeClaudeHome,
  readClaudeBinPath,
  readClaudeHome,
} from '../core/claude-settings.js';
import {
  PURGE_DAYS_KEY,
  RETENTION_DAYS_KEY,
  normalizePurgeDays,
  normalizeRetentionDays,
  readPurgeDays,
  readRetentionDays,
} from '../core/retention-settings.js';

// Réglages modifiables via l'API (allow-list stricte). Les secrets ne figurent JAMAIS ici.
export function registerSettingsRoutes(
  app: FastifyInstance,
  db: Db,
  config: Config,
  serverVersion: string,
  push: PushService,
  settingsBus: SettingsBus,
): void {
  const serverInfo = (): ServerInfo => ({
    version: serverVersion,
    port: config.port,
    bind: config.bind,
    allowedOrigins: config.allowedOrigins,
    dataDir: config.dataDir,
    vapidPublicKey: push.publicKey() || null,
    vapidContact: config.vapidContact,
    claudeHome: config.claudeHome,
    // Diagnostic recalculé à chaque GET : reflète l'état réel (override exécutable ? `which claude` ?).
    claudeBin: diagnoseClaudeBin(readClaudeBinPath(db)),
  });
  const snapshot = (): SettingsResponse => ({
    settings: {
      scanRoots: readScanRoots(db),
      scanIntervalMin: readScanIntervalMin(db),
      claudeBinPath: readClaudeBinPath(db),
      claudeHome: readClaudeHome(db),
      retentionDays: readRetentionDays(db),
      purgeDays: readPurgeDays(db),
    },
    server: serverInfo(),
  });

  app.get('/api/v1/settings', async (): Promise<SettingsResponse> => snapshot());

  app.patch('/api/v1/settings', async (req, reply) => {
    const body = (req.body as Partial<UpdateSettingsRequest> | null) ?? {};
    if ('scanRoots' in body) {
      const roots = normalizeScanRoots(body.scanRoots);
      if (!roots) {
        return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'scanRoots must be an array of ≤16 absolute, normalized paths (never "/")' } });
      }
      setSetting(db, SCAN_ROOTS_KEY, JSON.stringify(roots));
    }
    if ('scanIntervalMin' in body) {
      const interval = normalizeScanIntervalMin(body.scanIntervalMin);
      if (interval === null) {
        return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'scanIntervalMin must be an integer between 0 and 1440' } });
      }
      setSetting(db, SCAN_INTERVAL_KEY, String(interval));
    }
    if ('claudeBinPath' in body) {
      const value = normalizeClaudeBinPath(body.claudeBinPath);
      if (value === null) {
        return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'claudeBinPath must be an absolute path to an executable file (or "" to reset)' } });
      }
      setSetting(db, CLAUDE_BIN_PATH_KEY, value);
    }
    if ('claudeHome' in body) {
      const value = normalizeClaudeHome(body.claudeHome);
      if (value === null) {
        return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'claudeHome must be an absolute path to an existing directory (or "" to reset)' } });
      }
      setSetting(db, CLAUDE_HOME_KEY, value);
    }
    if ('retentionDays' in body) {
      const value = normalizeRetentionDays(body.retentionDays);
      if (value === null) {
        return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'retentionDays must be 0 (never) or an integer between 1 and 3650' } });
      }
      setSetting(db, RETENTION_DAYS_KEY, String(value));
    }
    if ('purgeDays' in body) {
      const value = normalizePurgeDays(body.purgeDays);
      if (value === null) {
        return reply.status(400).send({ error: { code: 'BAD_REQUEST', message: 'purgeDays must be 0 (disabled) or an integer between 1 and 3650' } });
      }
      setSetting(db, PURGE_DAYS_KEY, String(value));
    }
    recordAudit(db, {
      actor: req.authContext?.tokenId ?? 'unknown',
      action: 'settings.update',
      details: { keys: Object.keys(body) },
    });
    const snap = snapshot();
    // P11 · diffuse le nouvel état non sensible à tous les clients abonnés au topic 'settings'.
    settingsBus.emit('settings_update', snap.settings);
    return reply.send(snap);
  });
}
