/**
 * PolicyPresets — named security postures for daemon.json configuration.
 *
 * ADR-025: Operators choose a preset or write custom rules. No Lisp required.
 *
 * Presets:
 *   dev      — allow everything (equivalent to NullPolicyEngine)
 *   standard — allow by default, deny risky ops
 *   strict   — deny by default, allow only explicitly listed ops
 *   lockdown — deny by default, allow only system bootstrap ops
 */
import { NullPolicyEngine } from './NullPolicyEngine.js';
import { DeclarativePolicyEngine, type IDeclarativePolicy, type IDeclarativeRule } from './DeclarativePolicyEngine.js';
import type { IPolicyEngine } from './IPolicyEngine.js';
import { OmegaLispPolicyEngine } from './OmegaLispPolicyEngine.js';

// ─── System ops that must always be allowed for actor lifecycle ──────────

const SYSTEM_OPS = [
  '$ping', '$introspect', '$join', '$join_ack', '$leave',
  '$membrane', '$membrane-inherit',
  '$getState', '$getHistory', '$createControl',
  '$activity', '$preferences', '$config',
];

// ─── Presets ─────────────────────────────────────────────────────────────

export const POLICY_PRESETS: Record<string, IDeclarativePolicy> = {
  /** Allow everything — no restrictions. For development only. */
  dev: {
    rules: [],
    default: 'allow',
  },

  /** Allow by default, deny known-risky operations. Production baseline. */
  standard: {
    rules: [
      {
        match: { op: 'factory.create-from-code' },
        action: 'deny',
        reason: 'Dynamic component compilation denied by standard policy',
      },
    ],
    default: 'allow',
  },

  /** Deny by default, allow system ops + common domain ops. */
  strict: {
    rules: [
      // System ops always allowed
      { match: { op: SYSTEM_OPS }, action: 'allow' },
      // Agent/chat ops
      { match: { op: ['$prompt', '$response', '$error', '$cancelled'] }, action: 'allow' },
      // Component lifecycle
      { match: { op: ['component.list', 'component.get', '$source-code'] }, action: 'allow' },
      // Introspection
      { match: { op: ['introspect', 'snapshot.tree.oracle'] }, action: 'allow' },
    ],
    default: 'deny',
    defaultReason: 'Denied by strict policy — op not in allowlist',
  },

  /** Minimal bootstrap only — system ops and nothing else. */
  lockdown: {
    rules: [
      { match: { op: SYSTEM_OPS }, action: 'allow' },
    ],
    default: 'deny',
    defaultReason: 'Denied by lockdown policy — only system bootstrap ops allowed',
  },
};

// ─── Security Config (maps to daemon.json "security" section) ───────────

export interface ISecurityConfig {
  /** Engine type. Default: 'null' (no enforcement). */
  policyEngine?: 'null' | 'declarative' | 'omega-lisp';
  /** Named preset (only for declarative engine). */
  preset?: keyof typeof POLICY_PRESETS;
  /** Custom rules (only for declarative engine, merged after preset rules). */
  rules?: IDeclarativeRule[];
}

// ─── Factory ─────────────────────────────────────────────────────────────

/**
 * Create a policy engine from daemon.json security config.
 *
 * No config or policyEngine='null' → NullPolicyEngine (zero enforcement).
 * policyEngine='declarative' + preset → DeclarativePolicyEngine with preset rules.
 * policyEngine='declarative' + rules → DeclarativePolicyEngine with custom rules.
 * policyEngine='omega-lisp' → OmegaLispPolicyEngine (lazy-loaded to avoid import cost).
 */
export function createPolicyEngine(config: ISecurityConfig = {}): IPolicyEngine {
  const engineType = config.policyEngine ?? 'null';

  if (engineType === 'null') {
    return new NullPolicyEngine();
  }

  if (engineType === 'declarative') {
    const presetName = config.preset ?? 'dev';
    const preset = POLICY_PRESETS[presetName];
    if (!preset) {
      throw new Error(`Unknown policy preset: '${presetName}'. Available: ${Object.keys(POLICY_PRESETS).join(', ')}`);
    }

    // Merge custom rules after preset rules (custom rules take priority via first-match)
    const customRules = config.rules ?? [];
    const mergedPolicy: IDeclarativePolicy = {
      rules: [...customRules, ...preset.rules],
      default: preset.default,
      defaultReason: preset.defaultReason,
    };

    return new DeclarativePolicyEngine(mergedPolicy);
  }

  if (engineType === 'omega-lisp') {
    return new OmegaLispPolicyEngine();
  }

  throw new Error(`Unknown policy engine type: '${engineType}'. Available: null, declarative, omega-lisp`);
}
