import { LogContext } from '@rocicorp/logger';
import { astToZQL } from "../../ast-to-zql/src/ast-to-zql.js";
import { formatOutput } from "../../ast-to-zql/src/format.js";
import { assert } from "../../shared/src/asserts.js";
import { must } from "../../shared/src/must.js";
import { sleep } from "../../shared/src/sleep.js";
import { transformAndHashQuery } from "../../zero-cache/src/auth/read-authorizer.js";
import { computeZqlSpecs } from "../../zero-cache/src/db/lite-tables.js";
import { hydrate } from "../../zero-cache/src/services/view-syncer/pipeline-driver.js";
import { mapAST } from "../../zero-protocol/src/ast.js";
import { hashOfAST } from "../../zero-protocol/src/query-hash.js";
import { buildPipeline, } from "../../zql/src/builder/builder.js";
export async function runAst(lc, ast, isTransformed, options) {
    const { clientToServerMapper, permissions, host, db } = options;
    const result = {
        warnings: [],
        syncedRows: undefined,
        syncedRowCount: 0,
        start: 0,
        end: 0,
        afterPermissions: undefined,
        readRows: undefined,
        readRowCountsByQuery: {},
        readRowCount: undefined,
    };
    if (!isTransformed) {
        // map the AST to server names if not already transformed
        ast = mapAST(ast, must(clientToServerMapper));
    }
    if (options.applyPermissions) {
        const authData = options.authData ? JSON.parse(options.authData) : {};
        if (!options.authData) {
            result.warnings.push('No auth data provided. Permission rules will compare to `NULL` wherever an auth data field is referenced.');
        }
        ast = transformAndHashQuery(lc, 'clientGroupIDForAnalyze', ast, must(permissions), authData, false).transformedAst;
        result.afterPermissions = await formatOutput(ast.table + astToZQL(ast));
    }
    const tableSpecs = computeZqlSpecs(lc, db);
    const pipeline = buildPipeline(ast, host, 'query-id');
    const start = performance.now();
    let syncedRowCount = 0;
    const rowsByTable = {};
    const seenByTable = new Set();
    for (const rowChange of hydrate(pipeline, hashOfAST(ast), tableSpecs)) {
        assert(rowChange.type === 'add');
        // yield to other tasks to avoid blocking for too long
        if (syncedRowCount % 10 === 0) {
            await Promise.resolve();
        }
        if (syncedRowCount % 100 === 0) {
            await sleep(1);
        }
        let rows = rowsByTable[rowChange.table];
        const s = rowChange.table + '.' + JSON.stringify(rowChange.row);
        if (seenByTable.has(s)) {
            continue; // skip duplicates
        }
        syncedRowCount++;
        seenByTable.add(s);
        if (options.syncedRows) {
            if (!rows) {
                rows = [];
                rowsByTable[rowChange.table] = rows;
            }
            rows.push(rowChange.row);
        }
    }
    const end = performance.now();
    if (options.syncedRows) {
        result.syncedRows = rowsByTable;
    }
    result.start = start;
    result.end = end;
    // Always include the count of synced and vended rows.
    result.syncedRowCount = syncedRowCount;
    result.readRowCountsByQuery = host.debug?.getVendedRowCounts() ?? {};
    let readRowCount = 0;
    for (const c of Object.values(result.readRowCountsByQuery)) {
        for (const v of Object.values(c)) {
            readRowCount += v;
        }
    }
    result.readRowCount = readRowCount;
    if (options.vendedRows) {
        result.readRows = host.debug?.getVendedRows();
    }
    return result;
}
//# sourceMappingURL=run-ast.js.map