"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Database = void 0;
const node_fs_1 = __importDefault(require("node:fs"));
const logger_1 = require("../utils/logger");
const model_1 = require("./model");
const NS = "zh:controller:database";
class Database {
    _id;
    entries;
    path;
    maxId;
    get id() {
        return this._id;
    }
    constructor(entries, path, maxId) {
        this._id = this.generateDatabaseID();
        this.entries = entries;
        this.path = path;
        this.maxId = maxId;
    }
    generateDatabaseID() {
        for (let i = 1; i < 100000; i++) {
            if (!model_1.Entity.databaseIDExists(i)) {
                return i;
            }
        }
        return 100001;
    }
    static open(path) {
        const entries = new Map();
        let maxId = 0;
        if (node_fs_1.default.existsSync(path)) {
            const file = node_fs_1.default.readFileSync(path, "utf-8");
            for (const row of file.split("\n")) {
                if (!row) {
                    continue;
                }
                try {
                    const json = JSON.parse(row);
                    if (Number.isFinite(json.id)) {
                        entries.set(json.id, json);
                        if (json.id > maxId) {
                            maxId = json.id;
                        }
                    }
                }
                catch (error) {
                    logger_1.logger.error(`Corrupted database line, ignoring. ${error}`, NS);
                }
            }
        }
        return new Database(entries, path, maxId);
    }
    *getEntriesIterator(type) {
        for (const entry of this.entries.values()) {
            if (type.includes(entry.type)) {
                yield entry;
            }
        }
    }
    insert(databaseEntry) {
        if (this.entries.has(databaseEntry.id)) {
            throw new Error(`DatabaseEntry with ID '${databaseEntry.id}' already exists`);
        }
        this.entries.set(databaseEntry.id, databaseEntry);
        this.write();
    }
    update(databaseEntry, write) {
        if (!this.entries.has(databaseEntry.id)) {
            throw new Error(`DatabaseEntry with ID '${databaseEntry.id}' does not exist`);
        }
        this.entries.set(databaseEntry.id, databaseEntry);
        if (write) {
            this.write();
        }
    }
    remove(id) {
        if (this.entries.delete(id)) {
            this.write();
        }
        else {
            throw new Error(`DatabaseEntry with ID '${id}' does not exist`);
        }
    }
    has(id) {
        return this.entries.has(id);
    }
    newID() {
        this.maxId += 1;
        return this.maxId;
    }
    write() {
        logger_1.logger.debug(`Writing database to '${this.path}'`, NS);
        let lines = "";
        for (const entry of this.entries.values()) {
            lines += `${JSON.stringify(entry)}\n`;
        }
        const tmpPath = `${this.path}.tmp`;
        try {
            // If there already exsits a database.db.tmp, rename it to database.db.tmp.<now>
            const dateTmpPath = `${tmpPath}.${new Date().toISOString().replaceAll(":", "-")}`;
            node_fs_1.default.renameSync(tmpPath, dateTmpPath);
            // If we got this far, we succeeded! Warn the user about this
            logger_1.logger.warning(`Found '${tmpPath}' when writing database, indicating past write failure; renamed it to '${dateTmpPath}'`, NS);
        }
        catch {
            // Nothing to catch; if the renameSync fails, we ignore that exception
        }
        const fd = node_fs_1.default.openSync(tmpPath, "w");
        node_fs_1.default.writeFileSync(fd, lines.slice(0, -1)); // remove last newline, no effect if empty string
        // Ensure file is on disk https://github.com/Koenkk/zigbee2mqtt/issues/11759
        node_fs_1.default.fsyncSync(fd);
        node_fs_1.default.closeSync(fd);
        node_fs_1.default.renameSync(tmpPath, this.path);
    }
    clear() {
        logger_1.logger.debug(`Clearing database '${this.path}'...`, NS);
        this.entries.clear();
        this.write();
    }
}
exports.Database = Database;
exports.default = Database;
//# sourceMappingURL=database.js.map