// Archivage automatique des sessions terminées (P10). Scheduler calqué sur DiscoveryService :
// start()/stop() + setInterval().unref(). Soft-archive uniquement (jamais de DELETE dans cette
// itération ; la purge définitive est conçue mais désactivée par défaut, cf. retention-settings).
// Démarré dans runDaemon (jamais dans buildApp → ne tourne pas pendant les tests unitaires).
import { EventEmitter } from 'node:events';
import { archiveExpiredSessions, type Db } from '../db/index.js';
import { recordAudit } from './audit-log.js';
import { readRetentionDays } from './retention-settings.js';

/** Balayage toutes les 6 h : la rétention se compte en jours, pas besoin de plus fréquent. */
const DEFAULT_SWEEP_INTERVAL_MS = 6 * 60 * 60 * 1000;
const MS_PER_DAY = 24 * 60 * 60 * 1000;

export interface SessionArchiveEvents {
  /** une session vient d'être archivée (relayée en `session_archived` par la gateway). */
  session_archived: [{ id: string; archivedAt: string }];
}

export interface SessionArchiveOptions {
  db: Db;
  intervalMs?: number;
}

export class SessionArchiveService extends EventEmitter<SessionArchiveEvents> {
  private readonly db: Db;
  private readonly intervalMs: number;
  private timer: NodeJS.Timeout | null = null;

  constructor(opts: SessionArchiveOptions) {
    super();
    this.db = opts.db;
    this.intervalMs = opts.intervalMs ?? DEFAULT_SWEEP_INTERVAL_MS;
  }

  start(): void {
    if (this.timer) return;
    this.sweep(); // passage immédiat au boot (synchrone, bon marché : un seul UPDATE borné)
    this.timer = setInterval(() => this.sweep(), this.intervalMs);
    this.timer.unref(); // ne maintient pas le process en vie
  }

  stop(): void {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }

  /**
   * Archive les sessions terminées plus anciennes que la rétention configurée. Retourne le nombre
   * archivé. Tolérant : ne lève jamais (une erreur ne doit pas tuer le scheduler). `0 jour` = OFF.
   */
  sweep(): number {
    try {
      const days = readRetentionDays(this.db);
      if (days <= 0) return 0; // rétention désactivée
      const now = new Date();
      const archivedAt = now.toISOString();
      const cutoffIso = new Date(now.getTime() - days * MS_PER_DAY).toISOString();
      const ids = archiveExpiredSessions(this.db, archivedAt, cutoffIso);
      for (const id of ids) {
        this.emit('session_archived', { id, archivedAt });
        recordAudit(this.db, { actor: 'system', action: 'session.autoArchive', resourceId: id });
      }
      return ids.length;
    } catch {
      return 0;
    }
  }

  /** Diffuse l'archivage d'une session (déjà archivée + auditée par l'appelant, ex. route manuelle). */
  emitArchived(id: string, archivedAt: string): void {
    this.emit('session_archived', { id, archivedAt });
  }
}
