/**
 * Read tool — fetches a file's contents, optionally with line-range slicing.
 *
 * Output mirrors what Claude SDK's Read tool produces so the model — which
 * was trained against that format — uses it correctly. Line numbers are
 * prefixed with 1-based indices padded for alignment.
 */
import fs from 'fs';
import path from 'path';
import type { PiTool } from './types.js';
import { safeResolve, displayPath } from './path-safety.js';

const MAX_BYTES = 256 * 1024;        // 256 KB cap per read
const DEFAULT_LIMIT = 2000;          // default line cap

function formatWithLineNumbers(text: string, startLine: number): string {
  const lines = text.split('\n');
  return lines.map((line, i) => {
    const n = String(startLine + i).padStart(6, ' ');
    return `${n}\t${line}`;
  }).join('\n');
}

export const readTool: PiTool = {
  name: 'Read',
  description: 'Read a file from the workspace. Use this to inspect existing code, configuration, or data files.',
  inputSchema: {
    type: 'object',
    properties: {
      file_path: { type: 'string', description: 'Path to the file. Relative paths resolve against the workspace root.' },
      offset: { type: 'integer', description: '1-based line number to start at (default 1).', minimum: 1 },
      limit: { type: 'integer', description: 'How many lines to return (default 2000, max 2000).', minimum: 1 },
    },
    required: ['file_path'],
  },

  async run(input, ctx) {
    const filePath = input?.file_path;
    let abs: string;
    try {
      abs = safeResolve(ctx.cwd, filePath);
    } catch (err: any) {
      return { output: err.message, isError: true };
    }
    if (!fs.existsSync(abs)) {
      return { output: `File not found: ${displayPath(ctx.cwd, abs)}`, isError: true };
    }
    const stat = fs.statSync(abs);
    if (stat.isDirectory()) {
      return { output: `Path is a directory, not a file: ${displayPath(ctx.cwd, abs)}`, isError: true };
    }
    if (stat.size > MAX_BYTES) {
      return {
        output: `File too large (${stat.size} bytes; max ${MAX_BYTES}). Use a smaller range with offset/limit.`,
        isError: true,
      };
    }
    const raw = fs.readFileSync(abs, 'utf-8');
    const allLines = raw.split('\n');
    const offset = Math.max(1, Number(input?.offset) || 1);
    const limit = Math.min(DEFAULT_LIMIT, Math.max(1, Number(input?.limit) || DEFAULT_LIMIT));
    const slice = allLines.slice(offset - 1, offset - 1 + limit).join('\n');
    const truncatedNote = (offset - 1 + limit) < allLines.length
      ? `\n\n[Truncated — file has ${allLines.length} lines; showed ${offset}–${offset + limit - 1}.]`
      : '';
    if (!slice.trim()) {
      return { output: `(file ${displayPath(ctx.cwd, abs)} is empty${truncatedNote ? ` past line ${offset}` : ''})` };
    }
    return { output: formatWithLineNumbers(slice, offset) + truncatedNote };
  },
};
