"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SqliteClient = void 0;
const path_1 = __importDefault(require("path"));
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
const debug_1 = __importDefault(require("debug"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const nested_error_stacks_1 = __importDefault(require("nested-error-stacks"));
const common_utils_1 = require("./common-utils");
const constants_1 = require("./constants");
const common_1 = require("./db-utils/common");
const db_1 = require("./test-adapter/transformers/db");
const debug = (0, debug_1.default)('html-reporter:sqlite-client');
class SqliteClient {
    static async create(options) {
        const { htmlReporter, reportPath } = options;
        const dbPath = path_1.default.resolve(reportPath, constants_1.LOCAL_DATABASE_NAME);
        const dbUrlsJsonPath = path_1.default.resolve(reportPath, constants_1.DATABASE_URLS_JSON_NAME);
        let db;
        try {
            if (!options.reuse) {
                await Promise.all([
                    fs_extra_1.default.remove(dbPath),
                    fs_extra_1.default.remove(dbUrlsJsonPath)
                ]);
            }
            await fs_extra_1.default.ensureDir(reportPath);
            db = new better_sqlite3_1.default(dbPath);
            debug('db connection opened');
            (0, common_1.createTablesQuery)().forEach((query) => db?.prepare(query).run());
        }
        catch (err) { // eslint-disable-line @typescript-eslint/no-explicit-any
            throw new nested_error_stacks_1.default(`Error creating database at "${dbPath}"`, err);
        }
        const transformer = new db_1.DbTestResultTransformer({ baseHost: htmlReporter.config.baseHost });
        return new this(db, transformer);
    }
    constructor(db, transformer) {
        this._db = db;
        this._queryCache = new Map();
        this._transformer = transformer;
    }
    getRawConnection() {
        return this._db;
    }
    close() {
        this._db.prepare('VACUUM').run();
        debug('db connection closed');
        this._db.close();
    }
    query(queryParams = {}, ...queryArgs) {
        const { select, where, orderBy, orderDescending, limit, noCache = false } = queryParams;
        const cacheKey = (!noCache && (0, common_utils_1.getShortMD5)(`${select}#${where}#${orderBy}${orderDescending}${queryArgs.join('#')}`));
        if (!noCache && this._queryCache.has(cacheKey)) {
            return this._queryCache.get(cacheKey);
        }
        const sentence = `SELECT ${select || '*'} FROM ${constants_1.DB_SUITES_TABLE_NAME}`
            + this._createSentence({ where, orderBy, orderDescending, limit });
        const result = this._db.prepare(sentence).get(...queryArgs);
        if (!noCache) {
            this._queryCache.set(cacheKey, result);
        }
        return result;
    }
    write(testResult) {
        const dbTestResult = this._transformer.transform(testResult);
        const values = this._createValuesArray(dbTestResult);
        const placeholders = values.map(() => '?').join(', ');
        this._db.prepare(`INSERT INTO ${constants_1.DB_SUITES_TABLE_NAME} VALUES (${placeholders})`).run(...values);
    }
    delete(deleteParams = {}, ...deleteArgs) {
        const sentence = `DELETE FROM ${constants_1.DB_SUITES_TABLE_NAME}`
            + this._createSentence(deleteParams);
        this._db.prepare(sentence).run(...deleteArgs);
    }
    _createSentence(params) {
        let sentence = '';
        if (params.where) {
            sentence += ` WHERE ${params.where}`;
        }
        if (params.orderBy) {
            sentence += ` ORDER BY ${params.orderBy} ${params.orderDescending ? 'DESC' : 'ASC'}`;
        }
        if (params.limit) {
            sentence += ` LIMIT ${params.limit}`;
        }
        return sentence;
    }
    _createValuesArray(testResult) {
        return constants_1.SUITES_TABLE_COLUMNS.reduce((acc, { name }) => {
            const value = testResult[name];
            if (value === undefined || value === null) {
                acc.push(null);
                return acc;
            }
            switch (value.constructor) {
                case Array:
                case Object:
                    acc.push(JSON.stringify(value));
                    break;
                case Boolean:
                    acc.push(value ? 1 : 0);
                    break;
                default:
                    acc.push(value);
            }
            return acc;
        }, []);
    }
}
exports.SqliteClient = SqliteClient;
//# sourceMappingURL=sqlite-client.js.map