// G1 假零件偵測（component-gatekeeping SDD，R2）
//
// 判準（DECISIONS §1）：零件若滿足任一，是假零件，該降級成 recipe / 工作流：
//   (a) contract 或 wasm binary 出現具體外部服務 URL / domain
//   (b) 宣告能力是 http_request 子集（打某固定 endpoint）
//
// Q2 決議（richblack 2026-05-29）：兩者都「硬擋」（不是只 warn）。
//   理由：零件不該連外，連外即 recipe。這兩個 pattern 都是「該是 recipe 的東西偽裝成零件」。
//
// 排除：auth_* primitive（credential 後端，DECISIONS §3b 不適用假零件判準）、http_request 自己。

import type { ComponentContract } from '../types';

// auth primitive 與 http_request 不適用假零件判準
const EXEMPT_IDS = new Set([
  'http_request',
  'auth_static_key',
  'auth_oauth2',
  'auth_service_account',
  'auth_mtls',
]);

// 外部 URL / domain pattern。
// - 明確的 scheme://host
// - 裸 domain（api.foo.com / foo.googleapis.com 之類）
const URL_SCHEME_RE = /\bhttps?:\/\/[^\s"'`)]+/i;
// 裸 domain：至少 host.tld，排除過於通用的誤判（如 stdin.json）——要求含常見 TLD 或 .xxx.yyy 多段
const BARE_DOMAIN_RE = /\b[a-z0-9][a-z0-9-]*(\.[a-z0-9-]+)*\.(com|org|net|dev|io|me|click|app|co|googleapis\.com|telegram\.org)\b/i;

/**
 * 把 contract 的文字欄位攤平成一個字串，供 URL 掃描。
 * 掃 description / display_name / input_schema / output_schema / tags / aliases。
 */
function flattenContractText(contract: ComponentContract): string {
  const parts: string[] = [
    contract.display_name ?? '',
    contract.description ?? '',
    JSON.stringify(contract.input_schema ?? {}),
    JSON.stringify(contract.output_schema ?? {}),
    (contract.tags ?? []).join(' '),
    (contract.aliases ?? []).join(' '),
  ];
  return parts.join('\n');
}

/**
 * 偵測投稿零件是否為假零件。
 * 回 null = 通過；回字串 = 退稿原因（已含指回正路的訊息）。
 */
export function detectFakeComponent(
  contract: ComponentContract,
  wasmBytes: Uint8Array,
): string | null {
  if (EXEMPT_IDS.has(contract.canonical_id)) {
    return null;
  }

  const pointToRecipe =
    '。這該是 API recipe（http_request + 固定設定）或工作流，不是零件。' +
    '零件 = 封閉邏輯（流程控制 / 資料處理），不連外部服務。見 DECISIONS §1。';

  // (a) contract 文字含外部 URL / domain
  const contractText = flattenContractText(contract);
  const schemeHit = contractText.match(URL_SCHEME_RE);
  if (schemeHit) {
    return `偵測到 contract 含外部 URL：${schemeHit[0].slice(0, 80)}${pointToRecipe}`;
  }
  const domainHit = contractText.match(BARE_DOMAIN_RE);
  if (domainHit) {
    return `偵測到 contract 含外部 domain：${domainHit[0]}${pointToRecipe}`;
  }

  // (a') wasm binary 文字含外部 URL / domain（零件原碼把 endpoint 編進去）
  // 只掃可印 ASCII 區段以降低誤判；wasm 字串以 UTF-8 存放。
  const wasmText = new TextDecoder('utf-8').decode(wasmBytes);
  const wasmSchemeHit = wasmText.match(URL_SCHEME_RE);
  if (wasmSchemeHit) {
    return `偵測到 wasm 內嵌外部 URL：${wasmSchemeHit[0].slice(0, 80)}${pointToRecipe}`;
  }

  // (b) http_request 子集：零件宣告自己只是「打某 API/endpoint」
  // heuristic：description 描述「打/呼叫 ... API/endpoint」+ input 有 url-like 欄位
  const desc = (contract.description ?? '') + ' ' + contract.display_name;
  const inputKeys = Object.keys(contract.input_schema ?? {}).join(' ').toLowerCase();
  const hasUrlField = /\b(url|endpoint|api_url|base_url|host)\b/.test(inputKeys);
  const describesHttpCall = /(打|呼叫|call|fetch|request|POST|GET|PUT|DELETE)\s*.*(api|endpoint|url|https?)/i.test(desc);
  if (hasUrlField && describesHttpCall) {
    return `偵測到疑似 http_request 子集（input 有 url 欄位 + 描述為打 API）${pointToRecipe}`;
  }

  return null;
}
