import MemoryBudgetError from "../../errors/MemoryBudgetError.js";
import WarpMemoryPool from "../../memory/WarpMemoryPool.js";
import BoundedSyncPatchBatch from "./BoundedSyncPatchBatch.js";
const SYNC_PATCH_BATCH_LEASE_SCOPE = 'sync.patch.batch';
const SYNC_PATCH_BATCH_LEASE_AMOUNT = 1;
const SYNC_PATCH_BATCH_READ_AHEAD_INCREMENT = 1;
/** Cursorized bounded reader for sync patch descriptor streams. */
export default class BoundedSyncPatchBatchReader {
    _openSource;
    _pool;
    constructor(fields) {
        const validFields = requireReaderFields(fields);
        this._openSource = requireOpenSource(validFields.openSource);
        this._pool = requireWarpMemoryPool(validFields.pool);
        Object.freeze(this);
    }
    async readBatch(request) {
        const validRequest = requireBatchRequest(request);
        const limit = requirePositiveInteger(validRequest.limit, 'limit');
        const cursor = normalizeCursor(validRequest.cursor ?? null);
        return await collectBatch({
            source: this._openSource({ cursor, limit: readAheadLimit(limit) }),
            pool: this._pool,
            start: cursorOffset(cursor),
            limit,
        });
    }
}
async function collectBatch(options) {
    const patches = [];
    const leases = [];
    let index = options.start;
    let cursor = null;
    try {
        for await (const patch of options.source) {
            if (patches.length >= options.limit) {
                cursor = index.toString();
                break;
            }
            leases.push(options.pool.acquire({
                scope: SYNC_PATCH_BATCH_LEASE_SCOPE,
                amount: SYNC_PATCH_BATCH_LEASE_AMOUNT,
            }));
            const control = appendPatch({ patches, patch, index, limit: options.limit });
            index = control.nextIndex;
            cursor = control.cursor;
            if (cursor !== null) {
                break;
            }
        }
        return new BoundedSyncPatchBatch({ patches, cursor });
    }
    finally {
        for (const lease of leases) {
            lease.release();
        }
    }
}
function appendPatch(options) {
    if (options.patches.length >= options.limit) {
        return { nextIndex: options.index, cursor: options.index.toString() };
    }
    options.patches.push(options.patch);
    return { nextIndex: options.index + 1, cursor: null };
}
function normalizeCursor(cursor) {
    cursorOffset(cursor);
    return cursor;
}
function cursorOffset(cursor) {
    if (cursor === null) {
        return 0;
    }
    const parsed = Number.parseInt(cursor, 10);
    if (Number.isInteger(parsed) && parsed >= 0 && parsed.toString() === cursor) {
        return parsed;
    }
    throw new MemoryBudgetError('Bounded sync patch batch cursor must be a non-negative integer string', {
        code: 'E_BOUNDED_SYNC_BATCH_INVALID',
        context: { field: 'cursor', value: cursor },
    });
}
function readAheadLimit(limit) {
    return limit + SYNC_PATCH_BATCH_READ_AHEAD_INCREMENT;
}
function requireReaderFields(fields) {
    if (fields !== null && typeof fields === 'object') {
        return fields;
    }
    throw new MemoryBudgetError('BoundedSyncPatchBatchReader requires object fields', {
        code: 'E_BOUNDED_SYNC_BATCH_INVALID',
        context: { field: 'fields' },
    });
}
function requireBatchRequest(request) {
    if (request !== null && typeof request === 'object') {
        return request;
    }
    throw new MemoryBudgetError('Bounded sync patch batch request must be an object', {
        code: 'E_BOUNDED_SYNC_BATCH_INVALID',
        context: { field: 'request' },
    });
}
function requireOpenSource(value) {
    if (typeof value === 'function') {
        return value;
    }
    throw new MemoryBudgetError('BoundedSyncPatchBatchReader requires an openSource function', {
        code: 'E_BOUNDED_SYNC_BATCH_INVALID',
        context: { field: 'openSource' },
    });
}
function requirePositiveInteger(value, field) {
    if (Number.isInteger(value) && value > 0) {
        return value;
    }
    throw new MemoryBudgetError('Bounded sync patch batch limit must be a positive integer', {
        code: 'E_BOUNDED_SYNC_BATCH_INVALID',
        context: { field, value },
    });
}
function requireWarpMemoryPool(value) {
    if (value instanceof WarpMemoryPool) {
        return value;
    }
    throw new MemoryBudgetError('BoundedSyncPatchBatchReader requires a WarpMemoryPool', {
        code: 'E_MEMORY_BUDGET_INVALID',
        context: { field: 'pool' },
    });
}
