// ── Gebruikslog: signalen voor bibliotheekdekking (fase 5 — observability) ───
// Houdt bij wanneer een agent vastloopt op de bibliotheek: onbekende id's of
// genegeerde facultatieve onderdelen. Geen persoonsgegevens — enkel id's en
// endpointnamen, nooit dossierinhoud. Dient om fase 3 (meer modeldocumenten/
// modelbrieven) en fase 4 (kwaliteitslus) te prioriteren. Zelfde
// opslagstrategie als voorstellen-opslag.ts: persistente Supabase-tabel als
// geconfigureerd, anders best-effort in-geheugen.

import { maakServiceClient, type AnyClient } from "@/lib/supabase/server";

export type GebruikslogType =
  | "onbekend_modeldocument"
  | "onbekende_modelbrief"
  | "onbekende_rechtshandeling"
  | "genegeerd_facultatief_onderdeel"
  | "onbekende_parameter"
  // Nieuwe lagen (AUT-F5). GDPR: waarde is een bronTYPE, veldpad of
  // dienstnaam — nooit een bestandsnaam/ref (kan een partijnaam bevatten)
  // en nooit dossierinhoud. De mappers (gebruikslogEventsUitPerceptie,
  // gebruikslogEventUitPoort) bewaken dat.
  | "perceptie_onbekende_bron"
  | "perceptie_veld_ontbreekt"
  | "perceptie_veld_tegenstrijdig"
  | "connector_fout"
  | "connector_geescaleerd"
  | "connector_verboden";

export interface GebruikslogEvent {
  type: GebruikslogType;
  /** Het opgevraagde maar onbekende of genegeerde id (geen persoonsgegevens). */
  waarde: string;
  tijdstip: string;
}

export interface GebruikslogOpslag {
  registreer(event: Omit<GebruikslogEvent, "tijdstip">): Promise<void>;
  lijst(): Promise<GebruikslogEvent[]>;
}

const MAX_GEHEUGEN_EVENTS = 500;

class GeheugenGebruikslog implements GebruikslogOpslag {
  private events: GebruikslogEvent[] = [];

  async registreer(event: Omit<GebruikslogEvent, "tijdstip">): Promise<void> {
    this.events.push({ ...event, tijdstip: new Date().toISOString() });
    if (this.events.length > MAX_GEHEUGEN_EVENTS) {
      this.events = this.events.slice(-MAX_GEHEUGEN_EVENTS);
    }
  }

  async lijst(): Promise<GebruikslogEvent[]> {
    return [...this.events];
  }
}

// Vereiste tabel (SQL, één keer uitvoeren in de Supabase SQL Editor):
//
//   create table gebruikslog (
//     id        bigint generated always as identity primary key,
//     type      text not null,
//     waarde    text not null,
//     tijdstip  timestamptz not null default now()
//   );

class SupabaseGebruikslog implements GebruikslogOpslag {
  private readonly sb: AnyClient;

  constructor(sb: AnyClient) {
    this.sb = sb;
  }

  async registreer(event: Omit<GebruikslogEvent, "tijdstip">): Promise<void> {
    const { error } = await this.sb.from("gebruikslog").insert({
      type: event.type,
      waarde: event.waarde,
    });
    if (error) throw new Error(`Supabase registreer: ${error.message}`);
  }

  async lijst(): Promise<GebruikslogEvent[]> {
    const { data, error } = await this.sb
      .from("gebruikslog")
      .select("type, waarde, tijdstip")
      .order("tijdstip", { ascending: false })
      .limit(MAX_GEHEUGEN_EVENTS);
    if (error) throw new Error(`Supabase lijst: ${error.message}`);
    return (data ?? []) as GebruikslogEvent[];
  }
}

function maakStandaardGebruikslog(): GebruikslogOpslag {
  const sb = maakServiceClient();
  return sb ? new SupabaseGebruikslog(sb) : new GeheugenGebruikslog();
}

let gebruikslog: GebruikslogOpslag | null = null;

/** Huidige gebruikslog-opslag (persistent via Supabase indien geconfigureerd). */
export function gebruikslogOpslag(): GebruikslogOpslag {
  if (!gebruikslog) gebruikslog = maakStandaardGebruikslog();
  return gebruikslog;
}

/** Vervangt de opslagimplementatie, bv. in tests. */
export function stelGebruikslogOpslagIn(nieuw: GebruikslogOpslag): void {
  gebruikslog = nieuw;
}

/**
 * Registreert een gebruikssignaal zonder de aanroepende request te laten
 * falen: best-effort, fouten worden genegeerd (telemetrie mag de
 * kernfunctionaliteit nooit blokkeren).
 */
export async function registreerGebruik(event: Omit<GebruikslogEvent, "tijdstip">): Promise<void> {
  try {
    await gebruikslogOpslag().registreer(event);
  } catch {
    // Best-effort: telemetrie mag nooit de eigenlijke respons blokkeren.
  }
}
