// PRE Web GUI — File operation tools
// read_file, list_dir, glob, grep, file_write, file_edit

const fs = require('fs');
const pathMod = require('path');
const { nodeGlob, nodeGrep } = require('../platform');
const { parseVtt, looksLikeVtt } = require('../vtt');

const MAX_FILE_SIZE = 512 * 1024; // 512KB read limit

function resolvePath(p, cwd) {
  if (!p) return cwd;
  if (pathMod.isAbsolute(p)) return p;
  if (p.startsWith('~/')) return pathMod.join(require('os').homedir(), p.slice(2));
  return pathMod.resolve(cwd, p);
}

function readFile(args, cwd) {
  const filePath = resolvePath(args.path, cwd);
  try {
    const stat = fs.statSync(filePath);
    const raw = stat.size > MAX_FILE_SIZE
      ? fs.readFileSync(filePath, 'utf-8').slice(0, MAX_FILE_SIZE)
      : fs.readFileSync(filePath, 'utf-8');

    // WebVTT (.vtt) — meeting/lecture transcripts come this way from Zoom, Teams,
    // Otter, etc. Parse to clean speaker-attributed text so the model isn't
    // sifting through "00:00:04.500 -->" lines and cue numbers. Detection is
    // both by extension and by signature so renamed files still work.
    const isVtt = /\.vtt$/i.test(filePath) || looksLikeVtt(raw);
    if (isVtt) {
      const { text, cueCount, speakers } = parseVtt(raw);
      if (text) {
        let header = `[VTT transcript — ${cueCount} cue${cueCount === 1 ? '' : 's'}`;
        if (speakers.length) header += `, speakers: ${speakers.join(', ')}`;
        header += ']';
        const body = stat.size > MAX_FILE_SIZE
          ? text + `\n\n[...source was truncated at ${MAX_FILE_SIZE} bytes before parsing]`
          : text;
        return `${header}\n\n${body}`;
      }
    }

    if (stat.size > MAX_FILE_SIZE) {
      return raw + `\n\n[...truncated ${stat.size - MAX_FILE_SIZE} bytes — use bash to see full file]`;
    }
    return raw;
  } catch (err) {
    return `Error: cannot read file '${args.path}': ${err.message}`;
  }
}

function listDir(args, cwd) {
  const dirPath = resolvePath(args.path, cwd);
  try {
    const entries = fs.readdirSync(dirPath, { withFileTypes: true });
    const lines = [];
    for (const entry of entries) {
      if (entry.name.startsWith('.')) continue;
      try {
        const full = pathMod.join(dirPath, entry.name);
        const stat = fs.statSync(full);
        if (entry.isDirectory()) {
          lines.push(`  ${entry.name}/`);
        } else {
          const size = stat.size < 1024 ? `${stat.size}B`
            : stat.size < 1048576 ? `${(stat.size / 1024).toFixed(0)}K`
            : `${(stat.size / 1048576).toFixed(1)}M`;
          lines.push(`  ${entry.name} (${size})`);
        }
      } catch {}
    }
    return lines.join('\n') || '(empty directory)';
  } catch (err) {
    return `Error: cannot list directory '${args.path}': ${err.message}`;
  }
}

function glob(args, cwd) {
  const pattern = args.pattern;
  if (!pattern) return 'Error: no pattern provided';

  const basePath = resolvePath(args.path, cwd);
  const results = nodeGlob(basePath, pattern, 5, 100);
  return results.length > 0 ? results.join('\n') : 'No matches found';
}

function grep(args, cwd) {
  const pattern = args.pattern;
  if (!pattern) return 'Error: no pattern provided';

  const searchPath = resolvePath(args.path, cwd);
  return nodeGrep(pattern, searchPath, args.include, 100);
}

function fileWrite(args, cwd) {
  const filePath = resolvePath(args.path, cwd);
  const content = args.content;
  if (!content && content !== '') return 'Error: no content provided';

  try {
    // Ensure parent directory exists
    const dir = pathMod.dirname(filePath);
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
    fs.writeFileSync(filePath, content, 'utf-8');
    const size = Buffer.byteLength(content, 'utf-8');
    return `File written: ${filePath} (${size} bytes)`;
  } catch (err) {
    return `Error: cannot write file '${args.path}': ${err.message}`;
  }
}

function fileEdit(args, cwd) {
  const filePath = resolvePath(args.path, cwd);
  const oldString = args.old_string;
  const newString = args.new_string;

  if (!oldString) return 'Error: old_string is required';
  if (newString === undefined) return 'Error: new_string is required';

  try {
    let content = fs.readFileSync(filePath, 'utf-8');
    const count = content.split(oldString).length - 1;

    if (count === 0) return `Error: old_string not found in ${args.path}`;
    if (count > 1) return `Error: old_string found ${count} times (must be unique)`;

    content = content.replace(oldString, newString);
    fs.writeFileSync(filePath, content, 'utf-8');
    return `File edited: ${filePath}`;
  } catch (err) {
    return `Error: cannot edit file '${args.path}': ${err.message}`;
  }
}

module.exports = { readFile, listDir, glob, grep, fileWrite, fileEdit };
