/**
 * arcrun platform_crypto Worker
 *
 * POST / → JSON input {action, ...} → JSON output
 *
 * Actions:
 *   generate_api_key — HMAC-SHA256(email, ENCRYPTION_KEY) → ak_{hex[:32]}
 *   encrypt          — AES-GCM(plaintext, ENCRYPTION_KEY) → {encrypted, iv}（base64）
 *   random_token     — crypto random bytes → hex string
 *
 * 安全邊界：ENCRYPTION_KEY 只存在於 closure，永不進入外部（rule 02 §2.2）。
 * 此 Worker 直接用 crypto.subtle 實作，不走 WASM runner。
 * TinyGo WASM async host function 在 Cloudflare Workers 的 u6u namespace 不支援 Promise.
 * WASM 零件 (registry/components/platform_crypto/) 保留作為 edge-Go 移植時的參考。
 */

import { Hono } from 'hono';
import { cors } from 'hono/cors';

type Env = {
  ENCRYPTION_KEY: string;
};

type Input = {
  action: string;
  email?: string;
  plaintext?: string;
  bytes?: number;
};

const app = new Hono<{ Bindings: Env }>();
app.use('*', cors());

app.get('/', (c) => c.json({ ok: true, component: 'platform_crypto' }));

app.post('/', async (c) => {
  let input: Input;
  try {
    input = await c.req.json() as Input;
  } catch {
    return c.json({ success: false, error: 'request body must be JSON' }, 400);
  }

  const encryptionKey = c.env.ENCRYPTION_KEY;
  if (!encryptionKey) {
    return c.json({ success: false, error: 'ENCRYPTION_KEY not configured' }, 503);
  }

  try {
    switch (input.action) {
      case 'generate_api_key': {
        if (!input.email) return c.json({ success: false, error: 'email 必填' }, 400);
        const apiKey = await generateApiKey(input.email, encryptionKey);
        return c.json({ success: true, api_key: apiKey });
      }
      case 'encrypt': {
        if (!input.plaintext) return c.json({ success: false, error: 'plaintext 必填' }, 400);
        const { encrypted, iv } = await aesEncrypt(input.plaintext, encryptionKey);
        return c.json({ success: true, encrypted, iv });
      }
      case 'random_token': {
        const numBytes = (input.bytes ?? 32) > 0 ? (input.bytes ?? 32) : 32;
        const token = randomHex(numBytes);
        return c.json({ success: true, token });
      }
      default:
        return c.json({ success: false, error: `不支援的 action: ${input.action}` }, 400);
    }
  } catch (e) {
    return c.json(
      { success: false, error: e instanceof Error ? e.message : String(e) },
      500,
    );
  }
});

export default app;

// ── Crypto implementations (rule 02 §2.2: crypto.subtle 只准在 wasi-shim.ts 或 platform_crypto) ──

async function generateApiKey(email: string, encryptionKey: string): Promise<string> {
  const keyBytes = new TextEncoder().encode(encryptionKey.slice(0, 32));
  const cryptoKey = await crypto.subtle.importKey(
    'raw', keyBytes, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'],
  );
  const sig = await crypto.subtle.sign('HMAC', cryptoKey, new TextEncoder().encode(email));
  const hex = Array.from(new Uint8Array(sig)).map(b => b.toString(16).padStart(2, '0')).join('');
  return 'ak_' + hex.slice(0, 32);
}

async function aesEncrypt(plaintext: string, encryptionKey: string): Promise<{ encrypted: string; iv: string }> {
  const keyBytes = new TextEncoder().encode(encryptionKey.slice(0, 32));
  const cryptoKey = await crypto.subtle.importKey('raw', keyBytes, { name: 'AES-GCM' }, false, ['encrypt']);
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const enc = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    cryptoKey,
    new TextEncoder().encode(plaintext),
  );
  const toB64 = (buf: ArrayBuffer | Uint8Array) =>
    btoa(String.fromCharCode(...new Uint8Array(buf instanceof ArrayBuffer ? buf : buf)));
  return { encrypted: toB64(enc), iv: toB64(iv) };
}

function randomHex(numBytes: number): string {
  const arr = crypto.getRandomValues(new Uint8Array(numBytes));
  return Array.from(arr).map(b => b.toString(16).padStart(2, '0')).join('');
}
