"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SqlitePackageCache = void 0;
const tslib_1 = require("tslib");
const node_util_1 = require("node:util");
const node_zlib_1 = tslib_1.__importStar(require("node:zlib"));
const better_sqlite3_1 = tslib_1.__importDefault(require("better-sqlite3"));
const fs_extra_1 = require("fs-extra");
const upath = tslib_1.__importStar(require("upath"));
const logger_1 = require("../../../logger");
const fs_1 = require("../../fs");
const brotliCompress = (0, node_util_1.promisify)(node_zlib_1.default.brotliCompress);
const brotliDecompress = (0, node_util_1.promisify)(node_zlib_1.default.brotliDecompress);
function compress(input) {
    const jsonStr = JSON.stringify(input);
    return brotliCompress(jsonStr, {
        params: {
            [node_zlib_1.constants.BROTLI_PARAM_MODE]: node_zlib_1.constants.BROTLI_MODE_TEXT,
            [node_zlib_1.constants.BROTLI_PARAM_QUALITY]: 3,
        },
    });
}
async function decompress(input) {
    const buf = await brotliDecompress(input);
    const jsonStr = buf.toString('utf8');
    return JSON.parse(jsonStr);
}
class SqlitePackageCache {
    client;
    upsertStatement;
    getStatement;
    deleteExpiredRows;
    countStatement;
    static async init(cacheDir) {
        const sqliteDir = upath.join(cacheDir, 'renovate/renovate-cache-sqlite');
        await (0, fs_1.ensureDir)(sqliteDir);
        const sqliteFile = upath.join(sqliteDir, 'db.sqlite');
        if (await (0, fs_extra_1.exists)(sqliteFile)) {
            logger_1.logger.debug(`Using SQLite package cache: ${sqliteFile}`);
        }
        else {
            logger_1.logger.debug(`Creating SQLite package cache: ${sqliteFile}`);
        }
        const client = new better_sqlite3_1.default(sqliteFile);
        const res = new SqlitePackageCache(client);
        return res;
    }
    constructor(client) {
        this.client = client;
        client.pragma('journal_mode = WAL');
        client.pragma("encoding = 'UTF-8'");
        client
            .prepare(`
          CREATE TABLE IF NOT EXISTS package_cache (
            namespace TEXT NOT NULL,
            key TEXT NOT NULL,
            expiry INTEGER NOT NULL,
            data BLOB NOT NULL,
            PRIMARY KEY (namespace, key)
          )
        `)
            .run();
        client
            .prepare('CREATE INDEX IF NOT EXISTS expiry ON package_cache (expiry)')
            .run();
        client
            .prepare('CREATE INDEX IF NOT EXISTS namespace_key ON package_cache (namespace, key)')
            .run();
        this.upsertStatement = client.prepare(`
      INSERT INTO package_cache (namespace, key, data, expiry)
      VALUES (@namespace, @key, @data, unixepoch() + @ttlSeconds)
      ON CONFLICT (namespace, key) DO UPDATE SET
        data = @data,
        expiry = unixepoch() + @ttlSeconds
    `);
        this.getStatement = client
            .prepare(`
          SELECT data FROM package_cache
          WHERE
            namespace = @namespace AND key = @key AND expiry > unixepoch()
        `)
            .pluck(true);
        this.deleteExpiredRows = client.prepare(`
      DELETE FROM package_cache
      WHERE expiry <= unixepoch()
    `);
        this.countStatement = client
            .prepare('SELECT COUNT(*) FROM package_cache')
            .pluck(true);
    }
    async set(namespace, key, value, ttlMinutes = 5) {
        const data = await compress(value);
        const ttlSeconds = ttlMinutes * 60;
        this.upsertStatement.run({ namespace, key, data, ttlSeconds });
        return Promise.resolve();
    }
    async get(namespace, key) {
        const data = this.getStatement.get({ namespace, key });
        if (!data) {
            return undefined;
        }
        return await decompress(data);
    }
    cleanupExpired() {
        const start = Date.now();
        const totalCount = this.countStatement.get();
        const { changes: deletedCount } = this.deleteExpiredRows.run();
        const finish = Date.now();
        const durationMs = finish - start;
        logger_1.logger.debug(`SQLite package cache: deleted ${deletedCount} of ${totalCount} entries in ${durationMs}ms`);
    }
    cleanup() {
        this.cleanupExpired();
        this.client.close();
        return Promise.resolve();
    }
}
exports.SqlitePackageCache = SqlitePackageCache;
//# sourceMappingURL=sqlite.js.map