// src/tools/search/glob.ts
// Copyright (C) 2025 DTP Technologies, LLC
// All Rights Reserved

import fs from "node:fs/promises";
import path from "node:path";
import type { ToolDefinition } from "../../lib/ai-client.js";

import {
  DtpTool,
  type ToolArguments,
  type ToolContext,
} from "../../lib/tool.js";
import { ChatSessionMode } from "../../models/chat-session.js";
import HostMonitorService from "../../services/host-monitor.js";

const MAX_RESULTS = 1000;

class GlobTool extends DtpTool {
  get name(): string {
    return "GlobTool";
  }
  get slug(): string {
    return "glob";
  }
  get metadata() {
    return {
      name: this.definition.function.name || "glob",
      category: "search",
      tags: ["search", "find", "file", "pattern", "glob"],
      modes: [
        ChatSessionMode.Plan,
        ChatSessionMode.Build,
        ChatSessionMode.Test,
        ChatSessionMode.Ship,
        ChatSessionMode.Develop,
      ],
    };
  }

  public definition: ToolDefinition = {
    type: "function",
    function: {
      name: "glob",
      description:
        "Find files by name using pattern matching. Supports glob patterns like **/*.ts, *.js, src/**/*.tsx. Returns a list of matching file paths.",
      parameters: {
        type: "object",
        properties: {
          pattern: {
            type: "string",
            description:
              "Glob pattern to match files (e.g., '**/*.ts', 'src/**/*.tsx', '*.json').",
          },
          root: {
            type: "string",
            description:
              "Root directory to search from. Defaults to current working directory.",
          },
        },
        required: ["pattern"],
      },
    },
  };

  public async execute(
    _context: ToolContext,
    args: ToolArguments,
  ): Promise<string> {
    const pattern = args.pattern as string | undefined;
    const root = (args.root as string | undefined) || process.cwd();

    if (!pattern || pattern.trim().length === 0) {
      return this.error("MISSING_PARAMETER", "pattern is required.", {
        parameter: "pattern",
        recoveryHint:
          "Provide a glob pattern like '**/*.ts' or 'src/**/*.tsx'.",
      });
    }

    try {
      const matches = await this.globMatch(pattern, root);
      const limited = matches.slice(0, MAX_RESULTS);

      if (matches.length > MAX_RESULTS) {
        const output = `Found ${matches.length} files matching "${pattern}" (showing first ${MAX_RESULTS}):\n\n${limited.join("\n")}\n\n... and ${matches.length - MAX_RESULTS} more files`;
        const byteCount = Buffer.byteLength(output, "utf-8");
        HostMonitorService.toolCall(byteCount);
        return this.success(
          {
            files: limited,
            total: matches.length,
            truncated: true,
          },
          output,
        );
      }

      const output = `Found ${matches.length} file(s) matching "${pattern}":\n\n${matches.join("\n")}`;
      const byteCount = Buffer.byteLength(output, "utf-8");
      HostMonitorService.toolCall(byteCount);
      return this.success(
        {
          files: matches,
          total: matches.length,
          truncated: false,
        },
        output,
      );
    } catch (error) {
      const errorMessage =
        error instanceof Error ? error.message : String(error);
      return this.error(
        "OPERATION_FAILED",
        `Failed to search: ${errorMessage}`,
      );
    }
  }

  private async globMatch(pattern: string, root: string): Promise<string[]> {
    const results: string[] = [];
    const normalizedRoot = path.resolve(root);

    const parts = pattern.split("/");
    let isRecursive = false;
    let searchPattern = pattern;

    if (parts[0] === "**") {
      isRecursive = true;
      searchPattern = parts.slice(1).join("/");
    }

    const regexPattern = this.globToRegex(searchPattern);

    await this.recurseDir(
      normalizedRoot,
      regexPattern,
      isRecursive,
      results,
      0,
      20,
    );

    return results.sort();
  }

  private globToRegex(pattern: string): RegExp {
    let regexStr = pattern
      .replace(/[.+^${}()|[\]\\]/g, "\\$&")
      .replace(/\*/g, ".*")
      .replace(/\?/g, ".");

    regexStr = "^" + regexStr + "$";

    return new RegExp(regexStr);
  }

  private async recurseDir(
    dir: string,
    pattern: RegExp,
    recursive: boolean,
    results: string[],
    depth: number,
    maxDepth: number,
  ): Promise<void> {
    if (depth > maxDepth) return;

    let entries;
    try {
      entries = await fs.readdir(dir, { withFileTypes: true });
    } catch {
      return;
    }

    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      const relativePath = path.relative(process.cwd(), fullPath);

      if (entry.isFile()) {
        if (pattern.test(entry.name)) {
          results.push(relativePath);
        }
      } else if (entry.isDirectory()) {
        if (
          entry.name !== "node_modules" &&
          entry.name !== ".git" &&
          entry.name !== "dist" &&
          entry.name !== "build"
        ) {
          if (recursive) {
            await this.recurseDir(
              fullPath,
              pattern,
              recursive,
              results,
              depth + 1,
              maxDepth,
            );
          } else if (pattern.test(entry.name)) {
            results.push(relativePath + "/");
          }
        }
      }
    }
  }
}

export default new GlobTool();
