import { existsSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";

type ProcessResult = { ok: boolean; exitCode: number; stdout: string; stderr: string };

function resolveSkillRoot(cwd: string): string {
  const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));
  const candidates = [
    process.env.DEVFLOW_KIT_ROOT,
    path.join(cwd, ".skills"),
    path.resolve(moduleDirectory, "../skills"),
    path.resolve(moduleDirectory, "../../.."),
  ].filter(Boolean) as string[];
  for (const candidate of candidates) {
    if (existsSync(path.join(candidate, "workflow-code-index/scripts/code_index.py"))) {
      return path.resolve(candidate);
    }
  }
  throw new Error("Devflow intel skills not found");
}

async function run(command: string[], cwd: string): Promise<ProcessResult> {
  const child = Bun.spawn(command, { cwd, stdout: "pipe", stderr: "pipe" });
  const [exitCode, stdout, stderr] = await Promise.all([
    child.exited,
    new Response(child.stdout).text(),
    new Response(child.stderr).text(),
  ]);
  return { ok: exitCode === 0, exitCode, stdout: stdout.trim(), stderr: stderr.trim() };
}

function render(result: ProcessResult, details: Record<string, unknown>) {
  return {
    content: [
      {
        type: "text" as const,
        text: result.stdout || result.stderr || `Process exited with ${result.exitCode}`,
      },
    ],
    details: { ...details, exitCode: result.exitCode, stderr: result.stderr || undefined },
    isError: !result.ok,
  };
}

export default function devflowIntel(pi: ExtensionAPI) {
  const { z } = pi.zod;

  pi.registerTool({
    name: "devflow_code_index",
    label: "Devflow Code Index",
    description: "Scan, validate, audit, or query the deterministic project code index.",
    parameters: z.object({
      action: z.enum(["scan", "validate", "audit", "query", "relations", "impact"]),
      term: z.string().optional(),
      object: z.string().optional(),
      entity: z.string().optional(),
      changeType: z.enum(["remove_field", "rename_field", "change_type", "optional_to_required", "required_to_optional", "change_nullability", "change_enum"]).optional(),
      snapshot: z.string().optional(),
      depth: z.number().int().min(0).max(20).default(3),
      includeHeuristic: z.boolean().default(false),
      limit: z.number().int().min(1).max(100).default(20),
      changedFiles: z.array(z.string()).default([]),
    }),
    approval: (args) => {
      const action = (args as { action?: string }).action;
      // scan 写 .planning/code-index，按 exec 询问；validate/audit/query 只读，免确认。
      return action === "scan" ? "exec" : "read";
    },
    async execute(_id, params, _signal, _onUpdate, ctx) {
      const skillRoot = resolveSkillRoot(ctx.cwd);
      const script = path.join(skillRoot, "workflow-code-index/scripts/code_index.py");
      const args = ["python3", script, params.action];
      if (params.action === "scan") {
        args.push("--root", ctx.cwd, "--output", ".planning/code-index");
        if (params.changedFiles.length > 0) args.push("--changed-files", ...params.changedFiles);
      } else if (params.action === "validate") {
        args.push("--index", ".planning/code-index");
        if (params.snapshot) args.push("--snapshot", params.snapshot);
      } else if (params.action === "audit") {
        args.push("--root", ctx.cwd, "--index", ".planning/code-index", "--skill-root", skillRoot);
        if (params.snapshot) args.push("--snapshot", params.snapshot);
      } else if (params.action === "query") {
        if (!params.term) {
          return render({ ok: false, exitCode: 2, stdout: "", stderr: "term is required" }, {});
        }
        args.push("--index", ".planning/code-index", "--term", params.term, "--limit", String(params.limit));
        if (params.snapshot) args.push("--snapshot", params.snapshot);
      } else {
        if (params.action === "impact") {
          if (!params.entity) return render({ ok: false, exitCode: 2, stdout: "", stderr: "entity is required" }, {});
          args.push("--index", ".planning/code-index", "--entity", params.entity, "--depth", String(params.depth), "--limit", String(params.limit));
          if (params.snapshot) args.push("--snapshot", params.snapshot);
          if (params.changeType) args.push("--change-type", params.changeType);
          return render(await run(args, ctx.cwd), { action: params.action });
        }
        if (!params.object) return render({ ok: false, exitCode: 2, stdout: "", stderr: "object is required" }, {});
        args.push("--index", ".planning/code-index", "--object", params.object, "--depth", String(params.depth), "--limit", String(params.limit));
        if (params.snapshot) args.push("--snapshot", params.snapshot);
        if (params.includeHeuristic) args.push("--include-heuristic");
      }
      return render(await run(args, ctx.cwd), { action: params.action });
    },
  });

  pi.registerTool({
    name: "devflow_context_cursor",
    label: "Devflow Context Cursor",
    description: "Inspect or update repository cursors used by deterministic context reconciliation.",
    parameters: z.object({ args: z.array(z.string()).default([]) }),
    approval: "exec",
    async execute(_id, params, _signal, _onUpdate, ctx) {
      const skillRoot = resolveSkillRoot(ctx.cwd);
      const script = path.join(
        skillRoot,
        "workflow-context-sync/scripts/context_sync_cursor.py",
      );
      return render(await run(["python3", script, ...params.args], ctx.cwd), {
        action: "context-cursor",
      });
    },
  });
}
