// lib/process/stages/policy-gate.js — handler for policy_gate stages.
//
// Evaluates declarative rules against the case's intake + accumulated context.
// Each entry in `config.rules` is one of:
//   - a JSON predicate     → `../policy-predicates.js` (the canonical form)
//   - a string expression  → legacy DSL (`./policy-dsl-legacy.js`, deprecated,
//     kept one release for back-compat; routed per-rule)
//   - { expr, label }      → legacy DSL wrapper
//
// All rules must pass (implicit AND across the array) for the gate to pass.
//
// Outcomes:
//   - rules all pass         → kind=pass
//   - any rule fails         → kind=fail   (routed via on_fail_stage_slug)
//   - explicit manual_review → returns approval_required (rare; usually
//     downstream human_approval handles this)

import { createApprovalRequest } from '../../approvals.js';
import { evaluatePredicateSafe, isPredicateRule, describePredicate } from '../policy-predicates.js';
import { evaluateRule } from './policy-dsl-legacy.js';

// Re-exported for back-compat: tests and any callers still importing the
// legacy string-DSL evaluator from this module keep working during the
// deprecation window.
export { evaluateRule };

// Evaluate one rule entry — predicate JSON or legacy string — returning a
// uniform { result, error, label } record for the trace.
function evaluateRuleEntry(rule, ctx) {
  if (isPredicateRule(rule)) {
    const { result, error } = evaluatePredicateSafe(rule, ctx);
    return { result, error, label: rule.label || describePredicate(rule) };
  }
  const expr = typeof rule === 'string' ? rule : rule?.expr;
  const label = typeof rule === 'string' ? rule : (rule?.label || rule?.expr);
  const { result, error } = evaluateRule(expr, ctx);
  return { result, error, label };
}

export async function executeStage({ case: c, stage }) {
  const config = stage.config || {};
  const rules = Array.isArray(config.rules) ? config.rules : [];
  const intake = c.intake_payload || {};
  const context = c.context || {};
  const trace = [];
  let allPass = true;
  for (const rule of rules) {
    const { result, error, label } = evaluateRuleEntry(rule, { intake, context });
    trace.push({ rule: label, result, error });
    if (!result) allPass = false;
  }

  // Optional manual_review escape hatch: when set, fails the gate and creates
  // an approval. The compliance approval pattern usually owns this on its own
  // human_approval stage; this is for compound gates.
  if (!allPass && config.on_fail_create_approval) {
    const approval = createApprovalRequest({
      action_type: 'process.stage.approve',
      risk: config.risk || 'medium',
      summary: config.on_fail_create_approval.summary || `Policy gate ${stage.slug} requires manual review`,
      payload: { case_id: c.id, stage_slug: stage.slug, trace },
      project_id: c.project_id,
    });
    return {
      kind: 'pass',
      output: { trace, all_pass: false, manual_review_approval_id: approval.id },
      approval_required: { approval_request_id: approval.id, summary: approval.summary },
    };
  }

  return {
    kind: allPass ? 'pass' : 'fail',
    output: { trace, all_pass: allPass },
    error: allPass ? null : 'policy_gate_failed',
  };
}

export const STAGE_KIND = 'policy_gate';
