import StateSessionError from "../../errors/StateSessionError.js";
import PageCache from "../trie/PageCache.js";
import TrieCursor from "../trie/TrieCursor.js";
import TrieFlusher from "../trie/TrieFlusher.js";
import TrieGeometry from "../trie/TrieGeometry.js";
import ShadowTrieORSet from "../shadow/ShadowTrieORSet.js";
import StateSessionCloseResult from "./StateSessionCloseResult.js";
export default class StateSession {
    #nodeAlive;
    #edgeAlive;
    #closed = false;
    constructor(fields) {
        this.#nodeAlive = fields.nodeAlive;
        this.#edgeAlive = fields.edgeAlive;
        Object.freeze(this);
    }
    static async open(init) {
        validateRootOid("nodeAliveRootOid", init.nodeAliveRootOid);
        validateRootOid("edgeAliveRootOid", init.edgeAliveRootOid);
        validateStore(init.store);
        validateCodec(init.codec);
        validateGeometry(init.geometry);
        validatePageCache(init.pageCache);
        const nodeCursor = new TrieCursor({
            rootOid: init.nodeAliveRootOid,
            store: init.store,
            geometry: init.geometry,
            codec: init.codec,
            pageCache: init.pageCache,
        });
        const edgeCursor = new TrieCursor({
            rootOid: init.edgeAliveRootOid,
            store: init.store,
            geometry: init.geometry,
            codec: init.codec,
            pageCache: init.pageCache,
        });
        const nodeFlusher = new TrieFlusher({
            store: init.store,
            codec: init.codec,
        });
        const edgeFlusher = new TrieFlusher({
            store: init.store,
            codec: init.codec,
        });
        return new StateSession({
            nodeAlive: new ShadowTrieORSet({
                cursor: nodeCursor,
                flusher: nodeFlusher,
            }),
            edgeAlive: new ShadowTrieORSet({
                cursor: edgeCursor,
                flusher: edgeFlusher,
            }),
        });
    }
    async nodeContains(id) {
        this.#assertOpen();
        return await this.#nodeAlive.contains(id);
    }
    async nodeDots(id) {
        this.#assertOpen();
        return await this.#nodeAlive.getDots(id);
    }
    async nodeElementState(id) {
        this.#assertOpen();
        return await this.#nodeAlive.getElementState(id);
    }
    async edgeContains(key) {
        this.#assertOpen();
        return await this.#edgeAlive.contains(key);
    }
    async edgeDots(key) {
        this.#assertOpen();
        return await this.#edgeAlive.getDots(key);
    }
    async edgeElementState(key) {
        this.#assertOpen();
        return await this.#edgeAlive.getElementState(key);
    }
    async addNode(id, dot) {
        this.#assertOpen();
        await this.#nodeAlive.add(id, dot);
    }
    async addEdge(key, dot) {
        this.#assertOpen();
        await this.#edgeAlive.add(key, dot);
    }
    async removeNodes(observedDots) {
        this.#assertOpen();
        await this.#nodeAlive.remove(observedDots);
    }
    async removeEdges(observedDots) {
        this.#assertOpen();
        await this.#edgeAlive.remove(observedDots);
    }
    scanNodes() {
        this.#assertOpen();
        return this.#nodeAlive.scan();
    }
    scanNodeElementStates() {
        this.#assertOpen();
        return this.#nodeAlive.scanElementStates();
    }
    scanEdges() {
        this.#assertOpen();
        return this.#edgeAlive.scan();
    }
    scanEdgeElementStates() {
        this.#assertOpen();
        return this.#edgeAlive.scanElementStates();
    }
    async compact(includedVV) {
        this.#assertOpen();
        await this.#nodeAlive.compact(includedVV);
        await this.#edgeAlive.compact(includedVV);
    }
    async close() {
        this.#assertOpen();
        const nodeFlush = await this.#nodeAlive.flush();
        const edgeFlush = await this.#edgeAlive.flush();
        this.#closed = true;
        return new StateSessionCloseResult({
            nodeAliveRootOid: nodeFlush.rootOid,
            edgeAliveRootOid: edgeFlush.rootOid,
        });
    }
    #assertOpen() {
        if (this.#closed) {
            throw new StateSessionError("StateSession is closed", { code: "E_STATE_SESSION_CLOSED" });
        }
    }
}
function validateRootOid(name, rootOid) {
    if (rootOid === null) {
        return;
    }
    if (typeof rootOid !== "string" || rootOid.length === 0) {
        throw new StateSessionError(`StateSession ${name} must be null or a non-empty string; received ${String(rootOid)}`, {
            code: "E_STATE_SESSION_INPUT",
            context: { field: name, rootOid },
        });
    }
}
function validateStore(store) {
    if (typeof store.readLeaf !== "function" ||
        typeof store.readBranch !== "function" ||
        typeof store.writeLeaf !== "function" ||
        typeof store.writeBranch !== "function") {
        throw new StateSessionError("StateSession requires a TrieStorePort with read/write methods", {
            code: "E_STATE_SESSION_INPUT",
            context: { field: "store" },
        });
    }
}
function validateCodec(codec) {
    if (typeof codec.encode !== "function" ||
        typeof codec.decode !== "function") {
        throw new StateSessionError("StateSession requires a CodecPort with encode/decode methods", {
            code: "E_STATE_SESSION_INPUT",
            context: { field: "codec" },
        });
    }
}
function validateGeometry(geometry) {
    if (!(geometry instanceof TrieGeometry)) {
        throw new StateSessionError("StateSession requires a TrieGeometry instance", {
            code: "E_STATE_SESSION_INPUT",
            context: { field: "geometry" },
        });
    }
}
function validatePageCache(pageCache) {
    if (!(pageCache instanceof PageCache)) {
        throw new StateSessionError("StateSession requires a PageCache", {
            code: "E_STATE_SESSION_INPUT",
            context: { field: "pageCache" },
        });
    }
}
