import { validateGraphName, validateWriterId } from "../utils/RefLayout.js";
import { AuditReceiptService } from "../services/audit/AuditReceiptService.js";
import defaultCodec from "../utils/defaultCodec.js";
import defaultCrypto from "../utils/defaultCrypto.js";
import MaterializedViewService from "../services/MaterializedViewService.js";
import StateHashService from "../services/state/StateHashService.js";
import { DEFAULT_COMMIT_MESSAGE_CODEC } from "../services/codec/WarpMessageCodec.js";
import StateSession from "../orset/session/StateSession.js";
import PageCache from "../orset/trie/PageCache.js";
import TrieGeometry from "../orset/trie/TrieGeometry.js";
import WarpError from "../errors/WarpError.js";
import { resolveBlobStorage, resolvePatchWriteStorage, resolveIndexStore, buildEffectPipeline, normalizeTrustConfig, } from "../runtimeHelpers.js";
import GCPolicy from "../services/GCPolicy.js";
const VALID_DELETE_POLICIES = ['reject', 'cascade', 'warn'];
export class WarpOpenOptions {
    persistence;
    graphName;
    writerId;
    gcPolicy;
    adjacencyCacheSize;
    checkpointPolicy;
    autoMaterialize;
    onDeleteWithData;
    logger;
    crypto;
    codec;
    seekCache;
    stateCache;
    audit;
    blobStorage;
    patchBlobStorage;
    commitMessageCodec;
    patchJournal;
    checkpointStore;
    indexStore;
    trust;
    effectPipeline;
    effectSinks;
    externalizationPolicy;
    openStateSession;
    constructor(options) {
        if (options.persistence === null || options.persistence === undefined) {
            throw new WarpError('persistence is required', 'E_INVALID_ARG');
        }
        validateGraphName(options.graphName);
        validateWriterId(options.writerId);
        this.persistence = options.persistence;
        this.graphName = options.graphName;
        this.writerId = options.writerId;
        this.gcPolicy = snapshotGCPolicy(options.gcPolicy);
        this.crypto = options.crypto ?? defaultCrypto;
        this.codec = options.codec ?? defaultCodec;
        if (options.adjacencyCacheSize !== undefined) {
            this.adjacencyCacheSize = options.adjacencyCacheSize;
        }
        const checkpointPolicy = normalizeCheckpointPolicy(options.checkpointPolicy);
        if (checkpointPolicy !== undefined) {
            this.checkpointPolicy = checkpointPolicy;
        }
        if (options.autoMaterialize !== undefined) {
            this.autoMaterialize = normalizeBooleanOption(options.autoMaterialize, 'autoMaterialize', 'E_AUTO_MATERIALIZE_TYPE');
        }
        if (options.onDeleteWithData !== undefined) {
            this.onDeleteWithData = normalizeDeletePolicy(options.onDeleteWithData);
        }
        if (options.logger !== undefined) {
            this.logger = options.logger;
        }
        if (options.seekCache !== undefined) {
            this.seekCache = options.seekCache;
        }
        if (options.stateCache !== undefined) {
            this.stateCache = options.stateCache;
        }
        if (options.audit !== undefined) {
            this.audit = normalizeBooleanOption(options.audit, 'audit', 'E_AUDIT_TYPE');
        }
        if (options.blobStorage !== undefined) {
            this.blobStorage = options.blobStorage;
        }
        if (options.patchBlobStorage !== undefined) {
            this.patchBlobStorage = options.patchBlobStorage;
        }
        if (options.commitMessageCodec !== undefined) {
            this.commitMessageCodec = options.commitMessageCodec;
        }
        if (options.patchJournal !== undefined) {
            this.patchJournal = options.patchJournal;
        }
        if (options.checkpointStore !== undefined) {
            this.checkpointStore = options.checkpointStore;
        }
        if (options.indexStore !== undefined) {
            this.indexStore = options.indexStore;
        }
        if (options.trust !== undefined) {
            this.trust = Object.freeze(normalizeTrustConfig(options.trust));
        }
        if (options.effectPipeline !== undefined) {
            this.effectPipeline = options.effectPipeline;
        }
        if (options.effectSinks !== undefined) {
            this.effectSinks = Object.freeze([...options.effectSinks]);
        }
        if (options.externalizationPolicy !== undefined) {
            this.externalizationPolicy = options.externalizationPolicy;
        }
        if (options.openStateSession !== undefined) {
            this.openStateSession = options.openStateSession;
        }
        Object.freeze(this);
    }
    static from(options) {
        if (options instanceof WarpOpenOptions) {
            return options;
        }
        return new WarpOpenOptions(options);
    }
    static minimal(options) {
        return new WarpOpenOptions({
            persistence: options.persistence,
            graphName: options.graphName ?? 'default',
            writerId: options.writerId ?? 'local',
        });
    }
}
function normalizeBooleanOption(value, label, code) {
    if (typeof value !== 'boolean') {
        throw new WarpError(`${label} must be a boolean`, code);
    }
    return value;
}
function normalizeCheckpointPolicy(checkpointPolicy) {
    if (checkpointPolicy === null || checkpointPolicy === undefined) {
        return undefined;
    }
    if (typeof checkpointPolicy !== 'object') {
        throw new WarpError('checkpointPolicy must be an object with { every: number }', 'E_CHECKPOINT_POLICY_TYPE');
    }
    if (!Number.isInteger(checkpointPolicy.every) || checkpointPolicy.every <= 0) {
        throw new WarpError('checkpointPolicy.every must be a positive integer', 'E_CHECKPOINT_POLICY_EVERY');
    }
    return Object.freeze({ every: checkpointPolicy.every });
}
function snapshotGCPolicy(value) {
    if (value === undefined) {
        return Object.freeze({});
    }
    if (value instanceof GCPolicy) {
        return value;
    }
    return Object.freeze({ ...value });
}
function normalizeDeletePolicy(policy) {
    if (!VALID_DELETE_POLICIES.includes(policy)) {
        throw new WarpError(`onDeleteWithData must be one of: ${VALID_DELETE_POLICIES.join(', ')}`, 'E_ON_DELETE_WITH_DATA_INVALID', { context: { got: policy, valid: VALID_DELETE_POLICIES } });
    }
    return policy;
}
export async function resolveRuntimeHostConstructionOptions(input) {
    const options = WarpOpenOptions.from(input);
    const { persistence, graphName, writerId, gcPolicy, adjacencyCacheSize, checkpointPolicy, autoMaterialize, onDeleteWithData, logger, crypto, codec, seekCache, stateCache, audit, blobStorage, patchBlobStorage, commitMessageCodec, patchJournal, checkpointStore, indexStore, trust, effectPipeline, effectSinks, externalizationPolicy, openStateSession, } = options;
    const normalizedTrust = normalizeTrustConfig(trust);
    const resolvedBlobStorage = await resolveBlobStorage(blobStorage, persistence);
    const resolvedCommitMessageCodec = commitMessageCodec ?? DEFAULT_COMMIT_MESSAGE_CODEC;
    const resolvedCodec = codec ?? defaultCodec;
    const resolvedCrypto = crypto ?? defaultCrypto;
    const patchWriteStorage = resolvePatchWriteStorage(persistence, patchBlobStorage);
    const blobPort = persistence;
    const commitPort = persistence;
    const treePort = persistence;
    let resolvedPatchJournal;
    if (patchJournal !== undefined && patchJournal !== null) {
        resolvedPatchJournal = patchJournal;
    }
    else {
        const { CborPatchJournalAdapter } = await import(
        /* webpackIgnore: true */ "../../infrastructure/adapters/CborPatchJournalAdapter.js");
        resolvedPatchJournal = new CborPatchJournalAdapter({
            codec: resolvedCodec,
            blobPort,
            commitPort,
            commitMessageCodec: resolvedCommitMessageCodec,
            ...(patchWriteStorage.strategy === 'git-cas' ? { blobStorage: resolvedBlobStorage } : {}),
            ...(patchBlobStorage !== undefined && patchBlobStorage !== null
                ? { legacyPatchBlobStorage: patchBlobStorage }
                : {}),
            writeStorage: patchWriteStorage,
        });
    }
    let resolvedCheckpointStore;
    if (checkpointStore !== undefined && checkpointStore !== null) {
        resolvedCheckpointStore = checkpointStore;
    }
    else {
        const { CborCheckpointStoreAdapter } = await import(
        /* webpackIgnore: true */ "../../infrastructure/adapters/CborCheckpointStoreAdapter.js");
        resolvedCheckpointStore = new CborCheckpointStoreAdapter({
            codec: resolvedCodec,
            blobPort,
            blobStorage: resolvedBlobStorage,
        });
    }
    const resolvedIndexStore = await resolveIndexStore(indexStore, {
        codec: resolvedCodec,
        blobPort,
        treePort,
        blobStorage: resolvedBlobStorage,
    });
    const resolvedStateHashService = new StateHashService({
        codec: resolvedCodec,
        crypto: resolvedCrypto,
    });
    const resolvedViewService = new MaterializedViewService({
        codec: resolvedCodec,
        ...(logger !== undefined ? { logger } : {}),
        indexStore: resolvedIndexStore,
    });
    let resolvedAuditService;
    if (audit === true) {
        resolvedAuditService = new AuditReceiptService({
            persistence,
            graphName,
            writerId,
            codec: resolvedCodec,
            crypto: resolvedCrypto,
            ...(logger !== undefined ? { logger } : {}),
        });
        await resolvedAuditService.init();
    }
    let resolvedEffectPipeline;
    if (effectPipeline !== null && effectPipeline !== undefined) {
        resolvedEffectPipeline = effectPipeline;
    }
    else if (effectSinks !== null && effectSinks !== undefined && effectSinks.length > 0) {
        resolvedEffectPipeline = await buildEffectPipeline(effectSinks, externalizationPolicy);
    }
    let resolvedOpenStateSession;
    if (openStateSession !== undefined) {
        resolvedOpenStateSession = openStateSession;
    }
    else if (typeof persistence.createRuntimeTrieStore === 'function') {
        const store = await persistence.createRuntimeTrieStore();
        const pageCache = new PageCache({ maxResident: 256 });
        const geometry = TrieGeometry.default16way();
        resolvedOpenStateSession = async (roots) => await StateSession.open({
            nodeAliveRootOid: roots.nodeAliveRootOid,
            edgeAliveRootOid: roots.edgeAliveRootOid,
            store,
            codec: resolvedCodec,
            geometry,
            pageCache,
        });
    }
    return {
        normalizedTrust,
        options: {
            persistence,
            graphName,
            writerId,
            gcPolicy,
            ...(adjacencyCacheSize !== undefined ? { adjacencyCacheSize } : {}),
            ...(checkpointPolicy !== undefined ? { checkpointPolicy } : {}),
            ...(autoMaterialize !== undefined ? { autoMaterialize } : {}),
            ...(onDeleteWithData !== undefined ? { onDeleteWithData } : {}),
            ...(logger !== undefined ? { logger } : {}),
            ...(crypto !== undefined ? { crypto } : {}),
            ...(codec !== undefined ? { codec } : {}),
            ...(seekCache !== undefined ? { seekCache } : {}),
            ...(stateCache !== undefined ? { stateCache } : {}),
            ...(audit !== undefined ? { audit } : {}),
            blobStorage: resolvedBlobStorage,
            ...(patchBlobStorage !== undefined ? { patchBlobStorage } : {}),
            commitMessageCodec: resolvedCommitMessageCodec,
            ...(trust !== undefined ? { trust } : {}),
            patchJournal: resolvedPatchJournal,
            checkpointStore: resolvedCheckpointStore,
            indexStore: resolvedIndexStore,
            viewService: resolvedViewService,
            stateHashService: resolvedStateHashService,
            ...(resolvedAuditService !== undefined ? { auditService: resolvedAuditService } : {}),
            ...(resolvedEffectPipeline !== undefined && resolvedEffectPipeline !== null
                ? { effectPipeline: resolvedEffectPipeline }
                : {}),
            ...(resolvedOpenStateSession === undefined ? {} : { openStateSession: resolvedOpenStateSession }),
        },
    };
}
