import { Logger, LogLevel, StopWatch } from "@itwin/core-bentley";
import { expect } from "chai";
import { ConcurrentQuery } from "../../ConcurrentQuery";
import { SnapshotDb } from "../../IModelDb";
import { _nativeDb } from "../../core-backend";
import { IModelTestUtils } from "../IModelTestUtils";
class LoadSimulator {
    db;
    scenario;
    constructor(db, scenario) {
        this.db = db;
        this.scenario = scenario;
    }
    async runQueryTask(reader) {
        try {
            while (await reader.step()) { }
            return { stats: reader.stats };
        }
        catch (err) {
            return { stats: reader.stats, error: err };
        }
    }
    async run() {
        ConcurrentQuery.shutdown(this.db[_nativeDb]);
        if (this.scenario.config) {
            const config = ConcurrentQuery.resetConfig(this.db[_nativeDb], this.scenario.config);
            // eslint-disable-next-line no-console
            console.log(config);
        }
        const overallTime = new StopWatch();
        overallTime.start();
        const results = [];
        for (let i = 0; i < this.scenario.totalBatches; ++i) {
            const promises = [];
            const readerTasks = Array(this.scenario.taskPerBatch).fill(undefined).map(() => this.scenario.createReader(this.db));
            readerTasks.forEach((reader) => {
                promises.push(this.runQueryTask(reader));
            });
            results.push(...await Promise.all(promises));
        }
        overallTime.stop();
        const errors = results.filter((x) => x.error !== undefined);
        const errorsMap = new Map();
        errors.forEach((x) => {
            if (x.error instanceof Error) {
                if (!errorsMap.has(x.error.message)) {
                    errorsMap.set(x.error.message, 1);
                }
                else {
                    errorsMap.set(x.error.message, errorsMap.get(x.error.message) + 1);
                }
            }
            else {
                if (!errorsMap.has("error")) {
                    errorsMap.set("error", 1);
                }
                else {
                    errorsMap.set("error", errorsMap.get("error") + 1);
                }
            }
        });
        const errorCount = errors.length;
        let backendCpuTime = BigInt(0);
        let backendTotalTime = BigInt(0);
        let backendMemUsed = BigInt(0);
        let backendRowsReturned = BigInt(0);
        let totalTime = BigInt(0);
        let retryCount = BigInt(0);
        let prepareTime = BigInt(0);
        // Calculate average
        results.forEach((r) => {
            backendCpuTime += BigInt(r.stats.backendCpuTime);
            backendTotalTime += BigInt(r.stats.backendTotalTime);
            backendMemUsed += BigInt(r.stats.backendMemUsed);
            backendRowsReturned += BigInt(r.stats.backendRowsReturned);
            totalTime += BigInt(r.stats.totalTime);
            retryCount += BigInt(r.stats.retryCount);
            prepareTime += BigInt(r.stats.prepareTime);
        });
        backendCpuTime /= BigInt(results.length);
        backendTotalTime /= BigInt(results.length);
        backendMemUsed /= BigInt(results.length);
        backendRowsReturned /= BigInt(results.length);
        totalTime /= BigInt(results.length);
        retryCount /= BigInt(results.length);
        // prepareTime /= BigInt(results.length);
        return {
            result: {
                backendCpuTime,
                backendTotalTime,
                backendMemUsed,
                backendRowsReturned,
                totalTime,
                retryCount,
                prepareTime,
            },
            overallTimeInSec: overallTime.currentSeconds,
            errorCount,
            totalQueries: results.length,
            errorMap: errorsMap
        };
    }
}
describe.skip("ConcurrentQueryLoad", () => {
    it("should run", async () => {
        Logger.initializeToConsole();
        Logger.setLevel("ECDb.ConcurrentQuery", LogLevel.Trace);
        const scenario = {
            name: "ConcurrentQueryLoad",
            config: {
                globalQuota: { time: 1, memory: 8388608 },
                workerThreads: 1,
            },
            totalBatches: 1,
            taskPerBatch: 1,
            createReader: (dbs) => {
                const queries = [
                    {
                        sql: `
            WITH sequence(n) AS (
              SELECT  1
              UNION ALL
              SELECT n + 1 FROM sequence WHERE n < 10000000
            )
            SELECT  COUNT(*)
            FROM bis.SpatialIndex i, sequence s
            WHERE i.ECInstanceId MATCH  iModel_spatial_overlap_aabb(
              iModel_bbox(random(), random(), random(), random(),random(), random()))`
                    },
                    {
                        sql: `
            WITH sequence(n) AS (
              SELECT  1
              UNION ALL
              SELECT n + 1 FROM sequence WHERE n < 10000000
            )
            SELECT  COUNT(*) FROM sequence`
                    },
                    {
                        sql: "SELECT $ FROM bis.Element LIMIT 10000"
                    }
                ];
                const idx = Math.floor(Math.random() * queries.length);
                return dbs.createQueryReader(queries[idx].sql);
            }
        };
        const verySmallFile = IModelTestUtils.resolveAssetFile("test.bim");
        const db = SnapshotDb.openFile(verySmallFile);
        const simulator = new LoadSimulator(db, scenario);
        const result = await simulator.run();
        // eslint-disable-next-line no-console
        console.log(result);
        db.close();
        expect(result.errorCount).to.be.equal(0);
    });
});
//# sourceMappingURL=ConcurrentQueryLoad.test.js.map