import { isAbsolute, relative, resolve } from "node:path";

export interface OpenEditorRequest {
  file: string;
  line?: number;
  column?: number;
}

export interface OpenEditorOptions {
  root: string;
  editor?: string;
  spawn?: (command: string[], options?: { cwd?: string }) => unknown;
}

const EDITORS: Record<string, (file: string, line: number, column: number) => string[]> = {
  code: (file, line, column) => ["code", "--goto", `${file}:${line}:${column}`],
  "code-insiders": (file, line, column) => ["code-insiders", "--goto", `${file}:${line}:${column}`],
  cursor: (file, line, column) => ["cursor", "--goto", `${file}:${line}:${column}`],
  windsurf: (file, line, column) => ["windsurf", "--goto", `${file}:${line}:${column}`],
  zed: (file, line, column) => ["zed", `${file}:${line}:${column}`],
  sublime: (file, line, column) => ["subl", `${file}:${line}:${column}`],
  webstorm: (file, line) => ["webstorm", "--line", String(line), file],
  idea: (file, line) => ["idea", "--line", String(line), file],
};

export function resolveEditorFile(root: string, file: string): string {
  if (!file || file.includes("\0") || /^https?:\/\//i.test(file))
    throw new Error("Invalid source file path.");
  const resolvedRoot = resolve(root);
  const resolvedFile = isAbsolute(file) ? resolve(file) : resolve(resolvedRoot, file);
  const rel = relative(resolvedRoot, resolvedFile);
  if (rel === "" || (!rel.startsWith("..") && !isAbsolute(rel))) return resolvedFile;
  throw new Error("Source file is outside the configured project root.");
}

export function buildEditorCommand(
  request: OpenEditorRequest,
  options: OpenEditorOptions,
): string[] {
  const file = resolveEditorFile(options.root, request.file);
  const line = Math.max(1, Math.floor(request.line ?? 1));
  const column = Math.max(1, Math.floor(request.column ?? 1));
  const editor =
    options.editor ??
    process.env.WRNEXUS_EDITOR ??
    process.env.VISUAL ??
    process.env.EDITOR ??
    "code";
  const factory = EDITORS[editor];
  if (!factory) throw new Error(`Unsupported editor: ${editor}`);
  return factory(file, line, column);
}

export function openInEditor(request: OpenEditorRequest, options: OpenEditorOptions): void {
  const command = buildEditorCommand(request, options);
  const spawn =
    options.spawn ??
    ((args: string[], spawnOptions?: { cwd?: string }) =>
      Bun.spawn(args, { cwd: spawnOptions?.cwd, stdout: "ignore", stderr: "ignore" }));
  spawn(command, { cwd: resolve(options.root) });
}
