import { BinaryReader, BinaryWriter } from "@dao-xyz/borsh";
import { fromBase64URL, toBase64URL } from "@peerbit/crypto";
import { default as sqlite3InitModule, } from "@sqlite.org/sqlite-wasm";
import { v4 as uuid } from "uuid";
import {} from "./types.js";
export const encodeName = (name) => {
    // since "/" and perhaps other characters might not be allowed we do encode
    const writer = new BinaryWriter();
    writer.string(name);
    return toBase64URL(writer.finalize());
};
export const decodeName = (name) => {
    // since "/" and perhaps other characters might not be allowed we do encode
    const writer = new BinaryReader(fromBase64URL(name));
    return writer.string();
};
class Statement {
    statement;
    id;
    constructor(statement, id) {
        this.statement = statement;
        this.id = id;
    }
    async bind(values) {
        await this.statement.bind(values);
        return this;
    }
    async finalize() {
        const out = await this.statement.finalize();
        if (out != null && out > 0) {
            throw new Error("Error finalizing statement");
        }
    }
    get(values) {
        if (values && values?.length > 0) {
            this.statement.bind(values);
        }
        let step = this.statement.step();
        if (!step) {
            // no data available
            this.statement.reset();
            return undefined;
        }
        const results = this.statement.get({});
        this.statement.reset();
        return results;
    }
    run(values) {
        this.statement.bind(values);
        this.statement.stepReset();
    }
    async reset() {
        await this.statement.reset();
        return this;
    }
    all(values) {
        if (values && values.length > 0) {
            this.statement.bind(values);
        }
        let results = [];
        while (this.statement.step()) {
            results.push(this.statement.get({}));
        }
        this.statement.reset();
        return results;
    }
    step() {
        return this.statement.step();
    }
}
// eslint-disable-next-line no-console
const log = (...args) => console.log(...args);
// eslint-disable-next-line no-console
const error = (...args) => console.error(...args);
let poolUtil = undefined;
let sqlite3 = undefined;
const create = async (directory) => {
    let statements = new Map();
    sqlite3 =
        sqlite3 || (await sqlite3InitModule({ print: log, printErr: error }));
    let sqliteDb = undefined;
    let close = async () => {
        await Promise.all([...statements.values()].map((x) => x.finalize?.()));
        statements.clear();
        await sqliteDb?.close();
        sqliteDb = undefined;
    };
    let dbFileName;
    let drop = async () => {
        if (poolUtil && dbFileName != null) {
            poolUtil.unlink(dbFileName);
        }
        return close();
    };
    let open = async () => {
        if (sqliteDb) {
            return sqliteDb;
        }
        if (directory) {
            // directory has to be absolute path. Remove leading dot if any
            // TODO show warning if directory is not absolute?
            directory = directory.replace(/^\./, "");
            dbFileName = `${directory}/db.sqlite`;
            const poolDirectory = `${directory}/peerbit/sqlite-opfs-pool`; // we do a unique directory else we will get problem open a client in multiple tabs
            poolUtil =
                poolUtil ||
                    (await sqlite3.installOpfsSAHPoolVfs({
                        directory: poolDirectory,
                    }));
            await poolUtil.reserveMinimumCapacity(100);
            sqliteDb = new poolUtil.OpfsSAHPoolDb(dbFileName);
        }
        else {
            sqliteDb = new sqlite3.oo1.DB(":memory:");
        }
        sqliteDb.exec("PRAGMA journal_mode = WAL");
        sqliteDb.exec("PRAGMA foreign_keys = on");
    };
    return {
        close,
        exec: (sql) => {
            return sqliteDb.exec(sql);
        },
        open,
        drop,
        prepare: async (sql, id) => {
            if (id == null) {
                id = uuid();
            }
            let prev = statements.get(id);
            if (prev) {
                await prev.reset();
                return prev;
            }
            const statement = sqliteDb.prepare(sql);
            const wrappedStatement = new Statement(statement, id);
            statements.set(id, wrappedStatement);
            return wrappedStatement;
        },
        get(sql) {
            return sqliteDb.exec({ sql, rowMode: "array" });
        },
        run(sql, bind) {
            return sqliteDb.exec(sql, { bind, rowMode: "array" });
        },
        status: () => (sqliteDb?.isOpen() ? "open" : "closed"),
        statements,
    };
};
export { create };
//# sourceMappingURL=sqlite3.wasm.js.map