// 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 { formatError, type IToolError } from "@gadget/ai";
import type { AiToolbox } from "../toolbox.ts";

export const BINARY_EXTENSIONS = new Set([
  ".o",
  ".obj",
  ".a",
  ".lib",
  ".so",
  ".dll",
  ".exe",
  ".bin",
  ".png",
  ".jpg",
  ".jpeg",
  ".gif",
  ".bmp",
  ".ico",
  ".webp",
  ".pdf",
  ".zip",
  ".tar",
  ".gz",
  ".bz2",
  ".7z",
  ".wasm",
  ".pyc",
  ".class",
]);

export interface ResolvedProjectPath {
  inputPath: string;
  absolutePath: string;
  displayPath: string;
}

export function toolError(error: IToolError): string {
  return formatError(error);
}

export function getProjectRoot(toolbox: AiToolbox): string | undefined {
  return toolbox.env.workspace?.projectDir;
}

export function getCacheDir(toolbox: AiToolbox): string | undefined {
  return toolbox.env.workspace?.cacheDir;
}

export function resolveProjectPath(
  toolbox: AiToolbox,
  inputPath: string,
): ResolvedProjectPath | string {
  const projectRoot = getProjectRoot(toolbox);
  if (!projectRoot) {
    return toolError({
      code: "OPERATION_NOT_ALLOWED",
      message: "No active project workspace is configured for file tools.",
      recoveryHint: "Run this tool during an active Agent work order.",
    });
  }

  const trimmedPath = inputPath.trim();
  if (!trimmedPath) {
    return toolError({
      code: "MISSING_PARAMETER",
      message: "File path must not be empty.",
      parameter: "path",
      recoveryHint: "Provide a valid file path within the project directory.",
    });
  }

  const root = path.resolve(projectRoot);
  const absolutePath = path.isAbsolute(trimmedPath)
    ? path.resolve(trimmedPath)
    : path.resolve(root, trimmedPath);
  const relative = path.relative(root, absolutePath);

  if (relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))) {
    return {
      inputPath: trimmedPath,
      absolutePath,
      displayPath: relative || ".",
    };
  }

  return toolError({
    code: "SECURITY_VIOLATION",
    message: `Path is outside the active project directory: ${trimmedPath}`,
    parameter: "path",
    recoveryHint: "Use a relative path inside the project workspace.",
  });
}

export function isBinaryBuffer(buffer: Buffer): boolean {
  const sampleSize = Math.min(buffer.length, 8192);
  for (let i = 0; i < sampleSize; i++) {
    const byte = buffer[i];
    if (byte === undefined) continue;
    if (byte === 0) return true;
    if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) return true;
  }
  return false;
}

export function isBinaryPath(filePath: string): boolean {
  return BINARY_EXTENSIONS.has(path.extname(filePath).toLowerCase());
}

export function asPositiveInteger(value: unknown): number | undefined {
  if (typeof value !== "number" || !Number.isFinite(value)) {
    return undefined;
  }
  return Math.floor(value);
}

export function formatNumberedLines(
  lines: string[],
  startIndex: number,
): string {
  return lines.map((line, index) => `${startIndex + index + 1}: ${line}`).join("\n");
}

export async function pathExists(filePath: string): Promise<boolean> {
  try {
    await fs.access(filePath);
    return true;
  } catch {
    return false;
  }
}
