// BITACORA DE EVENTOS. La Providencia 000121 (articulo 3) exige registro de
// eventos: quien hizo que, cuando y desde donde. Va encadenada por hash igual
// que el libro, porque un registro de auditoria que se puede editar no sirve
// de nada.
import { createHash } from 'node:crypto';
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, writeSync } from 'node:fs';
import { dirname } from 'node:path';
import type { Evento } from './tipos.js';

const RAIZ = '0'.repeat(64);

function huella(e: Omit<Evento, 'hash'>): string {
  const base = [e.seq, e.en, e.quien, e.desde, e.accion, JSON.stringify(e.detalle ?? {}), e.hash_anterior].join('');
  return createHash('sha256').update(base, 'utf8').digest('hex');
}

export class Bitacora {
  private readonly ruta: string;
  private eventos: Evento[] = [];

  constructor(ruta: string) {
    this.ruta = ruta;
    mkdirSync(dirname(this.ruta), { recursive: true });
    if (existsSync(this.ruta)) {
      this.eventos = readFileSync(this.ruta, 'utf8')
        .split('\n')
        .filter((l) => l.trim() !== '')
        .map((l) => JSON.parse(l) as Evento);
    }
  }

  /** Anota un evento. Como el libro: solo agrega, nunca edita. */
  anotar(quien: string, desde: string, accion: string, detalle?: Record<string, unknown>): Evento {
    const prev = this.eventos[this.eventos.length - 1];
    const sinHash: Omit<Evento, 'hash'> = {
      seq: (prev?.seq ?? 0) + 1,
      en: new Date().toISOString(),
      quien,
      desde,
      accion,
      detalle,
      hash_anterior: prev?.hash ?? RAIZ,
    };
    const evento: Evento = { ...sinHash, hash: huella(sinHash) };
    const fd = openSync(this.ruta, 'a');
    try {
      writeSync(fd, JSON.stringify(evento) + '\n');
      fsyncSync(fd);
    } finally {
      closeSync(fd);
    }
    this.eventos.push(evento);
    return evento;
  }

  todos(): Evento[] {
    return this.eventos.map((e) => ({ ...e }));
  }

  verificar(): { integro: boolean; problema?: string; linea?: number } {
    let anterior = RAIZ;
    for (let i = 0; i < this.eventos.length; i++) {
      const e = this.eventos[i];
      if (e.hash_anterior !== anterior) {
        return { integro: false, problema: 'la cadena de eventos esta rota', linea: i + 1 };
      }
      const { hash, ...resto } = e;
      if (huella(resto) !== hash) {
        return { integro: false, problema: 'un evento fue modificado', linea: i + 1 };
      }
      anterior = e.hash;
    }
    return { integro: true };
  }
}
