import { prepareEngineDatabase } from "../database/init-db.js";
import { createHooks } from "../hooks/create-hooks.js";
import { applyFilesystemSchema } from "../filesystem/schema.js";
import { loadPluginFromString } from "../environment/load-from-string.js";
import { createExecuteSync } from "./execute-sync.js";
import { internalQueryBuilder } from "./internal-query-builder.js";
import { setDeterministicBoot } from "./deterministic-mode/is-deterministic-mode.js";
import { createFunctionRegistry, } from "./functions/function-registry.js";
import { registerBuiltinFunctions } from "./functions/register-builtins.js";
import { createPreprocessor } from "./preprocessor/create-preprocessor.js";
export async function boot(env) {
    const deterministicBoot = env.args.keyValues?.some((kv) => kv.key === "lix_deterministic_mode" &&
        kv.value !== undefined &&
        typeof kv.value === "object" &&
        kv.value?.enabled == true);
    const hooks = createHooks();
    const runtimeCacheRef = {};
    const engine = {
        sqlite: env.sqlite,
        hooks,
        getAllPluginsSync: () => plugins,
        runtimeCacheRef,
        preprocessQuery: () => {
            throw new Error("preprocessQuery() is not yet initialised");
        },
        executeSync: () => {
            throw new Error("executeSync() is not yet initialised");
        },
        call: async () => {
            throw new Error("call() is not yet initialised");
        },
        registerFunction: () => {
            throw new Error("registerFunction() is not yet initialised");
        },
        listFunctions: () => {
            throw new Error("listFunctions() is not yet initialised");
        },
    };
    const fnRegistry = createFunctionRegistry({
        engine,
    });
    engine.registerFunction = fnRegistry.register;
    engine.listFunctions = fnRegistry.list;
    engine.call = fnRegistry.call;
    if (deterministicBoot) {
        setDeterministicBoot({ runtimeCacheRef, value: true });
    }
    engine.preprocessQuery = createPreprocessor({ engine });
    engine.executeSync = createExecuteSync({ engine });
    prepareEngineDatabase({ engine });
    const plugins = [];
    for (const code of env.args.providePluginsRaw ?? []) {
        const plugin = await loadPluginFromString(code);
        plugins.push(plugin);
    }
    for (const input of env.args.providePlugins ?? []) {
        plugins.push(input);
    }
    applyFilesystemSchema({ engine });
    hooks.onStateCommit(({ changes }) => {
        env.emit({ type: "state_commit", payload: { changes } });
    });
    if (env.args.account) {
        const accountExists = engine.executeSync(internalQueryBuilder
            .selectFrom("account_by_version")
            .select("id")
            .where("id", "=", env.args.account.id)
            .where("lixcol_version_id", "=", "global")
            .limit(1)
            .compile()).rows.length > 0;
        if (!accountExists) {
            engine.executeSync(internalQueryBuilder
                .insertInto("account_by_version")
                .values({
                id: env.args.account.id,
                name: env.args.account.name,
                lixcol_version_id: "global",
            })
                .compile());
        }
        engine.executeSync(internalQueryBuilder.deleteFrom("active_account").compile());
        engine.executeSync(internalQueryBuilder
            .insertInto("active_account")
            .values({ account_id: env.args.account.id })
            .compile());
    }
    if (env.args.keyValues && env.args.keyValues.length > 0) {
        for (const kv of env.args.keyValues) {
            const explicitVid = kv.lixcol_version_id;
            if (explicitVid) {
                const exists = engine.executeSync(internalQueryBuilder
                    .selectFrom("key_value_by_version")
                    .select("key")
                    .where("key", "=", kv.key)
                    .where("lixcol_version_id", "=", explicitVid)
                    .limit(1)
                    .compile()).rows.length > 0;
                if (exists) {
                    engine.executeSync(internalQueryBuilder
                        .updateTable("key_value_by_version")
                        .set({ value: kv.value })
                        .where("key", "=", kv.key)
                        .where("lixcol_version_id", "=", explicitVid)
                        .compile());
                }
                else {
                    engine.executeSync(internalQueryBuilder
                        .insertInto("key_value_by_version")
                        .values({
                        key: kv.key,
                        value: kv.value,
                        lixcol_version_id: explicitVid,
                    })
                        .compile());
                }
            }
            else {
                const exists = engine.executeSync(internalQueryBuilder
                    .selectFrom("key_value")
                    .select("key")
                    .where("key", "=", kv.key)
                    .limit(1)
                    .compile()).rows.length > 0;
                if (exists) {
                    engine.executeSync(internalQueryBuilder
                        .updateTable("key_value")
                        .set({ value: kv.value })
                        .where("key", "=", kv.key)
                        .compile());
                }
                else {
                    engine.executeSync(internalQueryBuilder
                        .insertInto("key_value")
                        .values({
                        key: kv.key,
                        value: kv.value,
                    })
                        .compile());
                }
            }
        }
    }
    registerBuiltinFunctions({ register: fnRegistry.register, engine });
    if (deterministicBoot) {
        setDeterministicBoot({ runtimeCacheRef, value: false });
    }
    return engine;
}
//# sourceMappingURL=boot.js.map