/**
 * CONTACT-HAMILTONIAN GATE — AUDIT LEDGER
 * USPTO 19/730,900 — DCGP.AI LLC
 * 
 * Every gate decision is logged immutably to Upstash KV.
 * Key schema: aura115:gate-audit:{YYYY-MM-DD}:{uuid}
 * 
 * Uses direct Upstash REST — never @vercel/kv (known broken in this stack).
 * 
 * The ledger serves three purposes:
 *   1. Reduction to practice (patent evidence)
 *   2. Constitutional compliance audit trail
 *   3. Drift detection over time (H and L trends)
 */

import type { GateDecision } from "./hamiltonian-gate";

// ── Types ──────────────────────────────────────────────────────────────

export interface AuditEntry {
  id: string;                      // unique audit entry ID
  session_id: string;              // inference session identifier
  sequence_number: number;         // gate evaluation order within session
  decision: GateDecision;          // full gate decision payload
  model_id?: string;               // which model generated the output under evaluation
  input_hash?: string;             // SHA-256 of the prompt/input that triggered evaluation
  metadata?: Record<string, unknown>;
}

export interface AuditQuery {
  date?: string;                   // YYYY-MM-DD — filter to specific day
  session_id?: string;             // filter to specific session
  admitted_only?: boolean;         // only admitted decisions
  rejected_only?: boolean;         // only rejected decisions
  limit?: number;                  // max results (default 50)
}

export interface AuditStats {
  total_evaluations: number;
  admitted: number;
  rejected: number;
  admission_rate: number;
  mean_H: number;
  mean_L: number;
  max_H: number;
  max_L: number;
  dominant_defect_coordinate: string | null;
  K_versions_seen: string[];
}

export interface UpstashConfig {
  url: string;                     // e.g. https://funny-whale-134857.upstash.io
  token: string;                   // Upstash REST token
}

// ── Upstash REST client ────────────────────────────────────────────────

async function upstashCommand(
  config: UpstashConfig,
  command: string[]
): Promise<unknown> {
  const res = await fetch(`${config.url}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${config.token}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(command),
  });

  if (!res.ok) {
    const text = await res.text();
    throw new Error(`Upstash REST error ${res.status}: ${text}`);
  }

  const data = await res.json();
  if (data.error) {
    throw new Error(`Upstash error: ${data.error}`);
  }

  return data.result;
}

// ── UUID generation (no external deps) ─────────────────────────────────

function generateId(): string {
  const hex = (n: number) =>
    Array.from({ length: n }, () =>
      Math.floor(Math.random() * 16).toString(16)
    ).join("");
  return `${hex(8)}-${hex(4)}-${hex(4)}-${hex(4)}-${hex(12)}`;
}

// ── Ledger operations ──────────────────────────────────────────────────

/**
 * Log a gate decision to the audit ledger.
 * 
 * Key schema:
 *   aura115:gate-audit:{YYYY-MM-DD}:{id}     → full entry JSON
 *   aura115:gate-audit:index:{YYYY-MM-DD}     → sorted set of entry IDs by timestamp
 *   aura115:gate-audit:session:{session_id}   → list of entry IDs in this session
 *   aura115:gate-audit:counter                → total evaluation count
 */
export async function logGateDecision(
  config: UpstashConfig,
  decision: GateDecision,
  session_id: string,
  sequence_number: number,
  options?: {
    model_id?: string;
    input_hash?: string;
    metadata?: Record<string, unknown>;
  }
): Promise<AuditEntry> {
  const id = generateId();
  const date = decision.timestamp.slice(0, 10); // YYYY-MM-DD
  const epochMs = new Date(decision.timestamp).getTime();

  const entry: AuditEntry = {
    id,
    session_id,
    sequence_number,
    decision,
    model_id: options?.model_id,
    input_hash: options?.input_hash,
    metadata: options?.metadata,
  };

  const entryJson = JSON.stringify(entry);
  const entryKey = `aura115:gate-audit:${date}:${id}`;
  const indexKey = `aura115:gate-audit:index:${date}`;
  const sessionKey = `aura115:gate-audit:session:${session_id}`;
  const counterKey = `aura115:gate-audit:counter`;

  // Write entry
  await upstashCommand(config, ["SET", entryKey, entryJson]);

  // Add to daily sorted index (score = epoch ms for time ordering)
  await upstashCommand(config, ["ZADD", indexKey, epochMs.toString(), id]);

  // Add to session list
  await upstashCommand(config, ["RPUSH", sessionKey, id]);

  // Increment global counter
  await upstashCommand(config, ["INCR", counterKey]);

  return entry;
}

/**
 * Retrieve a single audit entry by ID and date.
 */
export async function getAuditEntry(
  config: UpstashConfig,
  date: string,
  id: string
): Promise<AuditEntry | null> {
  const key = `aura115:gate-audit:${date}:${id}`;
  const raw = await upstashCommand(config, ["GET", key]);
  if (!raw || typeof raw !== "string") return null;
  return JSON.parse(raw) as AuditEntry;
}

/**
 * Retrieve all audit entries for a given date, ordered by timestamp.
 */
export async function getAuditEntriesByDate(
  config: UpstashConfig,
  date: string,
  limit: number = 50
): Promise<AuditEntry[]> {
  const indexKey = `aura115:gate-audit:index:${date}`;

  // Get entry IDs from sorted set (oldest first)
  const ids = (await upstashCommand(config, [
    "ZRANGE", indexKey, "0", (limit - 1).toString()
  ])) as string[] | null;

  if (!ids || ids.length === 0) return [];

  const entries: AuditEntry[] = [];
  for (const id of ids) {
    const entry = await getAuditEntry(config, date, id);
    if (entry) entries.push(entry);
  }

  return entries;
}

/**
 * Retrieve all audit entries for a session.
 */
export async function getAuditEntriesBySession(
  config: UpstashConfig,
  session_id: string
): Promise<AuditEntry[]> {
  const sessionKey = `aura115:gate-audit:session:${session_id}`;

  const ids = (await upstashCommand(config, [
    "LRANGE", sessionKey, "0", "-1"
  ])) as string[] | null;

  if (!ids || ids.length === 0) return [];

  // We need dates to look up entries — extract from decision timestamps
  // For efficiency, we'll scan recent days. In production, store date with session index.
  const entries: AuditEntry[] = [];
  const today = new Date().toISOString().slice(0, 10);

  for (const id of ids) {
    // Try today first, then fall back to scanning
    const entry = await getAuditEntry(config, today, id);
    if (entry) entries.push(entry);
  }

  return entries;
}

/**
 * Compute aggregate statistics from a set of audit entries.
 */
export function computeAuditStats(entries: AuditEntry[]): AuditStats {
  if (entries.length === 0) {
    return {
      total_evaluations: 0,
      admitted: 0,
      rejected: 0,
      admission_rate: 0,
      mean_H: 0,
      mean_L: 0,
      max_H: 0,
      max_L: 0,
      dominant_defect_coordinate: null,
      K_versions_seen: [],
    };
  }

  let admitted = 0;
  let sumH = 0;
  let sumL = 0;
  let maxH = 0;
  let maxL = 0;
  const defectSums: { [id: string]: number } = {};
  const kVersions = new Set<string>();

  for (const entry of entries) {
    const d = entry.decision;
    if (d.admitted) admitted++;
    sumH += d.H;
    sumL += d.L;
    if (d.H > maxH) maxH = d.H;
    if (d.L > maxL) maxL = d.L;
    kVersions.add(d.K_version);

    for (const [coord, defect] of Object.entries(d.defects)) {
      defectSums[coord] = (defectSums[coord] || 0) + defect;
    }
  }

  // Find dominant defect coordinate (highest cumulative defect)
  let dominant: string | null = null;
  let maxDefectSum = 0;
  for (const [coord, sum] of Object.entries(defectSums)) {
    if (sum > maxDefectSum) {
      maxDefectSum = sum;
      dominant = coord;
    }
  }

  const n = entries.length;
  return {
    total_evaluations: n,
    admitted,
    rejected: n - admitted,
    admission_rate: admitted / n,
    mean_H: sumH / n,
    mean_L: sumL / n,
    max_H: maxH,
    max_L: maxL,
    dominant_defect_coordinate: dominant,
    K_versions_seen: Array.from(kVersions),
  };
}

/**
 * Get the total number of gate evaluations ever recorded.
 */
export async function getTotalEvaluationCount(
  config: UpstashConfig
): Promise<number> {
  const raw = await upstashCommand(config, ["GET", "aura115:gate-audit:counter"]);
  if (!raw) return 0;
  return parseInt(raw as string, 10);
}
