/**
 * Execution Logger — 執行結果寫入 KBDB（fire-and-forget）
 *
 * KV 額度事故修復（總管交辦，2026-08-07）：舊版寫 ANALYTICS_KV（Workers KV），
 * key = stats:{workflowId}:{timestamp}（註解寫「避免覆蓋」）⇒ 只增不減、永不覆蓋。
 * 封測者 Evan 處理約 690 個檔案，KV 免費層 write 上限 1,000/日被打爆（實測 1,070 write）。
 *
 * KBDB 鐵律（leo 2026-06-14）：KBDB＝API-as-Wall，零 SQL——任何存取一律走 KBDB 的 HTTP API，
 * 不准直接對它的 D1 下 SQL。本檔因此**不直連任何 D1**，改 fire-and-forget POST
 * `{KBDB_BASE_URL}/execution-log/record`（連法/認證頭完全比照既有 recordRecipeStats
 * 慣例，見 webhook-handlers.ts；儲存/降級實作在 kbdb/src/actions/execution-log.ts）。
 *
 * leo 兩條判準：
 *   ① 執行紀錄是稽核資料 → 搬去 D1（entries 表，rows written 100,000/日，額度是 KV 的 100 倍）。
 *   ② 不是 n8n、不靠 Execution 計費 → 少記：不留每節點輸入輸出，只留時間/workflow/verdict/
 *      duration/錯誤訊息/(可得的)目標；成功記最少，失敗多記一點（截斷長度不對稱，見 KBDB 端）。
 *
 * A2 自我降級（門檻與降級邏輯全在 KBDB 端，見 execution-log.ts）：D1 額度仍與知識卡共用，
 * 超過門檻 KBDB 會回報 mode='skip'/'log_failure_only'，但**這件事對呼叫端透明**——
 * 本函式不管 KBDB 決定寫或不寫，一律 fire-and-forget、永不 throw，workflow 執行不受影響。
 */

import type { Bindings, GraphNode } from '../types';
import { kbdbBase } from '../routes/kbdb-proxy';

export interface ExecutionVerdict {
  workflow_id: string;
  verdict: 'success' | 'failed';
  duration_ms: number;
  message: string;
  target?: string;
}

/**
 * 從觸發時的 trigger context 擷取這次處理的目標（page_name / path），供「哪些檔沒進去」
 * 這種問題答得出來。只認這兩個 key（少記，不做窮舉式欄位挖掘/猜測）。
 */
function extractTarget(input?: Record<string, unknown>): string | undefined {
  if (!input) return undefined;
  const raw = input.page_name ?? input.path;
  if (raw === undefined || raw === null) return undefined;
  return typeof raw === 'string' ? raw : JSON.stringify(raw);
}

/**
 * 寫入執行結果至 KBDB（fire-and-forget，不阻擋主流程）。
 * 由 c.executionCtx.waitUntil() 包裹呼叫。
 *
 * @param nodes 保留參數相容既有呼叫端簽名（原本用來算 component_ids）；「不記每節點」
 *   是本次修復的明確要求（少記），此參數現不使用。
 * @param input 觸發時的 trigger context（可選）——只用來抓 page_name / path 當 target，
 *   不整包送出（少記：不留每節點輸入輸出，這裡也不例外）。
 * @param apiKey 觸發者的租戶（可選，/execute 舊路徑無租戶概念）。
 */
export async function writeExecutionVerdict(
  env: Bindings,
  workflowId: string,
  nodes: GraphNode[],
  verdict: 'success' | 'failed',
  durationMs: number,
  message: string,
  input?: Record<string, unknown>,
  apiKey?: string,
): Promise<void> {
  void nodes; // 少記：不再從節點算 component_ids，保留參數只為呼叫端相容
  try {
    const { base, headers } = kbdbBase(env);
    await fetch(`${base}/execution-log/record`, {
      method: 'POST',
      headers,
      body: JSON.stringify({
        workflow_id: workflowId,
        owner_id: apiKey ?? null,
        verdict,
        duration_ms: Math.max(0, Math.round(durationMs)),
        message: message ?? '',
        target: extractTarget(input) ?? null,
      }),
    });
  } catch {
    // fire-and-forget：任何錯誤（含 KBDB 端額度打滿、網路失敗）都吞掉、不影響主流程
  }
}
