// 沙盒驗收流程：五個步驟依序執行
// Requirements: 2.1, 2.2, 2.3

import { FORBIDDEN_SYSCALLS } from '../types';
import type { ComponentContract, SandboxResult, SandboxStep } from '../types';
import { detectFakeComponent } from './detectFakeComponent';
import { checkPureWasi } from './wasmImports';
// 註：G4 Gherkin 不在 registry 跑（CF Worker 禁止 runtime 編譯 wasm）。
// Gherkin 在投稿者本地（CLI/Node）跑，registry 只驗 gherkin_evidence 一致性。
// 見 .agents/specs/component-gatekeeping/design.md §4 修訂。

// ── 步驟 (G1)：假零件偵測（最先擋，component-gatekeeping SDD R2）─────────────────

function checkFakeComponent(wasmBytes: Uint8Array, contract: ComponentContract): string | null {
  return detectFakeComponent(contract, wasmBytes);
}

// ── 步驟 (a)：體積檢查 ────────────────────────────────────────────────────────

function checkSize(wasmBytes: Uint8Array, contract: ComponentContract): string | null {
  const maxSizeKb = contract.constraints.max_size_kb;
  const actualKb = wasmBytes.byteLength / 1024;
  if (actualKb > maxSizeKb) {
    return `體積 ${actualKb.toFixed(1)}KB 超過上限 ${maxSizeKb}KB`;
  }
  return null;
}

// ── 步驟 (b)：冷啟動時間（Phase 0 mock 0ms）────────────────────────────────────


// ── 步驟 (c)：syscall 掃描 ────────────────────────────────────────────────────

function scanSyscalls(wasmBytes: Uint8Array): string | null {
  // G3（R3）：import module 白名單 — 只准 wasi_snapshot_preview1 + u6u（避免 runtime 鎖定）。
  const pureWasiError = checkPureWasi(wasmBytes);
  if (pureWasiError !== null) {
    return pureWasiError;
  }

  // 次要：禁止 syscall 黑名單（網路/檔案系統 import 名）。
  // WASM binary 中 import section 的函數名稱以 UTF-8 字串形式存在。
  const text = new TextDecoder('utf-8').decode(wasmBytes);
  for (const syscall of FORBIDDEN_SYSCALLS) {
    if (text.includes(syscall)) {
      return `發現禁止的 syscall：${syscall}`;
    }
  }
  return null;
}

// 註：cold_start / runtime_compat 未實作（列 UNIMPLEMENTED_STEPS，不假綠）。
// Gherkin 執行不在 registry（CF 跑不了 wasm），在投稿者本地；registry 只驗 evidence。

// ── 主流程 ────────────────────────────────────────────────────────────────────

interface StepDef {
  name: SandboxStep;
  run: (wasmBytes: Uint8Array, contract: ComponentContract) => string | null;
  guideAnchor: string;
}

// registry 端把關步驟（全靜態，CF Worker 可跑；不執行 wasm）。
// G4 Gherkin 的「執行」在投稿者本地（CLI/Node），registry 只驗 evidence 一致性。
const STEPS: StepDef[] = [
  { name: 'fake_component_scan', run: checkFakeComponent, guideAnchor: '#fake-component' },
  { name: 'size_check',     run: checkSize,           guideAnchor: '#common-errors' },
  { name: 'syscall_scan',   run: scanSyscalls,        guideAnchor: '#syscall-constraints' },
];

// 未真正實作 / 未在 registry 端執行的步驟（誠實標記，§3c/§7 禁假綠）：
//   gherkin_tests：在投稿者本地跑，registry 只驗 evidence（不重跑）→ 第一期 evidence 可造假
//   cold_start / runtime_compat：mock，未實作
const UNIMPLEMENTED_STEPS: SandboxStep[] = ['gherkin_tests', 'cold_start', 'runtime_compat'];

/** 投稿者本地跑 Gherkin 的結果證據（CLI 上傳；registry 存證可審） */
export interface GherkinEvidence {
  scenario: string;
  given: string;
  actual_stdout: string;
  passed: boolean;
}

function fail(step: SandboxStep, reason: string, guideAnchor: string, contract: ComponentContract): SandboxResult {
  return {
    success: false,
    failed_step: step,
    reason,
    guide_anchor: guideAnchor,
    component_hash_id: '',
    canonical_id: contract.canonical_id,
    version: contract.version,
  };
}

/**
 * 驗 gherkin_evidence 與 contract 一致（不重跑 wasm，CF 跑不了）：
 *   - evidence 的 scenario 集合須涵蓋 contract.gherkin_tests 的每個 scenario
 *   - 每個 evidence.passed 須為 true
 * 回 null = 通過；回字串 = 退稿原因。
 * evidence 缺省（backfill / 舊投稿）→ 不擋（回 null），但 gherkin_tests 仍列 unimplemented（未驗證）。
 */
function checkGherkinEvidence(contract: ComponentContract, evidence?: GherkinEvidence[]): string | null {
  if (!evidence || evidence.length === 0) return null;
  const evidenceScenarios = new Set(evidence.map(e => e.scenario));
  for (const test of contract.gherkin_tests) {
    if (!evidenceScenarios.has(test.scenario)) {
      return `gherkin_evidence 缺少 scenario「${test.scenario}」的本地測試結果`;
    }
  }
  for (const e of evidence) {
    if (!e.passed) {
      return `gherkin_evidence scenario「${e.scenario}」本地未通過（passed=false）`;
    }
  }
  return null;
}

export function runSandboxAcceptance(
  wasmBytes: Uint8Array,
  contract: ComponentContract,
  gherkinEvidence?: GherkinEvidence[],
): SandboxResult {
  // 1. 靜態步驟（假零件 / 體積 / 純WASI；不執行 wasm）
  for (const step of STEPS) {
    const error = step.run(wasmBytes, contract);
    if (error !== null) {
      return fail(step.name, error, step.guideAnchor, contract);
    }
  }

  // 2. Gherkin evidence 一致性（投稿者本地已跑；registry 不重跑）
  const evidenceError = checkGherkinEvidence(contract, gherkinEvidence);
  if (evidenceError !== null) {
    return fail('gherkin_tests', evidenceError, '#local-testing', contract);
  }

  return {
    success: true,
    unimplemented_steps: UNIMPLEMENTED_STEPS,  // gherkin(本地跑,registry未重跑) / cold_start / runtime_compat
    component_hash_id: '',  // 由 submitComponent 在驗收通過後填入
    canonical_id: contract.canonical_id,
    version: contract.version,
  };
}
