import pDefer from "p-defer";
import { v4 as uuid } from "uuid";
import * as messages from "./sqlite3-messages.worker.js";
import { create as createDatabase } from "./sqlite3.wasm.js";
import {} from "./types.js";
class ProxyStatement {
    send;
    databaseId;
    statementId;
    id;
    resolvers = {};
    constructor(send, databaseId, statementId) {
        this.send = send;
        this.databaseId = databaseId;
        this.statementId = statementId;
        this.id = statementId;
    }
    async bind(values) {
        await this.send({
            type: "bind",
            values: values.map(messages.encodeValue),
            id: uuid(),
            databaseId: this.databaseId,
            statementId: this.statementId,
        });
        return this;
    }
    async finalize() {
        await this.send({
            type: "finalize",
            id: uuid(),
            databaseId: this.databaseId,
            statementId: this.statementId,
        });
    }
    get(values) {
        return this.send({
            type: "get",
            values: values ? values.map(messages.encodeValue) : undefined,
            id: uuid(),
            databaseId: this.databaseId,
            statementId: this.statementId,
        });
    }
    async run(values) {
        await this.send({
            type: "run-statement",
            values: values.map(messages.encodeValue),
            id: uuid(),
            databaseId: this.databaseId,
            statementId: this.statementId,
        });
    }
    async reset() {
        await this.send({
            type: "reset",
            id: uuid(),
            databaseId: this.databaseId,
            statementId: this.statementId,
        });
        return this;
    }
    async all(values) {
        let id = uuid();
        const results = await this.send({
            type: "all",
            values: values.map(messages.encodeValue),
            id,
            databaseId: this.databaseId,
            statementId: this.statementId,
        });
        return results;
    }
}
class ProxyDatabase {
    send;
    statements = new Map();
    resolvers = {};
    databaseId;
    constructor(send) {
        this.send = send;
    }
    async init(directory) {
        this.databaseId = uuid();
        return this.send({
            type: "create",
            directory,
            databaseId: this.databaseId,
            id: uuid(),
        });
    }
    async exec(sql) {
        return this.send({
            type: "exec",
            sql,
            id: uuid(),
            databaseId: this.databaseId,
        });
    }
    async prepare(sql, id) {
        if (id != null) {
            const prev = this.statements.get(id);
            if (prev) {
                await prev.reset();
                return prev;
            }
        }
        const statementId = await this.send({
            type: "prepare",
            sql,
            id: uuid(),
            databaseId: this.databaseId,
        });
        const statement = new ProxyStatement(this.send, this.databaseId, statementId);
        this.statements.set(statementId, statement);
        if (id != null) {
            this.statements.set(id, statement);
        }
        return statement;
    }
    async open() {
        return this.send({ type: "open", id: uuid(), databaseId: this.databaseId });
    }
    async close() {
        return this.send({
            type: "close",
            id: uuid(),
            databaseId: this.databaseId,
        });
    }
    async drop() {
        return this.send({
            type: "drop",
            id: uuid(),
            databaseId: this.databaseId,
        });
    }
    async status() {
        return this.send({
            type: "status",
            id: uuid(),
            databaseId: this.databaseId,
        });
    }
}
let initialized = undefined;
const init = async () => {
    if (initialized) {
        return initialized;
    }
    let worker = new Worker(new URL("/peerbit/sqlite3/sqlite3.worker.min.js", import.meta.url), { type: "module" });
    let resolvers = {};
    let send = (message) => {
        const promise = new Promise((resolve, reject) => {
            resolvers[message.id] = { resolve, reject };
        });
        worker.postMessage(message);
        return promise.finally(() => delete resolvers[message.id]);
    };
    let isReady = pDefer();
    worker.onmessage = async (ev) => {
        const message = ev.data;
        if (message.type === "ready") {
            isReady.resolve();
            return;
        }
        const resolver = resolvers[message.id];
        if (message.type === "error") {
            resolver.reject(new Error(message.message));
        }
        else if (message.type === "response") {
            resolver.resolve(message.result);
        }
    };
    const create = async (directory) => {
        const db = new ProxyDatabase(send);
        await isReady.promise;
        await db.init(directory);
        await db.open();
        return db;
    };
    return (initialized = {
        create,
        close: () => {
            initialized = undefined;
            worker.terminate();
        },
    });
};
const create = (directory) => {
    if (directory) {
        // persist the database
        return init().then((creator) => creator.create(directory));
    }
    else {
        return createDatabase();
    }
};
export { create };
//# sourceMappingURL=sqlite3.browser.js.map