import fs from "node:fs/promises";
import path from "pathe";
import { exists } from "../util/exists.js";
import { memoize } from "../util/memoize.js";
import { importPeer } from "../util/peer.js";
import { MIGRATIONS_DIRECTORY } from "./migrations.js";
import { StateStoreProxy } from "./proxy.js";
export class SQLiteStateStore extends StateStoreProxy {
    options;
    constructor(scope, options) {
        super(scope);
        this.options = options;
    }
    async provision() {
        const db = await createDatabase({
            ...(this.options ?? {}),
            dotAlchemy: this.scope.dotAlchemy,
        });
        const { SQLiteStateStoreOperations } = await import("./operations.js");
        const operations = new SQLiteStateStoreOperations(db, {
            chain: this.scope.chain,
        });
        return operations.dispatch.bind(operations);
    }
}
const createDatabase = memoize(async (options) => {
    switch (options?.engine) {
        case "bun":
            return await createBunSQLiteDatabase(options);
        case "libsql":
            return await createLibSQLDatabase(options);
        default: {
            return await createDefaultDatabase(options?.filename, options?.dotAlchemy);
        }
    }
});
async function createDefaultDatabase(filename, dotAlchemy) {
    if ("Bun" in globalThis) {
        return createBunSQLiteDatabase({ engine: "bun", filename, dotAlchemy });
    }
    return createLibSQLDatabase({ engine: "libsql", filename, dotAlchemy });
}
async function createBunSQLiteDatabase(options) {
    if (!("Bun" in globalThis)) {
        throw new Error("[SQLiteStateStore] The `engine: 'bun'` option is only available in Bun. Please use `engine: 'libsql'` instead.");
    }
    const filename = options?.filename ??
        process.env.ALCHEMY_STATE_FILE ??
        path.join(options?.dotAlchemy ?? ".alchemy", "state.sqlite");
    await ensureDirectory(filename);
    const { Database } = await import("bun:sqlite");
    const { drizzle } = await importPeer(import("drizzle-orm/bun-sqlite"), "SQLiteStateStore");
    const { migrate } = await import("drizzle-orm/bun-sqlite/migrator");
    const schema = await import("./schema.js");
    // Bun's constructor throws if we pass in an empty object or if extraneous
    // options are passed in, so here's some ugly destructuring!
    const { engine: _engine, filename: _filename, ...rest } = options ?? {};
    const bunOptions = Object.keys(rest).length > 0 ? rest : undefined;
    const client = new Database(filename, {
        readwrite: true,
        create: true,
        ...bunOptions,
    });
    client.exec("PRAGMA busy_timeout = 5000;");
    client.exec("PRAGMA journal_mode = WAL;");
    const db = drizzle(client, {
        schema,
    });
    migrate(db, { migrationsFolder: MIGRATIONS_DIRECTORY });
    return db;
}
async function createLibSQLDatabase(options) {
    const url = options?.url ??
        `file:${options?.filename ?? process.env.ALCHEMY_STATE_FILE ?? path.join(options?.dotAlchemy ?? ".alchemy", "state.sqlite")}`;
    const filename = url.startsWith("file:") ? url.slice(5) : undefined;
    if (filename) {
        await ensureDirectory(filename);
    }
    const { drizzle } = await importPeer(import("drizzle-orm/libsql"), "SQLiteStateStore");
    const { createClient } = await importPeer(import("@libsql/client"), "SQLiteStateStore (libsql)");
    const { migrate } = await import("drizzle-orm/libsql/migrator");
    const schema = await import("./schema.js");
    const client = createClient({ url, ...options });
    await client.execute("PRAGMA busy_timeout = 5000;");
    await client.execute("PRAGMA journal_mode = WAL;");
    const db = drizzle(client, {
        schema,
    });
    await migrate(db, { migrationsFolder: MIGRATIONS_DIRECTORY });
    return db;
}
const ensureDirectory = async (filename) => {
    const dir = path.dirname(filename);
    if (!(await exists(dir))) {
        await fs.mkdir(dir, { recursive: true });
    }
};
//# sourceMappingURL=sqlite-state-store.js.map