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

import fs from "node:fs/promises";

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

export class PlanFileReadTool extends DroneTool {
  get name(): string {
    return "plan_file_read";
  }

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

  public definition: IToolDefinition = {
    type: "function",
    function: {
      name: this.name,
      description:
        "Read a file from Gadget's .gadget directory with line numbers. This Plan-mode tool works in Gadget's own directory (.gadget) for storing and reading plans, todos, and other knowledge. It does not affect project files.",
      parameters: {
        type: "object",
        properties: {
          path: { type: "string", description: "Path to the file to read, relative to the .gadget directory." },
          startLine: { type: "number", description: "Starting line number (1-indexed). Defaults to 1." },
          endLine: { type: "number", description: "Ending line number (inclusive). Defaults to end of file." },
        },
        required: ["path"],
      },
    },
  };

  public async execute(args: IToolArguments, logger: IAiLogger): Promise<string> {
    const filePath = args.path;
    if (typeof filePath !== "string" || filePath.trim().length === 0) {
      return toolError({
        code: "MISSING_PARAMETER",
        message: "File path must not be empty.",
        parameter: "path",
        recoveryHint: "Provide a valid file path within the .gadget directory.",
      });
    }

    const startLine = asPositiveInteger(args.startLine) ?? 1;
    const endLine = args.endLine === undefined ? undefined : asPositiveInteger(args.endLine);

    if (startLine < 1) {
      return toolError({
        code: "INVALID_PARAMETER",
        message: "startLine must be >= 1.",
        parameter: "startLine",
        expected: "A positive integer >= 1",
      });
    }
    if (endLine !== undefined && endLine < startLine) {
      return toolError({
        code: "INVALID_PARAMETER",
        message: "endLine must be >= startLine.",
        parameter: "endLine",
        expected: "An integer >= startLine",
      });
    }

    const resolved = resolvePlanPath(this.toolbox, filePath);
    if (typeof resolved === "string") return resolved;

    try {
      const stat = await fs.stat(resolved.absolutePath);
      if (!stat.isFile()) {
        return toolError({
          code: "INVALID_PARAMETER",
          message: `"${resolved.displayPath}" is not a file.`,
          parameter: "path",
          recoveryHint: "Provide a path to a regular file, not a directory.",
        });
      }

      const raw = await fs.readFile(resolved.absolutePath);
      if (isBinaryBuffer(raw)) {
        return [
          `PATH: ${resolved.displayPath}`,
          "TOTAL LINES: 0",
          "LINES SHOWN: 0",
          "FILE OPERATION: read",
          "---",
          `Binary file, cannot display: ${resolved.displayPath}`,
        ].join("\n");
      }

      const content = raw.toString("utf-8");
      const lines = content.split("\n");
      const startIdx = Math.max(0, startLine - 1);
      const endIdx = endLine !== undefined ? Math.min(endLine, lines.length) : lines.length;
      const selectedLines = lines.slice(startIdx, endIdx);
      const numberedLines = formatNumberedLines(selectedLines, startIdx);
      const totalLines = lines.length;
      const rangeLabel = endLine !== undefined || startLine > 1
        ? `lines ${startIdx + 1}-${endIdx} of ${totalLines}`
        : `${totalLines} lines`;

      return [
        `PATH: ${resolved.displayPath}`,
        `TOTAL LINES: ${totalLines}`,
        `LINES SHOWN: ${selectedLines.length}`,
        "FILE OPERATION: read",
        "---",
        `File: ${resolved.displayPath} (${rangeLabel})`,
        "",
        numberedLines,
      ].join("\n");
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      if (errorMessage.includes("ENOENT")) {
        const gadgetDir = getGadgetDir(this.toolbox);
        return toolError({
          code: "NOT_FOUND",
          message: `File not found in .gadget: ${resolved.displayPath}${gadgetDir ? `. The .gadget directory exists but this file does not.` : ". Plan storage is empty. Create a file with plan_file_write first."}`,
          parameter: "path",
          recoveryHint: "Check the file path. Use plan_list to see available files.",
        });
      }
      logger.error("failed to read plan file", { path: resolved.displayPath, error: errorMessage });
      return toolError({
        code: "OPERATION_FAILED",
        message: `Failed to read file: ${errorMessage}`,
      });
    }
  }
}
