import * as path from 'path';
import type { FileOperations } from './operations/types.js';

const queues = new Map<string, Promise<void>>();

async function queueKey(filePath: string, fileOps: FileOperations): Promise<string> {
  const resolved = path.resolve(filePath);
  try {
    return await fileOps.realpath(resolved);
  } catch {
    return resolved;
  }
}

function enqueue<T>(key: string, fn: () => Promise<T>): Promise<T> {
  const current = queues.get(key) ?? Promise.resolve();

  let release!: () => void;
  const next = new Promise<void>(resolve => {
    release = resolve;
  });
  const chained = current.then(() => next);
  queues.set(key, chained);

  return current.then(async () => {
    try {
      return await fn();
    } finally {
      release();
      if (queues.get(key) === chained) {
        queues.delete(key);
      }
    }
  });
}

export async function withFileMutationQueueOps<T>(
  filePath: string,
  fileOps: FileOperations,
  fn: () => Promise<T>
): Promise<T> {
  const key = await queueKey(filePath, fileOps);
  return enqueue(key, fn);
}
