import type { Bindings, ExecutionGraph } from '../types';
import { ExecutionError, WorkflowPaused } from '../types';
import { GraphExecutor } from '../graph-executor';
import { graphSchema } from '../lib/schemas';
import { createComponentLoader } from '../lib/component-loader';
import { recordComponentStats } from './execution-evaluator';
import { parseTriplets } from './triplet-parser';
import { searchNodes, type SearchMode, type SearchTarget } from './search-nodes';
import { buildExecutionGraph } from './graph-builder';

export async function handleCypherSearch(
  triplets: unknown[],
  env: Bindings,
  mode: SearchMode = 'discover',
  target?: SearchTarget,
): Promise<{ nodes: Record<string, unknown>; cypher: unknown; missing: string[] }> {
  const parsed = parseTriplets(triplets);
  if (!parsed) {
    throw new Error('無法解析任何節點');
  }

  // 2026-07-30：查 registry 判真實存在（workflow-discovery）。
  // `missing` 以前寫死 []，等於告訴 AI「什麼都有」——那是「腹語術」的入口。
  //
  // t158（07-31 迴歸修復，leo：「這裡只是複製一些工作流的 data 過去，沒有要在這裡驗證」）：
  // 誠實化只屬於 **discover**（AI 問「有沒有」）；**compile**（部署/推送的複製路徑）
  // 純編圖零查詢——那本來就是既有設計（workflows.json＝打包期預編的搬運），
  // 5cadc60 起誠實化漏進複製路徑＝迴歸（冷實例 8 節點 25.7s、安裝器 timeout 炸）。
  const { nodeResults, missingNodes } = await searchNodes(parsed, undefined, env, mode, target);

  const graph = buildExecutionGraph(parsed, nodeResults, 'cypher-search-result', 'Cypher Search Result');
  return { nodes: nodeResults, cypher: { nodes: graph.nodes, edges: graph.edges }, missing: missingNodes };
}

export async function handleCypherExecute(
  triplets: unknown[],
  context: Record<string, unknown> | undefined,
  graphId: string,
  graphName: string,
  config: Record<string, Record<string, unknown>> | undefined,
  env: Bindings,
  waitUntil: (promise: Promise<void>) => void,
  apiKey?: string,
): Promise<{
  success: boolean;
  data?: unknown;
  error?: string;
  trace?: unknown;
  duration_ms: number;
  graph?: ExecutionGraph;
  // resumable workflow: 節點 pending 時回 paused（不算 success 也不算 fail）
  paused?: boolean;
  task_id?: string;
  run_id?: string;
  paused_node_id?: string;
}> {
  const parsed = parseTriplets(triplets as unknown[]);
  if (!parsed) {
    throw new Error('無法解析任何節點');
  }

  // t158：執行路徑＝compile（零 discovery round-trip）——存在性由 component-loader
  // 在載入該節點時決定（原本的權威），查詢層不重複驗。
  const { nodeResults } = await searchNodes(parsed, config, env, 'compile');

  const graph = buildExecutionGraph(parsed, nodeResults, graphId, graphName, config);
  const parseResult = graphSchema.safeParse(graph);
  if (!parseResult.success) {
    throw new Error('圖定義產生失敗');
  }

  const loader = createComponentLoader(env);
  const executor = new GraphExecutor(loader, undefined, env, apiKey);
  const start = Date.now();

  try {
    const result = await executor.execute(parseResult.data as ExecutionGraph, context ?? {}, env.EXEC_CONTEXT);
    const duration_ms = Date.now() - start;

    // 非同步回寫每顆零件的執行統計（design.md「執行統計設計」；fire-and-forget 不阻擋回應）
    waitUntil(recordComponentStats(env, graph.nodes, result.trace));

    return { success: true, data: result.data, trace: result.trace, duration_ms, graph };
  } catch (err) {
    const duration_ms = Date.now() - start;

    // Resumable workflow: 節點回 pending → 回 paused 結構，不算成功也不算失敗
    // SDD: resumable-workflow/design.md
    if (err instanceof WorkflowPaused) {
      return {
        success: true,
        paused: true,
        task_id: err.task_id,
        run_id: err.run_id,
        paused_node_id: err.paused_node_id,
        trace: err.trace_so_far,
        duration_ms,
        graph,
      };
    }

    const errMsg = err instanceof Error ? err.message : String(err);
    // 失敗路徑同樣回寫每顆零件統計：ExecutionError 帶完整 trace（失敗節點有 error、
    // 之前成功的節點照記成功）；非 ExecutionError 無 trace 可歸因 → 不記（誠實：不瞎猜）。
    if (err instanceof ExecutionError) {
      waitUntil(recordComponentStats(env, graph.nodes, err.trace));
      const traceFormatted = err.trace.map(s => ({
        node: s.nodeId,
        status: s.error ? 'failed' : 'success',
        ...(s.error ? { error: s.error } : {}),
      }));
      throw new Error(JSON.stringify({
        success: false,
        error: errMsg,
        failed_node: err.failed_node,
        failed_input: err.failed_input,
        trace: traceFormatted,
        duration_ms,
      }));
    }
    throw err;
  }
}
