/**
 * acr parts          — 列出所有可用「零件（component）」（內建清單，不依賴 registry.arcrun.dev）
 * acr parts scaffold <component> — 輸出 config 範本（可直接貼入 workflow.yaml）
 * acr parts publish <component>  — 提交零件至公眾 registry（Phase 5，封測後）
 *
 * ⚠️ 分類原則（2026-06-29，issue #13 / component-gatekeeping W3）：
 *   - **零件（component）= 靜態清單**：WASM，只能走 GitHub PR + 人 merge 新增（mindset §4 人類閘門），
 *     固定慢增 → 用 BUILTIN_COMPONENTS hardcode 反映真實，正確。
 *   - **recipe / auth-recipe / workflow = 動態，存在 store**：任何人 `acr recipe push` 即新增 →
 *     **絕不可 hardcode 在這裡**（會「submitted = invisible」+ 誤導查錯表）。它們各有動態清單：
 *       recipe → `acr recipe list`（GET /recipes）｜auth-recipe → `acr auth-recipe list`（GET /auth-recipes）｜workflow → `acr list`（GET /webhooks/named）
 *   - **跨類找東西用 `acr search <term>`**（fan-out 上述 4 個來源，不必先知道它是哪一類）。
 *   歷史教訓：本檔曾把 gmail_send/telegram_send/notion 等 5 個 recipe hardcode 進零件清單 →
 *   誤導「telegram 是零件 / 走不同機制」。已移除（W3.1）。
 */
import { readFileSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import chalk from 'chalk';
import { loadConfig, getCypherExecutorUrl } from '../lib/config.js';

// ── 內建零件定義 ────────────────────────────────────────────────────────────────

interface CredentialRequirement {
  key: string;
  type: string;
  inject_as: string;
}

interface ComponentDef {
  canonical_id: string;
  display_name: string;
  category: 'logic' | 'data' | 'api' | 'ai';
  description: string;
  config_example: string;
  credentials_required?: CredentialRequirement[];
}

export const BUILTIN_COMPONENTS: ComponentDef[] = [
  // ── 控制類（Logic） ────────────────────────────────────────────────────────
  {
    canonical_id: 'if_control',
    display_name: 'If Control',
    category: 'logic',
    description: '條件分支：condition 為 true 走 ON_SUCCESS，否則走 ON_FAIL',
    config_example:
`  if_node:
    component: if_control
    condition: "status === active"`,
  },
  {
    canonical_id: 'switch',
    display_name: 'Switch',
    category: 'logic',
    description: '多分支條件：根據 value 欄位選擇對應分支',
    config_example:
`  switch_node:
    component: switch
    key: status
    cases:
      active: branch_a
      inactive: branch_b`,
  },
  {
    canonical_id: 'foreach_control',
    display_name: 'Foreach',
    category: 'logic',
    description: '迭代：對陣列每個元素執行下游節點',
    config_example:
`  loop_node:
    component: foreach_control
    iterator: item`,
  },
  {
    canonical_id: 'filter',
    display_name: 'Filter',
    category: 'logic',
    description: '過濾陣列：保留符合 condition 的元素',
    config_example:
`  filter_node:
    component: filter
    key: items
    condition: "status === active"`,
  },
  {
    canonical_id: 'merge',
    display_name: 'Merge',
    category: 'logic',
    description: '合併多個上游節點的輸出（Fan-in）',
    config_example:
`  merge_node:
    component: merge`,
  },
  {
    canonical_id: 'try_catch',
    display_name: 'Try Catch',
    category: 'logic',
    description: '錯誤捕捉：下游失敗時執行 catch 分支',
    config_example:
`  safe_node:
    component: try_catch`,
  },
  {
    canonical_id: 'wait',
    display_name: 'Wait',
    category: 'logic',
    description: '延遲執行：等待指定毫秒後繼續',
    config_example:
`  delay_node:
    component: wait
    ms: 1000`,
  },
  {
    // code 是自足 Worker（quickjs 沙箱，registry/components/code），非 TinyGo-wasm 家族，
    // 但同屬靜態零件（走 PR + 人類閘門 merge 新增）→ 進本清單正確（issue #13 W3 原則）。
    // 部署：acr init/update 的 downloadAndDeploy 自動部署（deploy.ts SELF_CONTAINED_COMPONENT_WORKERS）。
    canonical_id: 'code',
    display_name: '程式碼（沙箱 inline JS）',
    category: 'logic',
    description: 'QuickJS-wasm 沙箱執行 inline JS：讀 input、return JSON-able 值；碰不到網路/檔案/env',
    config_example:
`  my_code:
    component: code
    code: |
      const doubled = input.items.map(x => x * 2);
      return { doubled, count: doubled.length };
    input:
      items: [1, 2, 3]`,
  },
  // ── 資料類（Data） ─────────────────────────────────────────────────────────
  {
    canonical_id: 'set',
    display_name: 'Set',
    category: 'data',
    description: '設定欄位：將靜態值寫入 context',
    config_example:
`  set_node:
    component: set
    values:
      status: active
      source: webhook`,
  },
  {
    canonical_id: 'array_ops',
    display_name: 'Array Ops',
    category: 'data',
    description: '陣列操作：push / pop / slice / length',
    config_example:
`  arr_node:
    component: array_ops
    operation: push
    key: items
    value: "{{new_item}}"`,
  },
  {
    canonical_id: 'string_ops',
    display_name: 'String Ops',
    category: 'data',
    description: '字串操作：upper / lower / trim / replace / split / join / length',
    config_example:
`  str_node:
    component: string_ops
    operation: upper
    input: "{{text}}"`,
  },
  {
    canonical_id: 'number_ops',
    display_name: 'Number Ops',
    category: 'data',
    description: '數字操作：add / sub / mul / div / round / floor / ceil / abs',
    config_example:
`  num_node:
    component: number_ops
    operation: add
    a: "{{price}}"
    b: 10`,
  },
  {
    canonical_id: 'date_ops',
    display_name: 'Date Ops',
    category: 'data',
    description: '日期操作：now / format / diff / add_days',
    config_example:
`  date_node:
    component: date_ops
    operation: now
    format: "2006-01-02 15:04:05"`,
  },
  {
    canonical_id: 'validate_json',
    display_name: 'Validate JSON',
    category: 'data',
    description: '驗證 context 欄位是否符合 JSON Schema',
    config_example:
`  validate_node:
    component: validate_json
    schema:
      type: object
      required: [email, name]
      properties:
        email:
          type: string
          format: email`,
  },
  // ── AI 類：已移除 ──────────────────────────────────────────────────────────
  // ai_transform_compile / ai_transform_run 於 2026-05-29 刪除（mindset §2：arcrun 是
  // AI 呼叫的工具，不是工具回頭呼叫 LLM）。需要 AI 判斷/轉換由操盤的 CC 自己做。
  // ── API 整合類（Recipe 型，不需 deploy Worker） ────────────────────────────
  {
    canonical_id: 'http_request',
    display_name: 'HTTP Request',
    category: 'api',
    description: '通用 HTTP 請求：支援任意 method / headers / body',
    config_example:
`  api_node:
    component: http_request
    url: "https://api.example.com/data"
    method: POST
    headers:
      Content-Type: application/json
    body:
      key: "{{value}}"`,
  },
  // ⚠️ 此處曾 hardcode gmail_send / google_sheets_append / telegram_send / line_notify_send / notion
  //    這 5 個是 **recipe（動態，存 store）不是零件（component）**，已於 W3.1 移除（issue #13 根治）。
  //    要找它們：`acr search <term>`（跨類）或 `acr recipe list` / `acr auth-recipe list`（動態清單）。
];

// ── 指令實作 ──────────────────────────────────────────────────────────────────

export async function cmdParts(): Promise<void> {
  const categoryLabels: Record<string, string> = {
    logic: '控制類（Control Flow）',
    data: '資料類（Data）',
    api: '整合類（API / Integration）',
    ai: 'AI 類',
  };

  const grouped: Record<string, ComponentDef[]> = {};
  for (const comp of BUILTIN_COMPONENTS) {
    if (!grouped[comp.category]) grouped[comp.category] = [];
    grouped[comp.category].push(comp);
  }

  console.log(chalk.bold(`\n  arcrun 零件庫（${BUILTIN_COMPONENTS.length} 個內建零件 / component，靜態 PR-only）\n`));

  for (const cat of ['logic', 'data', 'ai', 'api']) {
    const comps = grouped[cat];
    if (!comps?.length) continue;
    console.log(chalk.bold.underline(`  ${categoryLabels[cat]}`));
    for (const comp of comps) {
      const credStr = comp.credentials_required?.length
        ? chalk.yellow(`  （需要 ${comp.credentials_required.map(c => c.key).join(', ')}）`)
        : '';
      console.log(`  • ${chalk.cyan(comp.canonical_id.padEnd(22))}${comp.display_name}${credStr}`);
      console.log(chalk.gray(`       ${comp.description}`));
    }
    console.log('');
  }

  console.log(chalk.gray('  使用 acr parts scaffold <component> 取得 config 範本'));
  console.log('');
  console.log(chalk.bold('  零件之外（動態，存在 store，不在上面這份靜態清單）：'));
  console.log(chalk.gray('    • API recipe（打外部服務）       acr recipe list'));
  console.log(chalk.gray('    • 第三方服務認證（auth-recipe）   acr auth-recipe list'));
  console.log(chalk.gray('    • 已部署的 workflow              acr list'));
  console.log(chalk.cyan('    • 不確定某能力是哪一類？         acr search <關鍵字>   ← 跨類一次搜，免先選表'));
  console.log('');
}

export async function cmdPartsScaffold(componentId: string): Promise<void> {
  const comp = BUILTIN_COMPONENTS.find(c => c.canonical_id === componentId);

  if (!comp) {
    // 找不到內建零件 → 嘗試 auth recipe
    const config = loadConfig();
    const baseUrl = getCypherExecutorUrl(config);
    try {
      const res = await fetch(`${baseUrl}/auth-recipes/${componentId}`);
      if (res.ok) {
        const data = await res.json() as { recipe: { display_name?: string; description?: string; required_secrets: Array<{ key: string; label: string; type?: string; help?: string; help_url?: string }> } };
        const recipe = data.recipe;
        console.log(chalk.bold(`\n  ${componentId} — ${recipe.display_name ?? componentId}\n`));
        if (recipe.description) console.log(chalk.gray(`  ${recipe.description}\n`));
        console.log(chalk.cyan('  # credentials.yaml 範本（填入後執行 acr creds push）\n'));
        for (const s of recipe.required_secrets) {
          if (s.help) console.log(chalk.gray(`  # ${s.label}`));
          if (s.help) console.log(chalk.gray(`  # ${s.help}`));
          if (s.help_url) console.log(chalk.gray(`  # 說明文件：${s.help_url}`));
          if (s.type === 'json_blob') {
            console.log(`  ${s.key}: |`);
            console.log(`    {`);
            console.log(`      "type": "service_account",`);
            console.log(`      ...`);
            console.log(`    }`);
          } else {
            console.log(`  ${s.key}: ""`);
          }
          console.log('');
        }
        console.log(chalk.cyan('  # workflow.yaml config 範例\n'));
        console.log(`  ${componentId}_node:`);
        console.log(`    component: ${componentId}`);
        console.log(`    method: POST`);
        console.log(`    _path: /your-endpoint-path`);
        console.log('');
        console.log(chalk.gray(`  完整說明：acr auth-recipe info ${componentId}\n`));
        return;
      }
    } catch {
      // 離線或服務不可用，繼續顯示錯誤
    }

    console.error(chalk.red(`找不到零件 "${componentId}"。`));
    console.log(chalk.gray('執行 acr parts 查看內建零件。'));
    console.log(chalk.gray('執行 acr auth-recipe list 查看第三方服務整合。'));
    process.exit(1);
  }

  console.log(chalk.bold(`\n  ${comp.canonical_id} — ${comp.display_name}\n`));
  console.log(chalk.gray(`  ${comp.description}\n`));

  console.log(chalk.cyan('  # 貼入 workflow.yaml 的 config: 區塊'));
  console.log(comp.config_example.split('\n').map(l => `  ${l}`).join('\n'));

  if (comp.credentials_required?.length) {
    console.log(chalk.bold('\n  credentials.yaml 範本（填入後執行 acr creds push）\n'));
    for (const cred of comp.credentials_required) {
      console.log(chalk.cyan(`  # ${cred.type}（執行時自動注入為 ${cred.inject_as} 欄位）`));
      console.log(`  ${cred.key}: "your-token-here"\n`);
    }
  }

  console.log('');
}

export async function cmdPartsPublish(componentDir: string, options: { status?: string }): Promise<void> {
  const REGISTRY_URL = 'https://registry.arcrun.dev';

  if (options.status) {
    try {
      const res = await fetch(`${REGISTRY_URL}/submit/status/${options.status}`);
      const data = await res.json() as { status: string; visibility?: string; failed_step?: string; reason?: string; approved_at?: string };
      console.log(chalk.bold(`\n  提交狀態：${options.status}\n`));
      console.log(`  狀態：${data.status}`);
      if (data.visibility) console.log(`  Visibility：${data.visibility}`);
      if (data.failed_step) console.log(chalk.red(`  失敗步驟：${data.failed_step}`));
      if (data.reason) console.log(chalk.red(`  原因：${data.reason}`));
      if (data.approved_at) console.log(chalk.green(`  核准時間：${data.approved_at}`));
    } catch (e) {
      console.error(chalk.red(`查詢失敗：${e instanceof Error ? e.message : e}`));
    }
    return;
  }

  const config = loadConfig();
  if (!config.api_key) {
    console.error(chalk.red('缺少 API Key，請執行 acr init'));
    process.exit(1);
  }

  const contractPath = join(componentDir, 'component.contract.yaml');
  const mainGoPath = join(componentDir, 'main.go');
  const wasmName = componentDir.split('/').pop() ?? componentDir;
  const wasmPath = join(componentDir, `${wasmName}.wasm`);

  if (!existsSync(contractPath)) {
    console.error(chalk.red(`找不到 ${contractPath}`));
    process.exit(1);
  }
  if (!existsSync(wasmPath)) {
    console.error(chalk.red(`找不到 ${wasmPath}（請先編譯：tinygo build -o ${wasmName}.wasm -target wasi .）`));
    process.exit(1);
  }

  console.log(chalk.bold('\n  提交零件至 registry.arcrun.dev...\n'));

  const formData = new FormData();
  formData.append('contract', new Blob([readFileSync(contractPath)], { type: 'application/yaml' }), 'component.contract.yaml');
  if (existsSync(mainGoPath)) {
    formData.append('source', new Blob([readFileSync(mainGoPath)], { type: 'text/plain' }), 'main.go');
  }
  formData.append('wasm', new Blob([readFileSync(wasmPath)], { type: 'application/wasm' }), `${wasmName}.wasm`);

  try {
    const res = await fetch(`${REGISTRY_URL}/submit`, {
      method: 'POST',
      headers: { 'X-Arcrun-API-Key': config.api_key },
      body: formData,
    });

    if (!res.ok) {
      const err = await res.text();
      console.error(chalk.red(`提交失敗（${res.status}）：${err.slice(0, 200)}`));
      process.exit(1);
    }

    const data = await res.json() as { submission_id: string; status: string; visibility?: string };
    console.log(chalk.green(`✓ 提交成功`));
    console.log(`\n  Submission ID：${chalk.cyan(data.submission_id)}`);
    console.log(`  狀態：${data.status}`);
    if (data.visibility) console.log(`  Visibility：${data.visibility}`);
    console.log(chalk.gray(`\n  查詢進度：acr parts publish --status ${data.submission_id}\n`));
  } catch (e) {
    console.error(chalk.red(`提交失敗：${e instanceof Error ? e.message : e}`));
    process.exit(1);
  }
}
