/** Log tools — read the Forge server log so a user can ask "what's up with
 *  Forge?" without opening the UI. Reads <dataDir>/forge.log directly.
 *  Real-time streaming is a v1b TODO (needs an SSE endpoint). */

import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { existsSync, statSync, openSync, readSync, closeSync } from 'node:fs';
import { join } from 'node:path';
import { getDataDir } from '@/lib/dirs';
import { text, guard } from './_shared';

/** Read the last `maxBytes` of a file, dropping a partial first line. */
function tailBytes(file: string, maxBytes: number): string {
  const size = statSync(file).size;
  const readSize = Math.min(size, maxBytes);
  const buf = Buffer.alloc(readSize);
  const fd = openSync(file, 'r');
  try { readSync(fd, buf, 0, readSize, size - readSize); }
  finally { closeSync(fd); }
  const str = buf.toString('utf-8');
  const nl = str.indexOf('\n');
  return nl > 0 && readSize < size ? str.slice(nl + 1) : str;
}

export function registerLogTools(server: McpServer): void {
  server.tool(
    'forge_tail_logs',
    'Show the most recent Forge server log lines (the next-server process log). Optionally filter by a search term. Use this to diagnose "what is Forge doing / why did X fail".',
    {
      lines: z.number().int().positive().max(1000).optional().describe('How many recent lines to show (default 100, max 1000)'),
      search: z.string().optional().describe('Only show lines containing this text (case-insensitive)'),
    },
    (params) => guard(() => {
      const file = join(getDataDir(), 'forge.log');
      if (!existsSync(file)) {
        // forge.log is the next-server's redirected stdout — written when Forge is
        // started via `forge server start` / `start.sh`, but NOT by a bare `next dev`
        // (which logs to its own terminal). Point the caller at the records that DO
        // carry per-step errors instead of dead-ending on "no log file".
        return text(
          `No server log file at ${file}.\n`
          + `It's written only when Forge is started via \`forge server start\` or \`start.sh\` — a bare \`next dev\` logs to its own stdout/terminal.\n`
          + `For why a task or pipeline failed, the error is on the record itself: forge_list_tasks status="failed", then forge_get_task id=…, or forge_get_pipeline id=… for per-node errors.`,
        );
      }
      const n = params.lines ?? 100;
      let all = tailBytes(file, 512 * 1024).split('\n').filter(Boolean);
      if (params.search) {
        const q = params.search.toLowerCase();
        all = all.filter((l) => l.toLowerCase().includes(q));
      }
      const shown = all.slice(-n);
      if (shown.length === 0) return text(params.search ? `No log lines match "${params.search}".` : 'Log is empty.');
      return text(`Last ${shown.length} line(s)${params.search ? ` matching "${params.search}"` : ''} of ${file}:\n\n${shown.join('\n')}`);
    }),
  );
}
