// 單元測試：sandboxAcceptance
// Requirements: 2.1, 2.2

import { describe, it, expect } from 'vitest';
import { runSandboxAcceptance } from '../src/actions/sandboxAcceptance';
import type { ComponentContract } from '../src/types';

const BASE_CONTRACT: ComponentContract = {
  canonical_id: 'validate_json',
  display_name: 'JSON 格式驗證器',
  category: 'logic',
  version: 'v1',
  wasi_target: 'preview1',
  stability: 'floating',
  runtime_compat: ['cf-workers', 'wazero'],
  constraints: {
    max_size_kb: 100,
    max_cold_start_ms: 50,
    no_network_syscall: true,
    io_model: 'stdin_stdout_json',
  },
  input_schema: { type: 'object' },
  output_schema: { type: 'object' },
  gherkin_tests: [
    { scenario: 'happy', given: '{}', then_contains: '{}' },
    { scenario: 'error', given: '{}', then_contains: '{}' },
  ],
};

// 建立合法的小型 WASM（最小 WASM magic + version header）
function makeMinimalWasm(extraBytes = 0): Uint8Array {
  const magic = [0x00, 0x61, 0x73, 0x6d]; // \0asm
  const version = [0x01, 0x00, 0x00, 0x00];
  const padding = new Array(extraBytes).fill(0x00);
  return new Uint8Array([...magic, ...version, ...padding]);
}

describe('runSandboxAcceptance', () => {
  // 註：G4 Gherkin 真實作後，minimal wasm（只有 magic header）無法 instantiate，
  // 會在 gherkin_tests 步驟失敗。同步步驟（size/syscall/fake）的失敗測試仍有效，
  // 因為它們在 Gherkin 之前就擋下。「全通過」需真實零件 wasm，移至整合測試。

  it('步驟 (a)：體積超過上限時失敗（在 Gherkin 前擋下）', async () => {
    const contract = { ...BASE_CONTRACT, constraints: { ...BASE_CONTRACT.constraints, max_size_kb: 1 } };
    const wasm = makeMinimalWasm(2000); // > 1KB
    const result = await runSandboxAcceptance(wasm, contract);
    expect(result.success).toBe(false);
    expect(result.failed_step).toBe('size_check');
    expect(result.reason).toContain('超過上限');
    expect(result.guide_anchor).toBeDefined();
    expect(result.canonical_id).toBe('validate_json');
    expect(result.version).toBe('v1');
  });

  it('步驟：含禁止 syscall 時失敗', async () => {
    const encoder = new TextEncoder();
    const syscallBytes = encoder.encode('sock_connect');
    const wasm = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, ...syscallBytes]);
    const result = await runSandboxAcceptance(wasm, BASE_CONTRACT);
    expect(result.success).toBe(false);
    expect(result.failed_step).toBe('syscall_scan');
    expect(result.reason).toContain('sock_connect');
  });

  it('size_check 失敗後不執行後續步驟', async () => {
    const encoder = new TextEncoder();
    const syscallBytes = encoder.encode('sock_connect');
    const padding = new Uint8Array(2000);
    const wasm = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, ...syscallBytes, ...padding]);
    const contract = { ...BASE_CONTRACT, constraints: { ...BASE_CONTRACT.constraints, max_size_kb: 1 } };
    const result = await runSandboxAcceptance(wasm, contract);
    expect(result.failed_step).toBe('size_check');
  });

  it('G1：contract 含外部 URL 的假零件被擋（最先擋）', async () => {
    const contract = { ...BASE_CONTRACT, canonical_id: 'fake_gmail', description: '打 https://gmail.googleapis.com 寄信' };
    const wasm = makeMinimalWasm(10);
    const result = await runSandboxAcceptance(wasm, contract);
    expect(result.success).toBe(false);
    expect(result.failed_step).toBe('fake_component_scan');
    expect(result.reason).toContain('recipe');
  });
});
