import { createHash } from 'node:crypto';
import type { NewAgentLog } from './agent-logs.js';

export type RuntimeAuditOperation =
  | 'session.list'
  | 'session.tree'
  | 'session.stream'
  | 'session.send'
  | 'session.attach'
  | 'session.terminate'
  | 'runtime.capabilities'
  | 'runtime.health'
  | 'runtime.transitional-capabilities';

export type RuntimeAuditOutcome = 'requested' | 'succeeded' | 'denied' | 'failed';
export type RuntimeAuditErrorCode = 'policy_denied' | 'provider_error';

/**
 * Deliberately metadata-only runtime audit record. It has no fields for message
 * content, credentials, approval references, tool arguments, or tool output.
 */
export interface RuntimeAuditEvent {
  providerId: string;
  operation: RuntimeAuditOperation;
  outcome: RuntimeAuditOutcome;
  actorId: string;
  tenantId: string;
  channelId: string;
  correlationId: string;
  resourceId?: string;
  durationMs?: number;
  errorCode?: RuntimeAuditErrorCode;
}

const SAFE_IDENTIFIER = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;

function safeIdentifier(value: string): string {
  if (SAFE_IDENTIFIER.test(value)) return value;
  return hashIdentifier(value);
}

function hashIdentifier(value: string): string {
  return `sha256:${createHash('sha256').update(value).digest('hex')}`;
}

/**
 * Converts a typed audit event into a durable log entry using an explicit
 * allowlist. Values that could carry credentials or untrusted content are
 * rejected before persistence or structured log emission.
 */
export function createRuntimeAuditLogEntry(event: RuntimeAuditEvent): NewAgentLog {
  const providerId = safeIdentifier(event.providerId);
  const actorId = safeIdentifier(event.actorId);
  const tenantId = safeIdentifier(event.tenantId);
  const channelId = safeIdentifier(event.channelId);
  const correlationId = safeIdentifier(event.correlationId);
  // Provider resource identifiers may be opaque or user-derived, so never persist them raw.
  const resourceId = event.resourceId ? hashIdentifier(event.resourceId) : undefined;
  const persistedUserId = SAFE_IDENTIFIER.test(event.actorId) ? event.actorId : null;

  if (
    event.durationMs !== undefined &&
    (!Number.isInteger(event.durationMs) || event.durationMs < 0)
  ) {
    throw new Error('Runtime audit duration must be a non-negative integer');
  }

  return {
    sessionId: `runtime:${providerId}`,
    userId: persistedUserId,
    level: event.outcome === 'failed' ? 'error' : event.outcome === 'denied' ? 'warn' : 'info',
    category: event.operation.startsWith('session.') ? 'tool_use' : 'general',
    content: 'runtime.provider.audit',
    metadata: {
      providerId,
      operation: event.operation,
      outcome: event.outcome,
      actorId,
      tenantId,
      channelId,
      correlationId,
      ...(resourceId ? { resourceId } : {}),
      ...(event.durationMs !== undefined ? { durationMs: event.durationMs } : {}),
      ...(event.errorCode ? { errorCode: event.errorCode } : {}),
    },
  };
}
