// POST /components — 零件提交端點（沙盒驗收流程）
// POST /components/index-only — metadata-only 索引（無 wasm、無沙盒，給 backfill 用）
// Requirements: 2.1, 2.2, 2.3
// SDD: matrix/arcrun/.agents/specs/component-registry-canon/design.md

import { Hono } from 'hono';
import type { Bindings } from '../types';
import { validateContract } from '../actions/validateContract';
import { submitComponent } from '../actions/submitComponent';
import type { SubmitOptions } from '../actions/submitComponent';
import { indexOnlyComponent } from '../actions/indexOnlyComponent';

const app = new Hono<{ Bindings: Bindings }>();

app.post('/', async c => {
  // 接受 multipart/form-data：contract（JSON 字串）+ wasm（binary）
  let contract: unknown;
  let wasmBytes: Uint8Array;
  // G0/G4：人類確認 + 舉證 + 本地 Gherkin evidence（multipart 或 JSON 皆可帶）
  const submitOptions: SubmitOptions = {};

  const contentType = c.req.header('content-type') ?? '';

  if (contentType.includes('multipart/form-data')) {
    const formData = await c.req.formData();
    const contractStr = formData.get('contract');
    const wasmFile = formData.get('wasm');

    if (!contractStr || typeof contractStr !== 'string') {
      return c.json({ success: false, error: '缺少 contract 欄位' }, 400);
    }
    if (!wasmFile || !(wasmFile instanceof File)) {
      return c.json({ success: false, error: '缺少 wasm 欄位' }, 400);
    }

    try {
      contract = JSON.parse(contractStr);
    } catch {
      return c.json({ success: false, error: 'contract 必須為合法 JSON' }, 400);
    }

    wasmBytes = new Uint8Array(await wasmFile.arrayBuffer());

    const hc = formData.get('human_confirmation');
    if (typeof hc === 'string') { try { submitOptions.human_confirmation = JSON.parse(hc); } catch { /* ignore */ } }
    const ge = formData.get('gherkin_evidence');
    if (typeof ge === 'string') { try { submitOptions.gherkin_evidence = JSON.parse(ge); } catch { /* ignore */ } }
    if (formData.get('skip_acceptance') === 'true') submitOptions.skip_acceptance = true;
  } else {
    // 也支援純 JSON（用於測試，wasm 以 base64 傳入）
    let body: Record<string, unknown>;
    try {
      body = await c.req.json();
    } catch {
      return c.json({ success: false, error: 'request body 必須為 multipart/form-data 或 JSON' }, 400);
    }

    contract = body.contract;
    const wasmBase64 = body.wasm_base64;

    if (!contract) {
      return c.json({ success: false, error: '缺少 contract 欄位' }, 400);
    }
    if (!wasmBase64 || typeof wasmBase64 !== 'string') {
      return c.json({ success: false, error: '缺少 wasm_base64 欄位' }, 400);
    }

    // base64 decode
    const binaryStr = atob(wasmBase64);
    wasmBytes = new Uint8Array(binaryStr.length);
    for (let i = 0; i < binaryStr.length; i++) {
      wasmBytes[i] = binaryStr.charCodeAt(i);
    }

    if (body.human_confirmation) submitOptions.human_confirmation = body.human_confirmation as SubmitOptions['human_confirmation'];
    if (body.gherkin_evidence) submitOptions.gherkin_evidence = body.gherkin_evidence as SubmitOptions['gherkin_evidence'];
    if (body.skip_acceptance === true) submitOptions.skip_acceptance = true;
  }

  // 驗證 contract 格式
  const validation = validateContract(contract);
  if (!validation.valid) {
    return c.json({
      success: false,
      failed_step: 'contract_validation',
      reason: `合約格式驗證失敗：${validation.errors.join(', ')}`,
      missing_fields: validation.missing_fields,
      guide_anchor: '#contract-example',
    }, 422);
  }

  // 執行沙盒驗收（含 G0 人類閘門）+ 寫入 metadata
  const result = await submitComponent(wasmBytes, validation.contract!, c.env, submitOptions);

  if (!result.success) {
    return c.json(result, 422);
  }

  return c.json(result, 201);
});

// POST /components/index-only — metadata-only 索引（給 backfill 用）
// 只接 contract（JSON），不收 wasm bytes、不沙盒驗收
// 用途：cypher-executor 已不從 R2 動態載 wasm（零件用獨立 Worker URL），
// 故已部署但未索引的零件，只要把 metadata 寫進 KV 讓 search 找得到即可。
app.post('/index-only', async c => {
  let body: Record<string, unknown>;
  try {
    body = await c.req.json();
  } catch {
    return c.json({ success: false, error: 'request body 必須為 JSON' }, 400);
  }

  const contract = body.contract;
  if (!contract) {
    return c.json({ success: false, error: '缺少 contract 欄位' }, 400);
  }

  // index-only 是 backfill 用，比 submit 寬鬆：
  // 既有零件可能沒 gherkin_tests / 沒 description / aliases — 補預設讓索引能進
  if (typeof contract === 'object' && contract !== null) {
    const c2 = contract as Record<string, unknown>;
    if (!c2.gherkin_tests || (Array.isArray(c2.gherkin_tests) && c2.gherkin_tests.length < 2)) {
      c2.gherkin_tests = [
        { scenario: 'placeholder happy', given: '{}', then_contains: '{' },
        { scenario: 'placeholder fail',  given: '{}', then_contains: '}' },
      ];
    }
    if (!c2.description) c2.description = '';
    if (!c2.tags) c2.tags = [];
  }

  const validation = validateContract(contract);
  if (!validation.valid) {
    return c.json({
      success: false,
      failed_step: 'contract_validation',
      reason: `合約格式驗證失敗：${validation.errors.join(', ')}`,
      missing_fields: validation.missing_fields,
    }, 422);
  }

  const result = await indexOnlyComponent(validation.contract!, c.env);
  return c.json(result, result.already_indexed ? 200 : 201);
});

export default app;
