var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
    if (value !== null && value !== void 0) {
        if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
        var dispose, inner;
        if (async) {
            if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
            dispose = value[Symbol.asyncDispose];
        }
        if (dispose === void 0) {
            if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
            dispose = value[Symbol.dispose];
            if (async) inner = dispose;
        }
        if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
        if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
        env.stack.push({ value: value, dispose: dispose, async: async });
    }
    else if (async) {
        env.stack.push({ async: true });
    }
    return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
    return function (env) {
        function fail(e) {
            env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
            env.hasError = true;
        }
        var r, s = 0;
        function next() {
            while (r = env.stack.pop()) {
                try {
                    if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
                    if (r.dispose) {
                        var result = r.dispose.call(r.value);
                        if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
                    }
                    else s |= 1;
                }
                catch (e) {
                    fail(e);
                }
            }
            if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
            if (env.hasError) throw env.error;
        }
        return next();
    };
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
    var e = new Error(message);
    return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
import auth from 'basic-auth';
import * as valita from "../../../shared/src/valita.js";
import { astSchema } from "../../../zero-protocol/src/ast.js";
import { Debug } from "../../../zql/src/builder/debug-delegate.js";
import { MemoryStorage } from "../../../zql/src/ivm/memory-storage.js";
import { Database } from "../../../zqlite/src/db.js";
import { explainQueries } from "../../../zqlite/src/explain-queries.js";
import { TableSource } from "../../../zqlite/src/table-source.js";
import { isAdminPasswordValid } from "../config/zero-config.js";
import { computeZqlSpecs, mustGetTableSpec } from "../db/lite-tables.js";
import { runAst } from "./run-ast.js";
export function setCors(res) {
    return res
        .header('Access-Control-Allow-Origin', '*')
        .header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
        .header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
        .header('Access-Control-Allow-Credentials', 'true');
}
export async function handleAnalyzeQueryRequest(lc, config, req, res) {
    const credentials = auth(req);
    void setCors(res);
    if (!isAdminPasswordValid(lc, config, credentials?.pass)) {
        await res
            .code(401)
            .header('WWW-Authenticate', 'Basic realm="analyze query Protected Area"')
            .send({ unauthorized: true });
        return;
    }
    const ast = valita.parse(req.body, astSchema);
    const result = await analyzeQuery(lc, config, ast);
    await res.send(result);
}
export async function analyzeQuery(lc, config, ast, options = {}) {
    const env_1 = { stack: [], error: void 0, hasError: false };
    try {
        const db = __addDisposableResource(env_1, new Database(lc, config.replica.file), false);
        const fullTables = new Map();
        const tableSpecs = new Map();
        const tables = new Map();
        computeZqlSpecs(lc, db, tableSpecs, fullTables);
        const { syncedRows = true, vendedRows = false } = options;
        const result = await runAst(lc, ast, true, {
            applyPermissions: false,
            syncedRows,
            vendedRows,
            db,
            tableSpecs,
            host: {
                debug: new Debug(),
                getSource(tableName) {
                    let source = tables.get(tableName);
                    if (source) {
                        return source;
                    }
                    const tableSpec = mustGetTableSpec(tableSpecs, tableName);
                    const { primaryKey } = tableSpec.tableSpec;
                    source = new TableSource(lc, config.log, db, tableName, tableSpec.zqlSpec, primaryKey);
                    tables.set(tableName, source);
                    return source;
                },
                createStorage() {
                    return new MemoryStorage();
                },
                decorateSourceInput: input => input,
                decorateInput: input => input,
                addEdge() { },
                decorateFilterInput: input => input,
            },
        });
        result.plans = explainQueries(result.readRowCountsByQuery ?? {}, db);
        return result;
    }
    catch (e_1) {
        env_1.error = e_1;
        env_1.hasError = true;
    }
    finally {
        __disposeResources(env_1);
    }
}
//# sourceMappingURL=analyze.js.map