/**
 * Execution Analytics — 零件執行後的統計回寫
 *
 * SDD: system-dev/docs/3-specs/arcrun-core-mvp/design.md「執行統計設計」
 * 執行完成處（cypher-handlers / webhook-handlers 收尾）對本次用到的**每顆零件**
 * fire-and-forget POST registry `/analytics/record`——統計失敗不影響執行、不增加同步延遲
 * （呼叫端一律用 waitUntil 包，仿 recordRecipeStats / recordTelemetry 既有慣例）。
 *
 * 每顆零件的成敗判定來源＝執行 trace（per-node）：
 *   - trace step 有 `error` → 失敗（runner throw）
 *   - output 是物件且 `success === false` → 失敗（makeHttpRunner 對非 2xx 不 throw，回這種）
 *   - 其餘 → 成功
 * FOREACH 重複執行同一節點 → trace 有幾筆就記幾次（每次真實執行都算一次樣本）。
 */

import type { GraphNode, TraceStep } from '../types';
import { wasmWorkerUrl } from '../lib/component-loader';

/** 本模組需要的環境子集（傳整份 Bindings 也相容，仿 SearchNodesEnv 慣例）。 */
export type AnalyticsEnv = {
  WORKER_SUBDOMAIN?: string;
  /** registry 位置覆蓋（可選；本地 wrangler dev / self-hosted 用）。未設 → wasmWorkerUrl('registry', WORKER_SUBDOMAIN)。 */
  REGISTRY_BASE_URL?: string;
};

export interface ComponentVerdict {
  component_id: string;
  success: boolean;
  duration_ms: number;
}

/** 從執行 trace 導出每顆零件的成敗（只算 type=Component 且有 componentId 的節點）。 */
export function componentVerdictsFromTrace(
  nodes: GraphNode[],
  trace: TraceStep[],
): ComponentVerdict[] {
  const componentByNodeId = new Map<string, string>();
  for (const n of nodes) {
    if (n.type === 'Component' && n.componentId) componentByNodeId.set(n.id, n.componentId);
  }

  const verdicts: ComponentVerdict[] = [];
  for (const step of trace) {
    const componentId = componentByNodeId.get(step.nodeId);
    if (!componentId) continue;

    const out = step.output;
    const outputSaysFailed =
      typeof out === 'object' && out !== null && !Array.isArray(out) &&
      (out as Record<string, unknown>).success === false;

    verdicts.push({
      component_id: componentId,
      success: !step.error && !outputSaysFailed,
      duration_ms: Math.max(0, Number(step.duration_ms) || 0),
    });
  }
  return verdicts;
}

/**
 * 對本次執行用到的每顆零件回寫統計到 registry（design.md「Analytics Record」）。
 * 永不 throw；呼叫端用 waitUntil 包，不阻擋主流程。
 */
export async function recordComponentStats(
  env: AnalyticsEnv,
  nodes: GraphNode[],
  trace: TraceStep[],
): Promise<void> {
  try {
    const base = (
      env.REGISTRY_BASE_URL ??
      (env.WORKER_SUBDOMAIN ? wasmWorkerUrl('registry', env.WORKER_SUBDOMAIN) : undefined)
    )?.replace(/\/$/, '');
    if (!base) return;

    const verdicts = componentVerdictsFromTrace(nodes, trace);
    if (verdicts.length === 0) return;

    await Promise.all(
      verdicts.map(v =>
        fetch(`${base}/analytics/record`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            canonical_id: v.component_id,
            success: v.success,
            duration_ms: v.duration_ms,
          }),
        }).catch(() => undefined), // 統計失敗不影響執行
      ),
    );
  } catch {
    // fire-and-forget：不拋錯，不影響主流程
  }
}
