import { readFile } from 'node:fs/promises';
import type { Diagnostic } from './types.js';
import { lspManager } from './server-manager.js';
import { getDiagnostics } from './operations.js';
import { getServerConfig } from './config.js';

const SEVERITY = ['', 'error', 'warning', 'info', 'hint'] as const;
const MAX_LINES = 20;

/**
 * Format a diagnostic list into a compact, LLM-readable block. Pure — no IO —
 * so it is the single formatter shared by the `lsp_diagnostics` tool and the
 * post-edit injection in `finalizeWrite`. Returns a one-line "clean" message for
 * an empty list.
 */
export function formatDiagnosticsBlock(diagnostics: readonly Diagnostic[]): string {
  if (diagnostics.length === 0) return 'LSP: no diagnostics.';
  const errors = diagnostics.filter((d) => d.severity === 1).length;
  const warnings = diagnostics.filter((d) => d.severity === 2).length;
  const shown = diagnostics.slice(0, MAX_LINES).map((d) => {
    const sev = SEVERITY[d.severity] ?? 'unknown';
    return `  [${sev}] ${d.range.start.line + 1}:${d.range.start.character + 1} ${d.message}`;
  });
  const more =
    diagnostics.length > MAX_LINES ? `\n  ...and ${diagnostics.length - MAX_LINES} more` : '';
  return `LSP diagnostics (${errors} error(s), ${warnings} warning(s)):\n${shown.join('\n')}${more}`;
}

/**
 * Run LSP diagnostics for a file and format them. Best-effort: returns `null`
 * for an unsupported file type, an unavailable server, or any failure — it never
 * throws, because LSP is an enhancement to the write/read path, not a dependency
 * of it. `absPath` must be absolute (as stored on `pending_changes.file_path`).
 */
export async function formatFileDiagnostics(absPath: string): Promise<string | null> {
  try {
    if (!getServerConfig(absPath)) return null;
    const content = await readFile(absPath, 'utf8');
    const client = await lspManager.getClient(absPath);
    if (!client) return null;
    const diagnostics = await getDiagnostics(client, absPath, content);
    return formatDiagnosticsBlock(diagnostics);
  } catch {
    return null;
  }
}
