"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RootConfigWatcher = void 0;
const chokidar_1 = __importDefault(require("chokidar"));
const promises_1 = require("node:fs/promises");
const configUtils_js_1 = require("./configUtils.js");
const node_events_1 = require("node:events");
const yaml_1 = require("yaml");
const watcherFallback_ts_1 = require("../utility/watcherFallback.js");
class RootConfigWatcher extends node_events_1.EventEmitter {
    #configFilePath;
    #watcher;
    #config;
    #usingPolling;
    #closed;
    #openCount = 0;
    ready;
    constructor() {
        super();
        this.#configFilePath = (0, configUtils_js_1.getConfigFilePath)();
        this.#usingPolling = false;
        this.#closed = false;
        this.ready = (0, node_events_1.once)(this, 'ready');
        this.#openWatcher();
    }
    #openWatcher() {
        this.#openCount++;
        this.#watcher = chokidar_1.default
            .watch(this.#configFilePath, {
            persistent: false,
            ...(this.#usingPolling ? watcherFallback_ts_1.POLLING_FALLBACK_OPTIONS : {}),
        })
            .on('add', this.handleChange.bind(this))
            .on('change', this.handleChange.bind(this))
            .on('error', this.handleError.bind(this));
    }
    // Test-only: simulate the underlying chokidar watcher emitting an error.
    // Exposed so the polling-fallback path can be exercised without triggering a
    // real ENOSPC/EMFILE on the host.
    _simulateWatcherErrorForTests(error) {
        this.handleError(error);
    }
    // Test-only: whether the watcher has fallen back to polling.
    get _usingPollingForTests() {
        return this.#usingPolling;
    }
    // Test-only: number of times the underlying watcher has been (re)opened.
    get _openCountForTests() {
        return this.#openCount;
    }
    handleError(error) {
        if ((0, watcherFallback_ts_1.isWatcherExhaustionError)(error)) {
            // Swallow every exhaustion error — chokidar can emit several before the
            // failed native watcher closes, and we don't want a flurry of ENOSPC to
            // surface to consumers in the middle of recovery.
            if (!this.#usingPolling) {
                (0, watcherFallback_ts_1.warnWatcherFallback)(this.#configFilePath);
                this.#usingPolling = true;
                // Guard against reopen-after-close: the caller may have invoked
                // close() while this teardown was in flight. The .catch is required
                // because `finally` would re-raise a teardown rejection as an
                // unhandled one.
                this.#watcher
                    .close()
                    .catch(() => {
                    // Teardown errors on an already-failed watcher are not actionable.
                })
                    .finally(() => {
                    if (!this.#closed)
                        this.#openWatcher();
                });
            }
            return;
        }
        this.emit('error', error);
    }
    handleChange() {
        (0, promises_1.readFile)(this.#configFilePath, 'utf-8')
            .then((data) => {
            if (!data)
                return;
            const config = (0, yaml_1.parse)(data);
            if (!this.#config) {
                this.#config = config;
                this.emit('ready', this.#config);
                return;
            }
            this.emit('change', (this.#config = config));
        })
            .catch((_error) => {
            // if yaml parse error ignore?
        });
    }
    close() {
        this.#closed = true;
        this.#watcher.close();
        this.#config = undefined;
        this.emit('close');
        this.removeAllListeners();
        return this;
    }
    get config() {
        return this.#config;
    }
}
exports.RootConfigWatcher = RootConfigWatcher;
//# sourceMappingURL=RootConfigWatcher.js.map