const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
const { threadId } = require("worker_threads");

const TRANSIENT_CODES = new Set(["EACCES", "EBUSY", "EEXIST", "ENOTEMPTY", "EPERM"]);
const activeWrites = new Set();
const diagnostics = [];
const sleepArray = new Int32Array(new SharedArrayBuffer(4));

function writeJsonAtomicSync(target, value, options = {}) {
  return writeTextAtomicSync(target, `${JSON.stringify(value, null, options.indent ?? 2)}\n`, options);
}

function writeTextAtomicSync(target, content, options = {}) {
  if (!String(target || "").trim()) throw new Error("A target file is required.");
  const file = path.resolve(String(target));
  if (activeWrites.has(file)) {
    const error = new Error(`A write to ${file} is already in progress.`);
    error.code = "EWRITELOCKED";
    recordDiagnostic(file, "write_locked", error, 0, "error", options);
    throw error;
  }

  activeWrites.add(file);
  fs.mkdirSync(path.dirname(file), { recursive: true });
  cleanupStaleTempFilesSync(file, options);
  const temporary = temporaryPath(file);
  let descriptor = null;
  try {
    descriptor = fs.openSync(temporary, "wx", options.mode ?? 0o600);
    fs.writeFileSync(descriptor, content, options.encoding || "utf8");
    try { fs.fsyncSync(descriptor); } catch (error) {
      recordDiagnostic(file, "fsync_unavailable", error, 0, "warning", options);
    }
    fs.closeSync(descriptor);
    descriptor = null;
    replaceFileSync(temporary, file, options);
    if (options.mode != null) {
      try { fs.chmodSync(file, options.mode); } catch {}
    }
    syncParentDirectory(file, options);
    return file;
  } catch (error) {
    recordDiagnostic(file, "write_failed", error, 0, "error", options);
    throw error;
  } finally {
    if (descriptor != null) {
      try { fs.closeSync(descriptor); } catch {}
    }
    try { fs.rmSync(temporary, { force: true }); } catch {}
    activeWrites.delete(file);
  }
}

function replaceFileSync(temporary, target, options = {}) {
  try {
    retrySync(() => fs.renameSync(temporary, target), target, "rename", options);
    return;
  } catch (renameError) {
    if (!fs.existsSync(target) || !TRANSIENT_CODES.has(renameError.code)) throw renameError;
  }

  const backup = `${target}.${process.pid}.${threadId}.${crypto.randomBytes(5).toString("hex")}.bak`;
  let movedOriginal = false;
  try {
    retrySync(() => fs.renameSync(target, backup), target, "backup_existing", options);
    movedOriginal = true;
    retrySync(() => fs.renameSync(temporary, target), target, "install_replacement", options);
    fs.rmSync(backup, { force: true });
    recordDiagnostic(target, "backup_replace_succeeded", null, 0, "warning", options);
  } catch (error) {
    if (movedOriginal && fs.existsSync(backup) && !fs.existsSync(target)) {
      try {
        retrySync(() => fs.renameSync(backup, target), target, "restore_original", options);
      } catch (restoreError) {
        recordDiagnostic(target, "restore_failed", restoreError, 0, "error", options);
        error.message = `${error.message} The original file remains at ${backup}.`;
      }
    }
    throw error;
  } finally {
    if (fs.existsSync(backup) && fs.existsSync(target)) {
      try { fs.rmSync(backup, { force: true }); } catch {}
    }
  }
}

function retrySync(operation, target, operationName, options = {}) {
  const attempts = Math.max(1, Number(options.attempts) || 8);
  const delayMs = Math.max(1, Number(options.delayMs) || 15);
  let lastError = null;
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
    try {
      return operation();
    } catch (error) {
      lastError = error;
      if (!TRANSIENT_CODES.has(error.code) || attempt === attempts) throw error;
      recordDiagnostic(target, operationName, error, attempt, "warning", options);
      Atomics.wait(sleepArray, 0, 0, Math.min(250, delayMs * attempt));
    }
  }
  throw lastError;
}

function runFileOperationWithRetries(operation, options = {}) {
  if (typeof operation !== "function") throw new Error("A file operation function is required.");
  return retrySync(
    operation,
    options.target || "file operation",
    options.operation || "file_operation",
    options
  );
}

function cleanupStaleTempFilesSync(target, options = {}) {
  const directory = path.dirname(target);
  const basename = path.basename(target);
  const maxAgeMs = Math.max(1000, Number(options.staleTempAgeMs) || 60 * 60 * 1000);
  const now = Date.now();
  let names = [];
  try { names = fs.readdirSync(directory); } catch { return; }
  for (const name of names) {
    if (name !== `${basename}.tmp` && !(name.startsWith(`${basename}.`) && name.endsWith(".tmp"))) continue;
    const candidate = path.join(directory, name);
    try {
      if (now - fs.statSync(candidate).mtimeMs < maxAgeMs) continue;
      fs.rmSync(candidate, { force: true });
      recordDiagnostic(target, "stale_temp_removed", null, 0, "warning", options);
    } catch {}
  }
}

function temporaryPath(target) {
  return `${target}.${process.pid}.${threadId}.${crypto.randomBytes(7).toString("hex")}.tmp`;
}

function syncParentDirectory(target, options) {
  let descriptor = null;
  try {
    descriptor = fs.openSync(path.dirname(target), "r");
    fs.fsyncSync(descriptor);
  } catch (error) {
    if (process.platform !== "win32") {
      recordDiagnostic(target, "directory_fsync_unavailable", error, 0, "warning", options);
    }
  } finally {
    if (descriptor != null) try { fs.closeSync(descriptor); } catch {}
  }
}

function recordDiagnostic(target, operation, error, attempt, level, options = {}) {
  const entry = {
    timestamp: new Date().toISOString(),
    level,
    operation,
    target,
    attempt,
    code: error?.code || null,
    message: error?.message || null
  };
  diagnostics.push(entry);
  if (diagnostics.length > 100) diagnostics.splice(0, diagnostics.length - 100);
  if (typeof options.onDiagnostic === "function") options.onDiagnostic(entry);
}

function getSafeFileDiagnostics() {
  return diagnostics.map((entry) => ({ ...entry }));
}

function clearSafeFileDiagnostics() {
  diagnostics.length = 0;
}

module.exports = {
  TRANSIENT_CODES,
  writeJsonAtomicSync,
  writeTextAtomicSync,
  cleanupStaleTempFilesSync,
  runFileOperationWithRetries,
  getSafeFileDiagnostics,
  clearSafeFileDiagnostics
};
