import { alchemy } from "../../alchemy.js";
import { createCloudflareApi, } from "../../cloudflare/api.js";
import { getAccountSubdomain } from "../../cloudflare/worker-subdomain.js";
import { ResourceScope } from "../../resource.js";
import { deserialize, serialize } from "../../serde.js";
import { DOFSStateStoreClient, upsertStateStoreWorker } from "./store.js";
/**
 * A state store backed by a {@link https://www.npmjs.com/package/dofs Durable Object File System (DOFS)}.
 *
 * @deprecated This state store is no longer recommended for production use. Please use {@link https://alchemy.run/guides/cloudflare-state-store/ CloudflareStateStore} from `alchemy/state` instead, which offers better reliability and performance.
 */
export class DOFSStateStore {
    scope;
    options;
    prefix;
    client;
    constructor(scope, options = {}) {
        this.scope = scope;
        this.options = options;
        this.prefix = [options.prefix ?? "alchemy", ...scope.chain, ""].join("/");
    }
    async createClient() {
        const token = this.options.worker?.token ??
            alchemy.secret.env.ALCHEMY_STATE_TOKEN.unencrypted;
        if (this.options.worker && "url" in this.options.worker) {
            const client = new DOFSStateStoreClient({
                app: this.scope.appName ?? "alchemy",
                stage: this.scope.stage,
                url: this.options.worker.url,
                token,
            });
            // The worker is already created and should be ready to use.
            // If it's not, we'll fail fast with a helpful error message.
            const res = await client.validate();
            if (res.status === 200) {
                return client;
            }
            if (res.status === 401) {
                throw new Error("A worker URL was provided to DOFSStateStore, but the token is incorrect. Correct the token or remove the worker URL to create a new worker.");
            }
            throw new Error(`A worker URL was provided to DOFSStateStore, but the worker status is ${res.status} ${res.statusText}.`);
        }
        const workerName = this.options.worker?.name ?? "alchemy-state";
        const api = await createCloudflareApi(this.options);
        const [subdomain, _] = await Promise.all([
            getAccountSubdomain(api),
            upsertStateStoreWorker(api, workerName, token, this.options.worker?.force ?? false),
        ]);
        if (!subdomain) {
            throw new Error("Failed to access state store worker because the workers.dev subdomain is not available.");
        }
        const client = new DOFSStateStoreClient({
            app: this.scope.appName ?? "alchemy",
            stage: this.scope.stage,
            url: `https://${workerName}.${subdomain}.workers.dev`,
            token,
        });
        await client.waitUntilReady();
        return client;
    }
    async getClient() {
        this.client ??= this.createClient();
        return await this.client;
    }
    async init() {
        await this.getClient();
    }
    async list() {
        const client = await this.getClient();
        const res = await client.rpc("list", { prefix: this.prefix });
        return res.map((key) => this.deserializeKey(key));
    }
    async count() {
        const client = await this.getClient();
        const res = await client.rpc("count", { prefix: this.prefix });
        return res;
    }
    async get(key) {
        const client = await this.getClient();
        const res = await client.rpc("get", { key: this.serializeKey(key) });
        if (res) {
            return await this.deserializeState(res);
        }
        return undefined;
    }
    async getBatch(ids) {
        const client = await this.getClient();
        const res = await client.rpc("getBatch", {
            keys: ids.map((id) => this.serializeKey(id)),
        });
        return await this.deserializeStates(res);
    }
    async all() {
        const client = await this.getClient();
        const res = await client.rpc("all", { prefix: this.prefix });
        return await this.deserializeStates(res);
    }
    async set(key, value) {
        const client = await this.getClient();
        await client.rpc("set", {
            key: this.serializeKey(key),
            value: await serialize(this.scope, value),
        });
    }
    async delete(key) {
        const client = await this.getClient();
        await client.rpc("delete", { key: this.serializeKey(key) });
    }
    async deserializeStates(input) {
        return Object.fromEntries(await Promise.all(Object.entries(input).map(async ([key, value]) => [
            this.deserializeKey(key),
            await this.deserializeState(value),
        ])));
    }
    async deserializeState(input) {
        const state = (await deserialize(this.scope, JSON.parse(input)));
        if (state.output === undefined) {
            state.output = {};
        }
        state.output[ResourceScope] = this.scope;
        return state;
    }
    deserializeKey(key) {
        return key.replace(this.prefix, "").replaceAll(":", "/");
    }
    serializeKey(key) {
        return `${this.prefix}${key.replaceAll("/", ":")}`;
    }
}
//# sourceMappingURL=index.js.map