'use strict';
/**
 * Conversation collapse policy — Item F of multi-agent deep-dive.
 *
 * Borrowed from OpenClaw's `lastRoutePolicy: "main" | "session"`
 * (resolve-route.ts). Wall-E's adaptation, scaled down to single-tenant:
 *
 *   'main'       — all inbound from all sources feeds one chat history
 *                  (current behavior, default)
 *   'per-source' — each channel/webhook/manual source gets its own
 *                  brain conversation, isolated from others
 *
 * Set via:
 *   - Setup page (UI exposes a dropdown)
 *   - brain.setKv('wall_e_conversation_collapse', 'per-source')
 *   - WALLE_CONVERSATION_COLLAPSE env override
 *
 * Default 'main' preserves current behavior. Existing chat.js callers
 * see no change unless they opt in.
 */

const COLLAPSE_KV_KEY = 'wall_e_conversation_collapse';
const VALID_POLICIES = new Set(['main', 'per-source']);

function _readPolicy(brain) {
  // env wins over kv (lets users debug in shell without DB writes)
  const envValue = process.env.WALLE_CONVERSATION_COLLAPSE;
  if (envValue && VALID_POLICIES.has(envValue)) return envValue;
  if (!brain) return 'main';
  try {
    const kv = brain.getKv(COLLAPSE_KV_KEY);
    if (kv && VALID_POLICIES.has(kv)) return kv;
  } catch {}
  return 'main';
}

/**
 * Resolve the brain `session_id` for a given dispatch source.
 *
 * @param {Object} opts
 * @param {string} [opts.source]        — dispatch_source enum value (chat, slack_dm, etc.)
 * @param {string} [opts.sessionIdHint] — caller-provided session id (wins over policy)
 * @param {string} [opts.fallback]      — fallback if no source + no hint
 * @param {Object} [opts.brain]         — brain module (defaults to require('../brain'))
 * @returns {string}
 */
function resolveSessionId(opts = {}) {
  const brain = opts.brain || require('../brain');
  // Caller hint always wins — preserves explicit sessionId routing
  // (e.g. CTM session URLs that map to specific sessions).
  if (opts.sessionIdHint) return opts.sessionIdHint;

  const policy = _readPolicy(brain);
  if (policy === 'per-source' && opts.source) {
    // Stable per-source key. Slack/iMessage/Telegram all collapse to
    // ONE session per source (not per-thread) by default — that's the
    // simple version. Per-thread is an extension if needed.
    return `source:${_sanitizeSource(opts.source)}`;
  }
  return opts.fallback || 'default';
}

function _sanitizeSource(source) {
  // Defensive: source values come from the source registry (Item E) but
  // may also come from external HTTP callers. Strip path-traversal-shaped
  // and quote characters.
  return String(source)
    .replace(/[/\\]/g, '_')
    .replace(/[<>"';`]/g, '')
    .slice(0, 64);
}

function getPolicy(brain) {
  return _readPolicy(brain || require('../brain'));
}

function setPolicy(value, brain) {
  if (!VALID_POLICIES.has(value)) {
    throw new Error(`Invalid policy: ${value}. Must be one of: ${[...VALID_POLICIES].join(', ')}`);
  }
  const b = brain || require('../brain');
  b.setKv(COLLAPSE_KV_KEY, value);
}

module.exports = {
  resolveSessionId,
  getPolicy,
  setPolicy,
  COLLAPSE_KV_KEY,
  VALID_POLICIES,
};
