import type { ComponentRunner, EdgeType } from '../types';

export const VALID_EDGE_TYPES = new Set([
  // 現有
  'PIPE', 'IF', 'FOREACH', 'CONTINUE',
  // 新增：執行語意
  'IS_A', 'ON_SUCCESS', 'ON_FAIL',
  // 新增：條件語意（SDD workflow-discovery 3.11）—— 讀上游 if_control/switch 的 branch
  'ON_TRUE', 'ON_FALSE', 'ON_BRANCH',
  // 新增：觸發語意
  'ON_CLICK', 'CALLS_SUBFLOW',
  // 新增：結構語意（記錄圖結構，不執行）
  'CONTAINS', 'HAS_STYLE', 'HAS_BEHAVIOR',
]);

/** 內建零件 ID 集合（Worker 記憶體中已有實作）*/
export const BUILTIN_IDS = new Set([
  'webhook', 'comp_passthrough', 'comp_uppercase', 'comp_counter',
]);

/** 語意邊 → EdgeType 映射（ADR-057 u6u L1：支援中文語意關係詞）
 *  完成後 → PIPE（成功後觸發下一個）
 *  失敗時 → CONTINUE（失敗後繼續）
 *  對每個 → FOREACH（迭代執行）
 *  條件滿足時 → IF（條件分支）
 */
export const SEMANTIC_EDGE_MAP: Record<string, EdgeType> = {
  // 中文語意詞
  '完成後': 'PIPE',
  '失敗時': 'ON_FAIL',
  '對每個': 'FOREACH',
  '條件滿足時': 'IF',
  // 條件分支語意（SDD workflow-discovery 3.11）：讓意圖工作流寫得出兩條路
  '成立時': 'ON_TRUE',
  '為真時': 'ON_TRUE',
  '不成立時': 'ON_FALSE',
  '為假時': 'ON_FALSE',
  '否則': 'ON_FALSE',
  // 英文別名
  'SUCCESS': 'ON_SUCCESS',
  'FAIL': 'ON_FAIL',
  'TRUE': 'ON_TRUE',
  'FALSE': 'ON_FALSE',
  'ELSE': 'ON_FALSE',
  'BRANCH': 'ON_BRANCH',
  'CLICK': 'ON_CLICK',
  'SUBFLOW': 'CALLS_SUBFLOW',
};

/**
 * wait 零件的等待上限（毫秒）。與 registry/components/wait/component.contract.yaml
 * 逐字相同 —— 超過此值截斷、不報錯。**不可為了閃避資源上限調小**（Arcrun#101 紅線）：
 * 「等外部系統跟上」是這顆零件存在的理由，把上限砍掉等於把能力換掉。
 */
export const WAIT_MAX_MS = 30000;

/**
 * 內建零件表（靜態函數）
 * WASM 零件 = 各自獨立 Worker，cypher-executor 走 HTTP URL 呼叫（不從 R2 讀）
 */
export const BUILTIN_COMPONENTS = new Map<string, ComponentRunner>([
  ['comp_passthrough', (ctx) => ctx],
  ['comp_uppercase', (ctx) => {
    const c = ctx as Record<string, unknown>;
    return { ...c, text: String(c.text || '').toUpperCase() };
  }],
  ['comp_counter', (ctx) => {
    const c = ctx as Record<string, unknown>;
    return { ...c, count: (Number(c.count) || 0) + 1 };
  }],

  // ── wait：等待 N 毫秒後繼續（Arcrun#101，2026-08-12）────────────────────────
  //
  // 為什麼「等待」搬進引擎，而不是修那顆 WASM：
  //
  // 舊實作是 registry/components/wait/main.go（TinyGo → WASM），用 time.Sleep。
  // TinyGo 的 sleep 走 WASI `poll_oneoff`；而每顆 component worker 的 WASI shim 把
  // poll_oneoff 實作成 ENOSYS（`.component-builds/*/src/index.ts`：`poll_oneoff: () => 76`）
  // ⇒ TinyGo 排程器拿不到「睡到某個時間」的手段，退化成迴圈重讀 `clock_time_get`
  // 自旋等時間到（wasm 內可見 runtime.sleepTicks / sleepQueue / runtime.ticks 符號）。
  //
  // 🔴 到這裡為止是**查得到原始碼的事實**。再往下「所以那個自旋迴圈的結束條件永遠
  //    不成立」曾被當成結論寫在這裡，但**寫了測試去證，反而被打臉**：在
  //    vitest-pool-workers 的 workerd 裡，同步自旋 2553 圈之後 Date.now() 就前進了
  //    ⇒ 時鐘並沒有全程凍結。
  //    ⇒ 「為什麼三秒的等待會拖到 35 秒才死」的完整機制**目前仍是推測**，
  //      證據只有下面 leo 的四次實測。別把它當定論往外傳。
  //
  // 所以症狀不是「等 N 秒花 N 秒 CPU」，而是「不管 ms 填多少都跑到 CPU 上限被砍」。
  // leo 2026-08-12 在 youlin stage 實測（只有 input >> wait 兩個節點）：
  //   ms=3000 → 38.9s 後 503 / ms=20000 → 34.0s / ms=30000 → 34.9s / 寫死 3000 → 34.8s
  // 四個值同一個死法、與 ms 無關 —— 3 秒的等待撐到 35 秒才死，就是「迴圈根本沒結束」
  // 的證據（若成本與時長成正比，ms=3000 只會花 3 秒 CPU，根本不該死）。
  // 也就是說 wait 零件在 Workers 上從來沒有真的等待成功過，不只是貴。
  //
  // 純 WASI 沙箱（stdin→stdout、無 socket、同步呼叫）本來就沒有「不花 CPU 地等」這種
  // 東西 —— 會等的只有宿主。故 wait 與 trigger_workflow 同類：**是 orchestrator 的
  // 執行排程職責，不是業務邏輯**（rule 02 §2.3 明列「workflow 執行排程」屬 cypher-executor
  // 合法職責；§2.2 禁的是解密／簽章／template 展開／具體 API 呼叫，等待都不是）。
  // 搬進引擎不違反「業務邏輯走 WASM」鐵律。引擎這側 await 一個 timer 只花 wall-clock、
  // 不記 CPU ⇒ 等 30 秒與等 3 秒同價（皆 ≈0）。
  //
  // I/O 契約沿用 component.contract.yaml，既有 workflow 的 wait 節點定義不必改：
  //   吃 ms（必填 > 0）＋可選 context；ms > WAIT_MAX_MS 截斷；
  //   回 { success: true, data: { ...context, waited_ms } }；ms <= 0 回 success:false。
  // 唯一刻意的放寬：ms 允許數字字串（"3000"）。WASM 版 json.Unmarshal 進 int 會直接
  // 失敗，但 node.data 走 interpolateData 後 `ms: "{{input.delay}}"` 必然是字串
  // ⇒ 收字串只會把「本來就跑不動的」變成跑得動，不會改變任何既有成功案例的行為。
  ['wait', async (ctx) => {
    const c = (ctx && typeof ctx === 'object') ? ctx as Record<string, unknown> : {};

    const requested = typeof c.ms === 'number' ? c.ms : Number(c.ms);
    if (!Number.isFinite(requested) || requested <= 0) {
      return { success: false, error: 'ms 必須大於 0' };
    }
    const ms = Math.min(Math.floor(requested), WAIT_MAX_MS);

    // 這一行就是整張票：await timer ⇒ 只走 wall-clock，不佔請求執行緒、不記 CPU。
    await new Promise<void>((resolve) => setTimeout(resolve, ms));

    const passthrough = (c.context && typeof c.context === 'object' && !Array.isArray(c.context))
      ? c.context as Record<string, unknown>
      : {};
    return { success: true, data: { ...passthrough, waited_ms: ms } };
  }],
]);

export const SCORE_THRESHOLD = 0.5;
