'use strict';
/**
 * Pluggable trigger types for skills.
 *
 * Today, Wall-E only auto-dispatches `trigger_type === 'interval'` skills
 * via runDueSkills. Other trigger types ('manual', 'webhook', 'event')
 * are gated by decideSkillDispatch's `no_trigger` reason, which means
 * they are visible in the UI/audit trail but never auto-fire.
 *
 * This registry lets plugins declare how a non-interval trigger should
 * decide whether to fire on each tick, and (optionally) how to dispatch
 * the skill when an external event arrives (HTTP webhook, pub/sub).
 *
 * Borrowed shape from OpenClaw's harness-supports pattern: each trigger
 * handler self-declares whether the skill is fireable right now.
 *
 * Trigger handler shape:
 *   {
 *     type: 'manual' | 'webhook' | 'event' | string,
 *     label: string,
 *     // Returns true if the skill should fire this tick.
 *     // For manual/webhook/event, this almost always returns false —
 *     // these are externally-driven, not cron-driven.
 *     shouldFire(skill, ctx) → boolean,
 *     // Optional: how to actually invoke the skill from external code.
 *     // Manual: invoked by /api/wall-e/skills/:id/run (existing route).
 *     // Webhook: invoked by /api/wall-e/skills/:name/trigger.
 *     dispatch?(skill, payload) → Promise<Result>,
 *   }
 *
 * Item H of skill-dispatch deep-dive. Stubs only — actual webhook
 * infrastructure (auth, signature verification, replay protection) is
 * out of scope for now.
 */

const _triggers = new Map();

function registerTrigger(handler) {
  if (!handler || typeof handler !== 'object') {
    throw new Error('registerTrigger requires a handler object');
  }
  if (!handler.type || typeof handler.type !== 'string') {
    throw new Error('handler.type must be a non-empty string');
  }
  if (typeof handler.shouldFire !== 'function') {
    throw new Error(`trigger "${handler.type}" requires shouldFire()`);
  }
  const previous = _triggers.get(handler.type);
  _triggers.set(handler.type, handler);
  return () => {
    if (previous) _triggers.set(handler.type, previous);
    else _triggers.delete(handler.type);
  };
}

function getTrigger(type) {
  return _triggers.get(type) || null;
}

function listTriggerTypes() {
  return Array.from(_triggers.keys()).sort();
}

function clearTriggerRegistry() {
  _triggers.clear();
}

// ── Bundled triggers ──

// 'interval' — the default. Cron-driven. shouldFire is the existing
// decideSkillDispatch flow, so this handler's shouldFire is effectively
// a no-op (decideSkillDispatch handles 'interval' inline).
registerTrigger({
  type: 'interval',
  label: 'Interval (cron-style)',
  shouldFire: () => false, // decideSkillDispatch handles this
});

// 'manual' — never auto-fires. User pokes via /api/wall-e/skills/:id/run.
registerTrigger({
  type: 'manual',
  label: 'Manual trigger only',
  shouldFire: () => false,
  // dispatch is the existing /api/wall-e/skills/:id/run path; nothing
  // to do here.
});

// 'webhook' — never auto-fires. Authenticated callers invoke the skill via
// /api/wall-e/skills/:name/trigger. Unauthenticated external webhook ingress
// uses /api/wall-e/webhook and is emitted onto the event bus.
registerTrigger({
  type: 'webhook',
  label: 'Authenticated webhook skill trigger',
  shouldFire: () => false,
  dispatch: async (skill, payload) => {
    // Stub: in production, signature verification + idempotency check
    // would go here before delegating to the skill runner.
    const { runSkillWithFallback } = require('./skill-fallback');
    return await runSkillWithFallback(skill, {
      decision_reason: 'webhook',
      dispatch_source: 'webhook', // Item E
      _payload: payload,
    });
  },
});

// 'event' — pub/sub stub. shouldFire returns false; eventual integration
// would consume from a brain table or external bus.
registerTrigger({
  type: 'event',
  label: 'Internal event subscription',
  shouldFire: () => false,
});

module.exports = {
  registerTrigger,
  getTrigger,
  listTriggerTypes,
  clearTriggerRegistry,
};
