/**
 * Audit log — append-only record of tool invocations.
 *
 * Complements the per-edit action receipts (services/receipts/) with a
 * broader trail that covers every tool call, not just successful file
 * mutations. The pair is what makes the `/security-mode` preset
 * pitch-able for regulated environments: edits are tamper-evident
 * (receipts) AND the full chain of tool attempts is reconstructable
 * after the fact (this audit log).
 *
 * Schema v0 captures tool_name, input_digest, session_id, security
 * mode state at call time, and timestamp. Approval path ('auto' vs
 * 'user' vs 'denied') and result are deferred to v1 — they need
 * deeper hooks into canUseTool and the tool response path.
 */

import { createHash, randomUUID } from 'crypto'
import { appendFile, mkdir, readFile } from 'fs/promises'
import { dirname, join } from 'path'
import { getCwd } from '../../utils/cwd.js'
import { logForDebugging } from '../../utils/debug.js'

const AUDIT_FILE = '.sandcode/audit.jsonl'
const MAX_AUDIT_TO_LIST = 500

export interface AuditEntry {
  version: 1
  id: string
  timestamp: number
  session_id: string
  tool_name: string
  /** SHA-256 of the canonical JSON of the tool input. */
  input_digest: string
  /** Whether /security-mode was on when this entry was captured. */
  security_mode: boolean
  model?: string
}

function canonicalStringify(value: unknown): string {
  if (value === undefined) return 'null'
  if (value === null || typeof value !== 'object') {
    return JSON.stringify(value) ?? 'null'
  }
  if (Array.isArray(value)) {
    return '[' + value.map(canonicalStringify).join(',') + ']'
  }
  const keys = Object.keys(value as Record<string, unknown>).sort()
  return (
    '{' +
    keys
      .map(
        (k) => JSON.stringify(k) + ':' + canonicalStringify((value as Record<string, unknown>)[k]),
      )
      .join(',') +
    '}'
  )
}

/**
 * Hash a tool input for the audit trail. Key-order stable so
 * `{a,b}` and `{b,a}` produce the same digest.
 */
export function digestInput(input: unknown): string {
  return createHash('sha256').update(canonicalStringify(input)).digest('hex')
}

export type AuditInputs = {
  sessionId: string
  toolName: string
  input: unknown
  securityMode: boolean
  model?: string
  now?: number
}

export function createAuditEntry(inputs: AuditInputs): AuditEntry {
  return {
    version: 1,
    id: randomUUID().slice(0, 8),
    timestamp: inputs.now ?? Date.now(),
    session_id: inputs.sessionId,
    tool_name: inputs.toolName,
    input_digest: digestInput(inputs.input),
    security_mode: inputs.securityMode,
    model: inputs.model,
  }
}

function getAuditPath(): string {
  return join(getCwd(), AUDIT_FILE)
}

/**
 * Best-effort append. Audit persistence must never interfere with the
 * underlying tool call.
 */
export async function appendAuditEntry(entry: AuditEntry): Promise<void> {
  const path = getAuditPath()
  try {
    await mkdir(dirname(path), { recursive: true })
    await appendFile(path, JSON.stringify(entry) + '\n', 'utf-8')
  } catch (e) {
    logForDebugging(`Failed to append audit entry: ${String(e)}`)
  }
}

export function parseAuditJsonl(raw: string): AuditEntry[] {
  const out: AuditEntry[] = []
  for (const line of raw.split('\n')) {
    const trimmed = line.trim()
    if (!trimmed) continue
    try {
      const parsed = JSON.parse(trimmed) as AuditEntry
      if (parsed && parsed.version === 1) out.push(parsed)
    } catch {
      // Skip malformed lines
    }
  }
  return out
}

export async function listAuditEntries(limit = 100): Promise<AuditEntry[]> {
  try {
    const raw = await readFile(getAuditPath(), 'utf-8')
    const all = parseAuditJsonl(raw)
    return all.slice(-Math.min(limit, MAX_AUDIT_TO_LIST)).reverse()
  } catch {
    return []
  }
}

export function formatAuditLine(e: AuditEntry): string {
  const when = new Date(e.timestamp).toISOString().replace('T', ' ').slice(0, 19)
  const digest8 = e.input_digest.slice(0, 8)
  const tag = e.security_mode ? ' [security-mode]' : ''
  const modelSuffix = e.model ? ` (${e.model})` : ''
  return `${when}  ${e.tool_name}  ${digest8}${modelSuffix}${tag}`
}
