/**
 * Workflow CRUD tools — LI SDD M2.2
 *
 * 對應 .agents/specs/llm-interface/ Milestone 2.2。
 *
 * 取代早期壞掉的 deploy_workflow（呼叫 /workflows/deploy — 該 endpoint 不存在），
 * 以及舊的 list/get workflow（透過 KBDB 撈 metadata 而非直接問 cypher-executor 真實狀態）。
 *
 * 新 tool 直打 cypher-executor /webhooks/named*：
 *   - arcrun_push_workflow
 *   - arcrun_list_workflows
 *   - arcrun_get_workflow
 *   - arcrun_delete_workflow
 *   - arcrun_run_workflow
 *
 * tool 名前綴一律走 brand.ts 的 toolName() helper（single source of truth）。
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import type { Env } from "../types.js";
import { cypherFetch, errorResponse, successResponse } from "../lib/cypher-client.js";
import { parse as parseYaml } from "yaml";

const apiKeyDesc =
  "你（用戶）的 arcrun api_key (ak_xxx)。從 https://arcrun.dev/me 取得";

/**
 * arcrun_push_workflow — 部署 YAML workflow
 *
 * 接受 yaml_content 或 graph object 兩種輸入。yaml_content 內部 parse 成 graph。
 */
export function registerPushWorkflow(server: McpServer, env: Env) {
  server.tool(
    toolName("push_workflow"),
    "部署 workflow 到 arcrun。輸入可以是 YAML 字串或 graph 物件。**建議先 call arcrun_validate_yaml 確認 schema**。",
    {
      api_key: z.string().describe(apiKeyDesc),
      yaml_content: z.string().optional().describe(
        "YAML 字串。內部會 parse 成 {name, flow, config}。優先於 graph 參數"
      ),
      graph: z.unknown().optional().describe(
        "已 parse 過的 graph 物件（含 nodes, edges）。yaml_content 沒給才用此"
      ),
      name: z.string().optional().describe(
        "workflow 名稱（只能 [a-zA-Z0-9_-]）。若給 yaml_content 會從 yaml 抽 name 欄"
      ),
      description: z.string().optional().describe("workflow 描述（選填）"),
    },
    async ({ api_key, yaml_content, graph, name, description }) => {
      let workflowName: string | undefined = name;
      let workflowGraph: unknown = graph;
      let workflowConfig: unknown = undefined;

      // 如果有 yaml_content，parse 出 name + graph + config
      if (yaml_content) {
        try {
          const parsed = parseYaml(yaml_content) as {
            name?: string;
            description?: string;
            flow?: string[];
            config?: Record<string, unknown>;
            nodes?: unknown[];
            edges?: unknown[];
          };
          workflowName = workflowName ?? parsed.name;
          description = description ?? parsed.description;
          workflowConfig = parsed.config;

          // 若 yaml 是已展開的 {nodes, edges} 格式
          if (parsed.nodes && parsed.edges) {
            workflowGraph = { nodes: parsed.nodes, edges: parsed.edges };
          }
          // 若 yaml 是 cypher binding {flow, config} 格式，傳 raw 給 cypher-executor parse
          else if (parsed.flow && parsed.config) {
            workflowGraph = { flow: parsed.flow, config: parsed.config };
          }
        } catch (e) {
          return errorResponse(
            "validation_failed",
            `YAML parse 失敗：${e instanceof Error ? e.message : String(e)}`,
            ["檢查 YAML 縮排 / 引號 / 冒號", "用 yamllint 或 validator 先過一次"],
          );
        }
      }

      if (!workflowName) {
        return errorResponse(
          "validation_failed",
          "缺少 workflow name（yaml_content 內 'name:' 欄或 name 參數）",
          ["yaml 加 name: my_workflow 欄", "或直接傳 name 參數"],
        );
      }

      if (!workflowGraph) {
        return errorResponse(
          "validation_failed",
          "缺少 graph 資料（yaml_content 內 flow+config 或 nodes+edges，或直接傳 graph 參數）",
          ["yaml 至少含 flow: + config: 兩欄", "或直接傳 graph 參數"],
        );
      }

      try {
        const res = await cypherFetch(env, "/webhooks/named", {
          apiKey: api_key,
          method: "POST",
          body: {
            name: workflowName,
            graph: workflowGraph,
            config: workflowConfig,
            description,
          },
        });

        const body = await res.json().catch(() => ({} as Record<string, unknown>));
        if (!res.ok) {
          return errorResponse(
            "push_failed",
            `部署失敗 HTTP ${res.status}: ${(body as { error?: string }).error ?? 'unknown'}`,
            [
              "先 call arcrun_validate_yaml 確認 graph schema 正確",
              "確認 workflow name 符合 [a-zA-Z0-9_-] 格式",
              "確認 api_key 是 ak_xxx 格式且有效",
            ],
            JSON.stringify(body),
          );
        }

        const result = body as { name?: string; webhook_url?: string };
        return successResponse(result, [
          `部署成功！webhook URL: ${result.webhook_url}`,
          `下一步：call arcrun_run_workflow('${result.name}', {你的 input}) 測試`,
          "或對 webhook URL 直接 curl POST 觸發",
        ]);
      } catch (e) {
        return errorResponse(
          "internal_error",
          e instanceof Error ? e.message : String(e),
          ["重試一次"],
        );
      }
    },
  );
}

export function registerListWorkflows(server: McpServer, env: Env) {
  server.tool(
    toolName("list_workflows"),
    "列出你 (api_key 對應 namespace) 已部署的所有 workflow。回 [{name}]。",
    {
      api_key: z.string().describe(apiKeyDesc),
    },
    async ({ api_key }) => {
      try {
        const res = await cypherFetch(env, "/webhooks/named", {
          apiKey: api_key,
        });
        const body = await res.json().catch(() => null);
        if (!res.ok) {
          return errorResponse(
            "fetch_failed",
            `撈 workflow 列表失敗 HTTP ${res.status}`,
            ["確認 api_key 正確", "稍後重試"],
            JSON.stringify(body),
          );
        }
        const data = body as { workflows?: Array<{ name: string; webhook_url?: string }> };
        return successResponse(data, [
          `${data.workflows?.length ?? 0} 個 workflow`,
          "call arcrun_get_workflow(name) 看單個細節",
          "call arcrun_list_recent_executions(workflow_name) 看執行歷史",
        ]);
      } catch (e) {
        return errorResponse(
          "internal_error",
          e instanceof Error ? e.message : String(e),
          ["重試一次"],
        );
      }
    },
  );
}

export function registerGetWorkflow(server: McpServer, env: Env) {
  server.tool(
    toolName("get_workflow"),
    "看單一 workflow 的完整定義（graph + config）。",
    {
      api_key: z.string().describe(apiKeyDesc),
      name: z.string().describe("workflow 名稱"),
    },
    async ({ api_key, name }) => {
      try {
        // cypher-executor 既有 /webhooks/named GET 只回 [{name}] 不含細節，
        // 要走 KV 直接讀 — 目前沒有單個 workflow GET endpoint。
        // workaround：撈 list 然後 client filter（M2.x 加 GET /webhooks/named/:name）
        const res = await cypherFetch(env, "/webhooks/named", {
          apiKey: api_key,
        });
        const body = await res.json().catch(() => null) as {
          workflows?: Array<{ name: string; webhook_url?: string }>;
        } | null;

        if (!res.ok || !body?.workflows) {
          return errorResponse(
            "fetch_failed",
            `撈 workflow 列表失敗`,
            ["確認 api_key 正確"],
            JSON.stringify(body),
          );
        }

        const found = body.workflows.find((w) => w.name === name);
        if (!found) {
          return errorResponse(
            "not_found",
            `workflow "${name}" 不存在或不屬於你`,
            [
              "call arcrun_list_workflows 看你有什麼 workflow",
              "確認名稱拼寫正確（注意大小寫）",
            ],
          );
        }

        return successResponse(
          {
            name: found.name,
            webhook_url: found.webhook_url,
            note: "目前 list endpoint 不回完整 graph，未來會加 GET /webhooks/named/:name",
          },
          [
            "可 call arcrun_list_recent_executions 看執行歷史",
            "可 call arcrun_run_workflow 觸發測試",
          ],
        );
      } catch (e) {
        return errorResponse(
          "internal_error",
          e instanceof Error ? e.message : String(e),
          ["重試一次"],
        );
      }
    },
  );
}

export function registerDeleteWorkflow(server: McpServer, env: Env) {
  server.tool(
    toolName("delete_workflow"),
    "刪除 workflow。**不可逆，確認後再做**。會清掉對應 cron index 與 webhook URL。",
    {
      api_key: z.string().describe(apiKeyDesc),
      name: z.string().describe("要刪的 workflow 名稱"),
      confirm: z.literal(true).describe("必須傳 true 確認"),
    },
    async ({ api_key, name, confirm: _confirm }) => {
      try {
        const res = await cypherFetch(env, `/webhooks/named/${encodeURIComponent(name)}`, {
          apiKey: api_key,
          method: "DELETE",
        });
        const body = await res.json().catch(() => ({}));
        if (!res.ok) {
          return errorResponse(
            res.status === 404 ? "not_found" : "delete_failed",
            res.status === 404
              ? `workflow "${name}" 不存在`
              : `刪除失敗 HTTP ${res.status}`,
            [
              "call arcrun_list_workflows 確認名稱",
              "若已不存在可忽略此錯誤",
            ],
            JSON.stringify(body),
          );
        }
        return successResponse({ deleted: name }, [
          `已刪除 ${name}`,
          "若該 workflow 有 cron，索引也已清",
        ]);
      } catch (e) {
        return errorResponse(
          "internal_error",
          e instanceof Error ? e.message : String(e),
          ["重試一次"],
        );
      }
    },
  );
}

export function registerRunWorkflow(server: McpServer, env: Env) {
  server.tool(
    toolName("run_workflow"),
    "觸發 workflow 執行。input 物件帶進 trigger context。回 {success, data, trace?, duration_ms}。",
    {
      api_key: z.string().describe(apiKeyDesc),
      name: z.string().describe("workflow 名稱"),
      input: z.record(z.unknown()).optional().describe(
        "trigger context（會塞進 workflow 第一個節點的輸入）。記得帶 api_key 給內部需要的節點用"
      ),
    },
    async ({ api_key, name, input }) => {
      try {
        const triggerBody = input ?? {};
        // 若 input 沒帶 api_key，自動補（內部多數零件需要）
        if (!('api_key' in triggerBody)) {
          (triggerBody as Record<string, unknown>).api_key = api_key;
        }

        const res = await cypherFetch(env, `/webhooks/named/${encodeURIComponent(name)}/trigger`, {
          apiKey: api_key,
          method: "POST",
          body: triggerBody,
        });
        const body = await res.json().catch(() => null) as {
          success?: boolean;
          data?: unknown;
          error?: string;
          duration_ms?: number;
          trace?: unknown;
        } | null;

        if (res.status === 404) {
          return errorResponse(
            "not_found",
            `workflow "${name}" 不存在`,
            ["call arcrun_list_workflows 確認名稱", "或先 arcrun_push_workflow 部署"],
          );
        }

        // workflow 自己 success/fail 不算 HTTP 錯誤
        const isPaused = body?.error && /workflow paused/i.test(body.error);
        if (isPaused) {
          return successResponse(
            { ...body, status: "running_async" },
            [
              "workflow 已接受，正在背景跑（等 claude_api 等 daemon callback）",
              "call arcrun_list_paused_executions 看當前 running_async 的",
              "正常 30-90 秒會 resume 完成（從 user 角度像同步完成）",
            ],
          );
        }

        if (!body?.success) {
          return errorResponse(
            "execution_failed",
            body?.error ?? `執行失敗 HTTP ${res.status}`,
            [
              "看 trace 陣列第一個 status=failed 的 node 是哪個",
              "call arcrun_list_recent_executions 看歷史趨勢",
            ],
            JSON.stringify(body),
          );
        }

        return successResponse(body, [
          `執行成功，耗時 ${body.duration_ms}ms`,
          "call arcrun_list_recent_executions 看歷史 verdict",
        ]);
      } catch (e) {
        return errorResponse(
          "internal_error",
          e instanceof Error ? e.message : String(e),
          ["重試一次"],
        );
      }
    },
  );
}

export function registerAllWorkflowCrudTools(server: McpServer, env: Env) {
  registerPushWorkflow(server, env);
  registerListWorkflows(server, env);
  registerGetWorkflow(server, env);
  registerDeleteWorkflow(server, env);
  registerRunWorkflow(server, env);
}
