'use strict';

/**
 * Plugin Activation Planner — given a trigger, returns the set of plugins
 * that should activate for it, with reason codes for inspectability.
 *
 * Inspired by OpenClaw's `src/plugins/activation-planner.ts:resolveManifestActivationPlan`,
 * but scoped to Wall-E's smaller plugin surface. The reason enum matches
 * OpenClaw's vocabulary so a future "why did X run" UI can use the same
 * verbiage.
 *
 * Triggers Wall-E understands:
 *   { kind: 'cron'        }                          — cron-due skills
 *   { kind: 'chat'        }                          — chat-routed skills
 *   { kind: 'manual', skillId? }                     — explicit invocation
 *   { kind: 'command', command }                     — slash-command (future)
 *   { kind: 'provider', provider }                   — provider auth flow
 *   { kind: 'channel', channel }                     — incoming channel msg
 *   { kind: 'capability', capability, memoryType? }  — capability lookup
 *
 * Reason codes (frozen):
 *   - manifest-channel-owner       (channel registry: configKey owns the channel)
 *   - manifest-provider-owner      (provider registry: id owns the provider type)
 *   - manifest-trigger-cron        (skill manifest: trigger.type='cron')
 *   - manifest-trigger-chat        (skill manifest: trigger.type='chat')
 *   - manifest-trigger-manual      (skill manifest: trigger.type='manual')
 *   - manifest-capability-match    (capabilities[capability]=true)
 *   - manifest-memory-type-match   (capabilities.memoryTypes contains memoryType)
 *   - hint-id-match                (skillId/command/provider literal match)
 */

const VALID_TRIGGER_KINDS = new Set([
  'cron',
  'chat',
  'manual',
  'command',
  'provider',
  'channel',
  'capability',
]);

/** Pure function — given a trigger and a list of pluggable entries, decide. */
function planActivations(trigger, candidates) {
  if (!trigger || typeof trigger !== 'object' || !VALID_TRIGGER_KINDS.has(trigger.kind)) {
    return {
      trigger,
      entries: [],
      diagnostics: [
        {
          level: 'error',
          message: `unknown trigger kind: ${trigger?.kind}`,
          component: 'activation-planner',
        },
      ],
    };
  }
  if (!Array.isArray(candidates)) {
    return { trigger, entries: [], diagnostics: [] };
  }

  const entries = [];
  for (const cand of candidates) {
    if (!cand) continue;
    const reasons = listManifestReasons(cand, trigger);
    if (reasons.length === 0) continue;
    entries.push({
      pluginId: cand.id || cand.manifest?.id || null,
      origin: cand.source || cand.manifest?.source || 'bundled',
      reasons,
      kind: cand.manifest?.kind || cand.kind || null,
    });
  }

  // Dedup-while-preserving-order; ids unique per plan
  const seen = new Set();
  const deduped = [];
  for (const e of entries) {
    if (e.pluginId == null) continue;
    if (seen.has(e.pluginId)) continue;
    seen.add(e.pluginId);
    deduped.push(e);
  }

  return {
    trigger,
    entries: deduped,
    pluginIds: deduped.map((e) => e.pluginId),
    diagnostics: [],
  };
}

/**
 * Per-candidate reason list. A candidate may match a trigger for multiple
 * reasons; we return all of them so callers can render "matched because: X, Y".
 */
function listManifestReasons(cand, trigger) {
  const manifest = cand.manifest || cand;
  if (!manifest) return [];
  const reasons = [];
  const k = manifest.kind;

  switch (trigger.kind) {
    case 'cron':
      if (k === 'skill' && trigger?.now != null && cand?.trigger?.type === 'cron') {
        reasons.push('manifest-trigger-cron');
      } else if (k === 'skill' && cand?.trigger?.type === 'cron') {
        reasons.push('manifest-trigger-cron');
      }
      break;
    case 'chat':
      if (k === 'skill' && cand?.trigger?.type === 'chat') reasons.push('manifest-trigger-chat');
      break;
    case 'manual':
      if (k === 'skill' && cand?.trigger?.type === 'manual') reasons.push('manifest-trigger-manual');
      if (trigger.skillId && (manifest.id === trigger.skillId || cand.name === trigger.skillId)) {
        reasons.push('hint-id-match');
      }
      break;
    case 'command':
      if (manifest.id === trigger.command || cand.name === trigger.command) {
        reasons.push('hint-id-match');
      }
      break;
    case 'provider':
      if (k === 'provider' && manifest.id === trigger.provider) {
        reasons.push('manifest-provider-owner');
      }
      break;
    case 'channel':
      if (k === 'channel') {
        if (manifest.id === trigger.channel || manifest.configKey === trigger.channel) {
          reasons.push('manifest-channel-owner');
        }
      }
      break;
    case 'capability':
      if (manifest.capabilities && trigger.capability) {
        if (manifest.capabilities[trigger.capability] === true) {
          reasons.push('manifest-capability-match');
        }
      }
      if (
        manifest.capabilities?.memoryTypes &&
        trigger.memoryType &&
        manifest.capabilities.memoryTypes.includes(trigger.memoryType)
      ) {
        reasons.push('manifest-memory-type-match');
      }
      break;
    default:
      break;
  }

  return reasons;
}

module.exports = {
  planActivations,
  listManifestReasons,
  VALID_TRIGGER_KINDS,
};
