// Copyright (C) 2026 Rob Colbert <rob.colbert@openplatform.us>
// Licensed under the Apache License, Version 2.0

import fs from "node:fs/promises";
import path from "node:path";

import type { IAiLogger, IToolArguments, IToolDefinition } from "@gadget/ai";
import { DroneTool } from "../tool.ts";
import { toolError } from "../system/common.ts";
import { getGadgetDir, resolvePlanPath } from "./common.ts";

export class PlanListTool extends DroneTool {
  get name(): string {
    return "plan_list";
  }

  get category(): string {
    return "plan";
  }

  public definition: IToolDefinition = {
    type: "function",
    function: {
      name: this.name,
      description:
        "List contents of Gadget's .gadget directory. This Plan-mode tool works in Gadget's own directory (.gadget) for exploring stored plans, todos, and other knowledge. It does not affect project files.",
      parameters: {
        type: "object",
        properties: {
          path: {
            type: "string",
            description: "Subdirectory path to list within .gadget, relative to the .gadget directory.",
          },
        },
        required: [],
      },
    },
  };

  public async execute(args: IToolArguments, logger: IAiLogger): Promise<string> {
    const gadgetDir = getGadgetDir(this.toolbox);
    if (!gadgetDir) {
      return toolError({
        code: "OPERATION_NOT_ALLOWED",
        message: "No active project workspace is configured for plan file tools.",
        recoveryHint: "Run this tool during an active Agent work order in Plan mode.",
      });
    }

    let targetPath: string;
    let displayPath: string;

    if (args.path !== undefined && typeof args.path === "string") {
      const resolved = resolvePlanPath(this.toolbox, args.path);
      if (typeof resolved === "string") return resolved;
      targetPath = resolved.absolutePath;
      displayPath = resolved.displayPath;
    } else {
      targetPath = path.resolve(gadgetDir);
      displayPath = ".";
    }

    try {
      let stat;
      try {
        stat = await fs.stat(targetPath);
      } catch {
        return "Plan storage is empty. The .gadget directory has not been created yet. Use plan_file_write to store your first file.";
      }

      if (!stat.isDirectory()) {
        return toolError({
          code: "INVALID_PARAMETER",
          message: `"${displayPath}" is not a directory.`,
          parameter: "path",
          recoveryHint: "Provide a directory path to list.",
        });
      }

      const entries = await fs.readdir(targetPath, { withFileTypes: true });
      if (entries.length === 0) {
        return `The .gadget directory "${displayPath}" is empty.`;
      }

      const lines: string[] = [
        `Contents of ".gadget${displayPath === "." ? "" : `/${displayPath}`}" (${entries.length} entries):`,
        "",
      ];

      for (const entry of entries) {
        const typeIndicator = entry.isDirectory() ? "d" : entry.isSymbolicLink() ? "l" : "-";
        let size = "-";
        let modified = "-";
        try {
          const entryStat = await fs.stat(path.join(targetPath, entry.name));
          if (entry.isFile()) size = entryStat.size.toString();
          modified = entryStat.mtime.toISOString().split("T")[0];
        } catch {
          // stat unavailable — skip size and date
        }
        lines.push(`${typeIndicator} ${size.padStart(10)} ${modified} ${entry.name}`);
      }

      return lines.join("\n");
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      logger.error("failed to list .gadget directory", { error: errorMessage });
      return toolError({
        code: "OPERATION_FAILED",
        message: `Failed to list directory: ${errorMessage}`,
      });
    }
  }
}
