/**
 * Arcrun#88：「零件目錄會說『這顆零件不存在』，但同一台實例上那顆零件跑得動」
 *
 * 病史（leo21c 實例實測，2026-08-11）：
 *   `/cypher/search` 對 `if_control`／`http_request` 回 `not_found`，
 *   但兩者其實由 component-loader.ts 直接解析（LOGIC_BINDING_MAP／WASM_HTTP_RUNNER_IDS），
 *   從不查 registry；registry catalog 端點在該實例回 404（`GET /components/catalog` →
 *   `{"success":false,"error":"零件 catalog 不存在"}`），search 因此誤判成「兩庫都查過沒有」。
 *
 * 本測試複現病史的環境條件（wrangler.test.toml 未設 WORKER_SUBDOMAIN → registryBase
 * undefined → catalog.status='unreachable'，等價於「registry 完全連不到」），
 * 驗證修法：RUNTIME_NATIVE_COMPONENT_IDS 的成員必須在 registry 查詢**之前**就短路成 found，
 * 不受 registry 健康狀態影響——因為它們的存在性從不依賴 registry。
 */
import { describe, it, expect, vi, afterEach } from 'vitest';
import { parseTriplets } from '../src/actions/triplet-parser';
import { searchNodes } from '../src/actions/search-nodes';

afterEach(() => {
  vi.unstubAllGlobals();
});

const IF_CONTROL_TRIPLETS = [
  'input >> ON_SUCCESS >> if_control',
];

const HTTP_REQUEST_TRIPLETS = [
  'input >> ON_SUCCESS >> http_request',
];

const MULTI_BUILTIN_TRIPLETS = [
  'input >> ON_SUCCESS >> switch',
  'input >> ON_SUCCESS >> filter',
  'input >> ON_SUCCESS >> code',
];

const FAKE_COMPONENT_TRIPLETS = [
  'input >> ON_SUCCESS >> totally_made_up_component_xyz',
];

describe('Arcrun#88：執行期原生零件不受 registry 健康狀態影響', () => {
  it('if_control（LOGIC_BINDING_MAP 成員）在 registry 不可達時仍回 found', async () => {
    const parsed = parseTriplets(IF_CONTROL_TRIPLETS);
    expect(parsed).not.toBeNull();
    const { nodeResults, missingNodes } = await searchNodes(parsed!, undefined, {
      // 無 WORKER_SUBDOMAIN／REGISTRY_BASE_URL → registryBase undefined → catalog unreachable
    });
    expect(nodeResults.if_control.status).toBe('found');
    expect(nodeResults.if_control.source).toBe('builtin');
    // if_control 會分岔，branch_hint 應隨 found 一併附上（不必逐顆再查一次）
    expect(nodeResults.if_control.branch_hint?.edge_types).toEqual(['ON_TRUE', 'ON_FALSE']);
    expect(missingNodes).not.toContain('if_control');
  });

  it('http_request（WASM_HTTP_RUNNER_IDS 成員）在 registry 不可達時仍回 found', async () => {
    const parsed = parseTriplets(HTTP_REQUEST_TRIPLETS);
    const { nodeResults, missingNodes } = await searchNodes(parsed!, undefined, {});
    expect(nodeResults.http_request.status).toBe('found');
    expect(nodeResults.http_request.source).toBe('builtin');
    expect(missingNodes).not.toContain('http_request');
  });

  it('switch／filter／code（同一批白名單的其他成員）也回 found，不逐一漏網', async () => {
    const parsed = parseTriplets(MULTI_BUILTIN_TRIPLETS);
    const { nodeResults } = await searchNodes(parsed!, undefined, {});
    expect(nodeResults.switch.status).toBe('found');
    expect(nodeResults.filter.status).toBe('found');
    expect(nodeResults.code.status).toBe('found');
  });

  it('registry 完全連不到時，真正不存在的名字誠實回 unknown（不敢空口說沒有——既有行為，修法沒有動它）', async () => {
    const parsed = parseTriplets(FAKE_COMPONENT_TRIPLETS);
    const { nodeResults } = await searchNodes(parsed!, undefined, {});
    expect(nodeResults.totally_made_up_component_xyz.status).toBe('unknown');
  });

  it('registry 查得到但目錄是空的（複現 leo21c 實例 catalog 404 的真實症狀）：真正不存在的名字回 not_found', async () => {
    // 複現生產實測：GET /components/catalog → HTTP 200 空陣列（本測試模擬「registry 活著但沒東西」，
    // 與 leo21c 實例的「404 零件 catalog 不存在」殊途同歸——都會落到「查得到、目錄無此零件」）。
    vi.stubGlobal('fetch', vi.fn(async () =>
      new Response(JSON.stringify({ success: true, data: { components: [], count: 0 } }), {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      }),
    ));
    const parsed = parseTriplets(FAKE_COMPONENT_TRIPLETS);
    const { nodeResults, missingNodes } = await searchNodes(parsed!, undefined, {
      WORKER_SUBDOMAIN: 'test-sub',
    });
    expect(nodeResults.totally_made_up_component_xyz.status).toBe('not_found');
    expect(missingNodes).toContain('totally_made_up_component_xyz');
  });

  it('registry 目錄是空的（catalog 通但無資料）時，執行期原生零件依然 found——這才是 Arcrun#88 的核心場景', async () => {
    // 這就是 leo21c 實例的真實狀態：registry 活著、目錄卻沒有任何一顆執行期原生零件的記錄
    // （SUBMISSIONS_KV 從未收到 if_control/http_request 的 submit）。若沒有本次修法，
    // 這裡會落到「兩庫都查過沒有」→ not_found，正是 Arcrun#88 回報的病徵。
    vi.stubGlobal('fetch', vi.fn(async () =>
      new Response(JSON.stringify({ success: true, data: { components: [], count: 0 } }), {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      }),
    ));
    const parsed = parseTriplets(IF_CONTROL_TRIPLETS);
    const { nodeResults } = await searchNodes(parsed!, undefined, { WORKER_SUBDOMAIN: 'test-sub' });
    expect(nodeResults.if_control.status).toBe('found');
    expect(nodeResults.if_control.source).toBe('builtin');
  });

  it('target=recipe 明確只查 recipe 庫時，執行期原生零件不搶答 found（尊重使用者明確限庫）', async () => {
    const parsed = parseTriplets(IF_CONTROL_TRIPLETS);
    const { nodeResults } = await searchNodes(parsed!, undefined, {}, 'discover', 'recipe');
    // if_control 從來不是 recipe，target=recipe 下不該被 builtin 短路成 found
    expect(nodeResults.if_control.status).not.toBe('found');
  });
});
