// Append-only shard event log. The WS ingest dispatcher calls append() for the
// notable kinds; the public/admin read endpoints call list(). The DB layer only
// sees an already-computed dedupe_key so INSERT IGNORE is idempotent across
// WS-reconnect backfill.

const crypto = require('crypto')
const db = require('./shardEvents.db')

const MAX_LIMIT = 1000
const DEFAULT_LIMIT = 100

// Stable stringify — keys sorted — so the dedupe hash is independent of the
// property order the sidecar happened to serialize with.
function stableStringify(value) {
  if (value === null || typeof value !== 'object') return JSON.stringify(value)
  if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`
  const keys = Object.keys(value).sort()
  const entries = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`)
  return `{${entries.join(',')}}`
}

// dedupe_key = sha256(kind + t + stable-json(payload)), truncated to 40 hex chars.
// This is a content fingerprint for idempotent INSERT IGNORE, not a security value,
// but we use SHA-256 rather than SHA-1 anyway; the truncation keeps it inside the
// CHAR(40) column (160 bits is ample collision resistance for dedupe). Two identical
// events (same kind, same timestamp, same body) collapse to one row.
function dedupeKey(kind, t, payload) {
  return crypto
    .createHash('sha256')
    .update(`${kind}|${t}|${stableStringify(payload)}`)
    .digest('hex')
    .slice(0, 40)
}

// Append one event. Returns true if a new row was inserted (false = deduped).
async function append({ kind, t, bootId, payload }) {
  return db.insertIgnore({ kind, t, bootId, payload, dedupeKey: dedupeKey(kind, t, payload) })
}

function normalizeLimit(limit) {
  const n = Number(limit)
  if (!Number.isFinite(n) || n <= 0) return DEFAULT_LIMIT
  return Math.min(Math.floor(n), MAX_LIMIT)
}

// Recent events, newest first. Each row's JSON payload is parsed back to an
// object. `kinds` (array) restricts to an allowlist; `kind` filters a single kind.
async function list({ kind, kinds, limit } = {}) {
  const rows = await db.list({ kind, kinds, limit: normalizeLimit(limit) })
  return rows.map((row) => ({
    id: row.id,
    kind: row.kind,
    t: row.t,
    bootId: row.boot_id || null,
    // mariadb returns JSON columns as strings on some versions; parse defensively.
    payload: typeof row.payload === 'string' ? JSON.parse(row.payload) : row.payload,
    createdAt: row.created_at,
  }))
}

module.exports = { append, list, dedupeKey }
