import IndexStorePort from "../../ports/IndexStorePort.js";
import WarpError from "../../domain/errors/WarpError.js";
import WarpStream from "../../domain/stream/WarpStream.js";
import { MetaShard } from "../../domain/artifacts/MetaShard.js";
import { EdgeShard } from "../../domain/artifacts/EdgeShard.js";
import { LabelShard } from "../../domain/artifacts/LabelShard.js";
import { PropertyShard } from "../../domain/artifacts/PropertyShard.js";
import { ReceiptShard } from "../../domain/artifacts/ReceiptShard.js";
import { IndexShardEncodeTransform } from "./IndexShardEncodeTransform.js";
import { readPayloadBlob, writePayloadBlob } from "./CasPayloadPointer.js";
function classifyMeta(match, data) {
    const d = data;
    return new MetaShard({
        shardKey: match[1],
        nodeToGlobal: d.nodeToGlobal,
        nextLocalId: d.nextLocalId,
        alive: d.alive,
    });
}
function classifyEdge(match, data) {
    return new EdgeShard({
        shardKey: match[2],
        direction: match[1],
        buckets: data,
    });
}
function classifyLabel(_match, data) {
    return new LabelShard({
        labels: data,
    });
}
function classifyProperty(match, data) {
    return new PropertyShard({
        shardKey: match[1],
        entries: data,
    });
}
function classifyReceipt(_match, data) {
    const d = data;
    return new ReceiptShard({
        version: d.version,
        nodeCount: d.nodeCount,
        labelCount: d.labelCount,
        shardCount: d.shardCount,
    });
}
const SHARD_CLASSIFIERS = Object.freeze([
    { pattern: /^meta_([0-9a-f]+)\.cbor$/, classify: classifyMeta },
    { pattern: /^(fwd|rev)_([0-9a-f]+)\.cbor$/, classify: classifyEdge },
    { pattern: /^labels\.cbor$/, classify: classifyLabel },
    { pattern: /^props_([0-9a-f]+)\.cbor$/, classify: classifyProperty },
    { pattern: /^receipt\.cbor$/, classify: classifyReceipt },
]);
/**
 * CBOR-backed implementation of IndexStorePort.
 *
 * Owns the codec and raw Git persistence. Domain services produce
 * IndexShard streams; the adapter encodes, writes blobs, and
 * assembles Git trees. On read, the adapter decodes blobs and
 * constructs IndexShard subclass instances.
 *
 * Write pipeline reuses existing infrastructure transforms:
 *   WarpStream<IndexShard>
 *     → IndexShardEncodeTransform → [path, bytes]
 *     → GitBlobWriteTransform     → [path, oid]
 *     → TreeAssemblerSink         → tree OID
 */
export class CborIndexStoreAdapter extends IndexStorePort {
    _codec;
    _blobPort;
    _treePort;
    _blobStorage;
    constructor({ codec, blobPort, treePort, blobStorage }) {
        super();
        _requireDep(codec, 'codec');
        _requireDep(blobPort, 'blobPort');
        _requireDep(treePort, 'treePort');
        this._codec = codec;
        this._blobPort = blobPort;
        this._treePort = treePort;
        this._blobStorage = blobStorage ?? null;
    }
    async writeShards(shardStream) {
        const entries = [];
        await shardStream
            .pipe(new IndexShardEncodeTransform(this._codec))
            .forEach(async ([path, bytes]) => {
            const oid = await writePayloadBlob({
                blobPort: this._blobPort,
                blobStorage: this._blobStorage,
                bytes,
                options: {
                    slug: path,
                    mime: 'application/cbor',
                    size: bytes.length,
                },
            });
            entries.push(`100644 blob ${oid}\t${path}`);
        });
        entries.sort();
        return await this._treePort.writeTree(entries);
    }
    scanShards(treeOid) {
        const adapter = this;
        return WarpStream.from((async function* () {
            const oids = await adapter._treePort.readTreeOids(treeOid);
            const paths = Object.keys(oids).sort();
            for (const path of paths) {
                const shard = tryClassifyPath(path);
                if (shard === null) {
                    continue;
                }
                const blobOid = oids[path];
                const bytes = await readPayloadBlob({
                    blobPort: adapter._blobPort,
                    blobStorage: adapter._blobStorage,
                    oid: blobOid,
                });
                const data = adapter._codec.decode(bytes);
                yield shard(data);
            }
        })());
    }
    async readShardOids(treeOid) {
        return await this._treePort.readTreeOids(treeOid);
    }
    async decodeShard(blobOid) {
        const bytes = await readPayloadBlob({
            blobPort: this._blobPort,
            blobStorage: this._blobStorage,
            oid: blobOid,
        });
        return this._codec.decode(bytes);
    }
}
function tryClassifyPath(path) {
    for (const { pattern, classify } of SHARD_CLASSIFIERS) {
        const match = path.match(pattern);
        if (match) {
            return (data) => classify(match, data);
        }
    }
    return null;
}
function _requireDep(dep, name) {
    if (dep === null || dep === undefined) {
        throw new WarpError(`CborIndexStoreAdapter requires a ${name}`, 'E_INVALID_DEPENDENCY');
    }
}
