import fs from "node:fs/promises";
import path from "node:path";
import { z } from "zod";
import type { AgentTool } from "@kenkaiiii/gg-agent";
import { resolvePath } from "./path-utils.js";

const FindParams = z.object({
  pattern: z.string().describe("Glob pattern to match files (e.g. '**/*.ts', 'src/**/*.tsx')"),
  path: z.string().optional().describe("Directory to search in (defaults to cwd)"),
});

const MAX_RESULTS = 100;

export function createFindTool(cwd: string): AgentTool<typeof FindParams> {
  return {
    name: "find",
    description:
      "Find files matching a glob pattern. Respects .gitignore. " +
      "Returns sorted file paths, truncated if more than 100 matches.",
    parameters: FindParams,
    async execute({ pattern, path: searchPath }) {
      const dir = searchPath ? resolvePath(cwd, searchPath) : cwd;

      // Dynamic import for ESM-only fast-glob
      const fg = await import("fast-glob");
      const ignore = await import("ignore");

      // Load .gitignore patterns
      const ignorePatterns = await loadGitignore(dir);
      const ig = ignore.default();
      ig.add(ignorePatterns);

      const entries = await fg.default(pattern, {
        cwd: dir,
        dot: false,
        onlyFiles: true,
        ignore: ["**/node_modules/**", "**/.git/**"],
        suppressErrors: true,
        followSymbolicLinks: false,
      });

      // Filter by .gitignore
      const filtered = entries.filter((entry) => !ig.ignores(entry));
      filtered.sort();

      const truncated = filtered.length > MAX_RESULTS;
      const shown = truncated ? filtered.slice(0, MAX_RESULTS) : filtered;

      let output = shown.join("\n");
      if (truncated) {
        output += `\n\n[Truncated: showing ${MAX_RESULTS} of ${filtered.length} matches]`;
      } else {
        output += `\n\n${filtered.length} file(s) found`;
      }

      return output;
    },
  };
}

async function loadGitignore(dir: string): Promise<string[]> {
  try {
    const content = await fs.readFile(path.join(dir, ".gitignore"), "utf-8");
    return content
      .split("\n")
      .map((l) => l.trim())
      .filter((l) => l && !l.startsWith("#"));
  } catch {
    return [];
  }
}
