"use strict";
/* Socket Lib - Built with rolldown */
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
const require_runtime = require('../_virtual/_rolldown/runtime.js');
const require_primordials_error = require('../primordials/error.js');
const require_node_fs = require('../node/fs.js');
const require_primordials_json = require('../primordials/json.js');
const require_errors_predicates = require('../errors/predicates.js');
const require_json_format = require('./format.js');
let node_process = require("node:process");
node_process = require_runtime.__toESM(node_process);
let node_timers_promises = require("node:timers/promises");

//#region src/json/edit.ts
/**
* @file Editable JSON file manipulation with formatting preservation.
*/
const identSymbol = require_json_format.INDENT_SYMBOL;
const newlineSymbol = require_json_format.NEWLINE_SYMBOL;
let _EditableJsonClass;
/**
* Get the EditableJson class for JSON file manipulation.
*
* @example
*   ;```ts
*   import { getEditableJsonClass } from '@socketsecurity/lib/json/edit'
*
*   const EditableJson = getEditableJsonClass<MyConfigType>()
*   const config = await EditableJson.load('./config.json')
*   config.update({ someField: 'newValue' })
*   await config.save({ sort: true })
*   ```
*/
/*@__NO_SIDE_EFFECTS__*/
function getEditableJsonClass() {
	if (_EditableJsonClass === void 0) _EditableJsonClass = class EditableJson {
		_canSave = true;
		_content = {};
		_path = void 0;
		_readFileContent = "";
		_readFileJson = void 0;
		get content() {
			return this._content;
		}
		get filename() {
			const path = this._path;
			if (!path) return "";
			return path;
		}
		get path() {
			return this._path;
		}
		static async create(path, opts = { __proto__: null }) {
			const instance = new EditableJson();
			instance.create(path);
			return opts.data ? instance.update(opts.data) : instance;
		}
		static async load(path, opts = { __proto__: null }) {
			const instance = new EditableJson();
			if (!opts.create) return await instance.load(path);
			try {
				return await instance.load(path);
			} catch (err) {
				if (!require_errors_predicates.isErrnoException(err) || err.code !== "ENOENT") throw err;
				return instance.create(path);
			}
			/* c8 ignore stop */
		}
		create(path) {
			this._path = path;
			this._content = {};
			this._canSave = true;
			return this;
		}
		fromContent(data) {
			this._content = data;
			this._canSave = false;
			return this;
		}
		fromJSON(data) {
			const parsed = require_primordials_json.JSONParse(data);
			const indent = require_json_format.detectIndent(data);
			const newline = require_json_format.detectNewline(data);
			parsed[identSymbol] = indent;
			parsed[newlineSymbol] = newline;
			this._content = parsed;
			return this;
		}
		async load(path) {
			this._path = path;
			this._readFileContent = await readFile(this.filename);
			this.fromJSON(this._readFileContent);
			this._readFileJson = require_primordials_json.JSONParse(this._readFileContent);
			return this;
		}
		async save(options) {
			if (!this._canSave || this.content === void 0) throw new require_primordials_error.ErrorCtor("No file path to save to");
			if (!require_json_format.shouldSave(this.content, this._readFileJson, this._readFileContent, options)) return false;
			const content = require_json_format.stripFormattingSymbols(this.content);
			const fileContent = require_json_format.stringifyWithFormatting(options?.sort ? require_json_format.sortKeys(content) : content, require_json_format.getFormattingFromContent(this.content));
			await retryWrite(this.filename, fileContent);
			this._readFileContent = fileContent;
			this._readFileJson = require_primordials_json.JSONParse(fileContent);
			return true;
		}
		saveSync(options) {
			if (!this._canSave || this.content === void 0) throw new require_primordials_error.ErrorCtor("No file path to save to");
			if (!require_json_format.shouldSave(this.content, this._readFileJson, this._readFileContent, options)) return false;
			const content = require_json_format.stripFormattingSymbols(this.content);
			const fileContent = require_json_format.stringifyWithFormatting(options?.sort ? require_json_format.sortKeys(content) : content, require_json_format.getFormattingFromContent(this.content));
			(/* @__PURE__ */ require_node_fs.getNodeFs()).writeFileSync(this.filename, fileContent);
			this._readFileContent = fileContent;
			this._readFileJson = require_primordials_json.JSONParse(fileContent);
			return true;
		}
		update(content) {
			this._content = {
				...this._content,
				...content
			};
			return this;
		}
		willSave(options) {
			if (!this._canSave || this.content === void 0) return false;
			return require_json_format.shouldSave(this.content, this._readFileJson, this._readFileContent, options);
		}
	};
	return _EditableJsonClass;
}
/**
* Read file content from disk with retry logic for ENOENT errors.
*
* @private
*/
async function readFile(filepath) {
	const { promises: fsPromises } = /* @__PURE__ */ require_node_fs.getNodeFs();
	/* c8 ignore start */
	const maxRetries = node_process.default.platform === "win32" ? 5 : 1;
	for (let attempt = 0; attempt <= maxRetries; attempt++) try {
		return await fsPromises.readFile(filepath, "utf8");
	} catch (e) {
		const isLastAttempt = attempt === maxRetries;
		if (!(require_errors_predicates.isErrnoException(e) && e.code === "ENOENT") || isLastAttempt) throw e;
		await (0, node_timers_promises.setTimeout)(node_process.default.platform === "win32" ? 50 * (attempt + 1) : 20);
	}
	/* c8 ignore stop */
	/* c8 ignore next 3 - Loop has 'return' on success and 'throw' in
	each iteration; only reachable if maxRetries is somehow negative. */
	throw new require_primordials_error.ErrorCtor(`readFile: exhausted ${maxRetries + 1} attempts reading ${filepath}`);
}
/**
* Retry a file write operation with exponential backoff on Windows EPERM
* errors. Windows can have transient file locking issues with temp
* directories.
*
* @private
*/
async function retryWrite(filepath, content, retries = 3, baseDelay = 10) {
	const fs = /* @__PURE__ */ require_node_fs.getNodeFs();
	const { promises: fsPromises } = fs;
	for (let attempt = 0; attempt <= retries; attempt++) try {
		await fsPromises.writeFile(filepath, content);
		/* c8 ignore start - Windows-only flush+verify loop. Tested on
		Windows runners. */
		if (node_process.default.platform === "win32") {
			await (0, node_timers_promises.setTimeout)(50);
			let accessRetries = 0;
			const maxAccessRetries = 5;
			while (accessRetries < maxAccessRetries) {
				if (fs.existsSync(filepath)) {
					await (0, node_timers_promises.setTimeout)(10);
					break;
				}
				await (0, node_timers_promises.setTimeout)(20 * (accessRetries + 1));
				accessRetries++;
			}
		}
		/* c8 ignore stop */
		return;
	} catch (e) {
		const isLastAttempt = attempt === retries;
		if (!(require_errors_predicates.isErrnoException(e) && (e.code === "EPERM" || e.code === "EBUSY" || e.code === "ENOENT")) || isLastAttempt) throw e;
		await (0, node_timers_promises.setTimeout)(baseDelay * 2 ** attempt);
	}
}

//#endregion
exports.getEditableJsonClass = getEditableJsonClass;
exports.readFile = readFile;
exports.retryWrite = retryWrite;