// src/core/security/LispSecurityContext.ts

/**
 * Security context for Lisp/VLM evaluation.
 *
 * Controls what a Lisp expression can do:
 * - Which primitives are allowed
 * - Resource limits (depth, time, memory)
 * - Communication capabilities (call, emit, spawn)
 *
 * See: Vision/18-VIRTUAL-LISP-MACHINE.md
 * See: Vision/27-SECURITY-FEDERATION-DRAFT.md Section 5
 */
export interface ILispSecurityContext {
  /** Context identifier */
  id: string;

  /** Security principal (who is running this code) */
  principal: string;

  /** When this context was created */
  createdAt: number;

  // ═══════════════════════════════════════════════════════════════════════
  // PRIMITIVE CONTROL
  // ═══════════════════════════════════════════════════════════════════════

  /**
   * Primitive access mode:
   * - 'allowlist': Only primitives in primitiveList are allowed
   * - 'blocklist': All primitives except those in primitiveList are allowed
   * - 'unrestricted': All primitives allowed (developer mode)
   */
  primitiveMode: 'allowlist' | 'blocklist' | 'unrestricted';

  /** List of primitive names (interpretation depends on primitiveMode) */
  primitiveList?: string[];

  // ═══════════════════════════════════════════════════════════════════════
  // RESOURCE LIMITS
  // ═══════════════════════════════════════════════════════════════════════

  /** Maximum evaluation depth (prevents infinite recursion) */
  maxEvalDepth: number;

  /** Maximum execution time in milliseconds */
  maxExecutionMs: number;

  /** Maximum memory in bytes (optional, Phase 2+) */
  maxMemoryBytes?: number;

  // ═══════════════════════════════════════════════════════════════════════
  // CAPABILITY CONSTRAINTS
  // ═══════════════════════════════════════════════════════════════════════

  /**
   * Mount patterns allowed for 'call' primitive.
   * - ['*'] = any target allowed
   * - [] = no calls allowed
   * - ['app/*'] = only app subtree
   */
  callTargets: string[];

  /** Whether 'emit' primitive is allowed */
  emitAllowed: boolean;

  /** Whether 'spawn' primitive is allowed */
  spawnAllowed: boolean;
}

// ═══════════════════════════════════════════════════════════════════════════
// PREDEFINED CONTEXTS
// ═══════════════════════════════════════════════════════════════════════════

/**
 * Sandbox context - safe for user-provided code.
 *
 * Allows only pure computation, no side effects.
 */
export const SANDBOX_CONTEXT: ILispSecurityContext = {
  id: 'sandbox',
  principal: 'system',
  createdAt: Date.now(),

  // Only safe primitives
  primitiveMode: 'allowlist',
  primitiveList: [
    // Core Lisp
    'quote', 'car', 'cdr', 'cons', 'list', 'atom?', 'eq?', 'null?',
    // Conditionals
    'cond', 'if', 'and', 'or', 'not',
    // Arithmetic
    '+', '-', '*', '/', '<', '>', '=', '<=', '>=',
    // Functions
    'lambda', 'let', 'let*', 'letrec',
    // Higher-order
    'map', 'filter', 'reduce', 'fold',
    // Type checks
    'number?', 'string?', 'list?', 'function?',
  ],

  // Strict resource limits
  maxEvalDepth: 100,
  maxExecutionMs: 5000, // 5 seconds

  // No communication
  callTargets: [],
  emitAllowed: false,
  spawnAllowed: false,
};

/**
 * Component context - for trusted component code.
 *
 * Allows communication within the component's realm.
 */
export const COMPONENT_CONTEXT: ILispSecurityContext = {
  id: 'component',
  principal: 'component',
  createdAt: Date.now(),

  // All primitives except dangerous ones
  primitiveMode: 'blocklist',
  primitiveList: [
    'eval',      // No meta-evaluation
    'js-eval',   // No JavaScript escape
    'require',   // No module loading
  ],

  // Moderate resource limits
  maxEvalDepth: 1000,
  maxExecutionMs: 30000, // 30 seconds

  // Limited communication
  callTargets: ['self', 'parent', 'children/*'],
  emitAllowed: true,
  spawnAllowed: false,
};

/**
 * Developer context - for development and testing.
 *
 * All capabilities enabled, long timeouts.
 *
 * Mark Miller (E Language, Capability Security):
 *   "Security code must always run. blocklist:[] achieves same permissive
 *    behavior as 'unrestricted' but the checking code IS executed."
 *
 * Joe Armstrong (Erlang, "Programming Erlang"):
 *   "Same mechanism, different policy. Dev mode is permissive policy,
 *    not bypassed mechanism."
 */
export const DEVELOPER_CONTEXT: ILispSecurityContext = {
  id: 'developer',
  principal: 'system',
  createdAt: Date.now(),

  // CHANGED: blocklist:[] instead of 'unrestricted'
  // Empty blocklist = allow all, BUT security code is exercised
  primitiveMode: 'blocklist',
  primitiveList: [], // Empty = allow all primitives (through checking code!)

  // Generous resource limits
  maxEvalDepth: 10000,
  maxExecutionMs: 300000, // 5 minutes

  // Full communication
  callTargets: ['*'],
  emitAllowed: true,
  spawnAllowed: true,
};

/**
 * Create a custom security context by merging with sandbox defaults.
 */
export function createLispSecurityContext(
  overrides: Partial<ILispSecurityContext>
): ILispSecurityContext {
  return {
    ...SANDBOX_CONTEXT,
    ...overrides,
    id: overrides.id ?? `custom-${Date.now()}`,
    createdAt: Date.now(),
  };
}

/**
 * Check if a primitive is allowed in the given context.
 *
 * Mark Miller (E Language):
 *   "Every primitive check goes through this function. No bypass."
 */
export function isPrimitiveAllowed(
  primitiveName: string,
  context: ILispSecurityContext
): boolean {
  switch (context.primitiveMode) {
    case 'unrestricted':
      // DEPRECATED: 'unrestricted' bypasses security checks
      // Use blocklist:[] instead for same behavior with checks running
      // eslint-disable-next-line no-console -- deprecated security mode warning in kernel evaluation layer
      console.warn(
        `[Security] DEPRECATED: primitiveMode 'unrestricted' bypasses security checks. ` +
        `Use primitiveMode: 'blocklist' with primitiveList: [] instead. ` +
        `Context: ${context.id}`
      );
      return true;

    case 'allowlist':
      return context.primitiveList?.includes(primitiveName) ?? false;

    case 'blocklist':
      // Empty blocklist = allow all (same as old 'unrestricted', but code runs!)
      return !context.primitiveList?.includes(primitiveName);

    default:
      // Type guard - unknown mode, fail safe
      // eslint-disable-next-line no-console -- invalid primitive mode warning in kernel security layer
      console.warn(`[Security] Unknown primitiveMode: ${context.primitiveMode}`);
      return false;
  }
}

/**
 * Check if a call target is allowed in the given context.
 */
export function isCallTargetAllowed(
  target: string,
  context: ILispSecurityContext
): boolean {
  if (context.callTargets.includes('*')) {
    return true;
  }

  for (const pattern of context.callTargets) {
    if (pattern === target) {
      return true;
    }

    // Simple wildcard matching
    if (pattern.endsWith('/*')) {
      const prefix = pattern.slice(0, -1);
      if (target.startsWith(prefix)) {
        return true;
      }
    }
  }

  return false;
}
