import MemoryBudgetError from "../errors/MemoryBudgetError.js";
import MemoryBudget from "./MemoryBudget.js";
import MemoryBudgetLease from "./MemoryBudgetLease.js";
import WarpMemoryPoolSnapshot from "./WarpMemoryPoolSnapshot.js";
/** Runtime pool for deterministic git-warp-owned memory-budget leases. */
export default class WarpMemoryPool {
    name;
    budget;
    _leased;
    _peak;
    _rejected;
    _nextLease;
    _releasedLeases;
    constructor(fields) {
        const validFields = requirePoolFields(fields);
        this.name = requireNonEmptyString(validFields.name, 'name');
        this.budget = requireMemoryBudget(validFields.budget);
        this._leased = 0;
        this._peak = 0;
        this._rejected = 0;
        this._nextLease = 1;
        this._releasedLeases = new Set();
    }
    acquire(request) {
        const validRequest = requireLeaseRequest(request);
        const scope = requireNonEmptyString(validRequest.scope, 'scope');
        const amount = requirePositiveInteger(validRequest.amount, 'amount');
        this.assertAvailable(scope, amount);
        this._leased += amount;
        this._peak = Math.max(this._peak, this._leased);
        const id = this.nextLeaseId();
        return new MemoryBudgetLease({
            id,
            name: this.name,
            scope,
            amount,
            unit: this.budget.unit,
            releaseLease: (leaseId) => this.release(leaseId, amount),
        });
    }
    snapshot() {
        return new WarpMemoryPoolSnapshot({
            name: this.name,
            limit: this.budget.limit,
            unit: this.budget.unit,
            leased: this._leased,
            peak: this._peak,
            rejected: this._rejected,
        });
    }
    assertAvailable(scope, amount) {
        if (this._leased + amount <= this.budget.limit) {
            return;
        }
        this._rejected += 1;
        throw new MemoryBudgetError('Memory budget exceeded for git-warp-owned resident data', {
            context: {
                name: this.name,
                scope,
                unit: this.budget.unit,
                limit: this.budget.limit,
                leased: this._leased,
                requested: amount,
                rejected: this._rejected,
            },
        });
    }
    release(id, amount) {
        if (this._releasedLeases.has(id)) {
            return;
        }
        this._releasedLeases.add(id);
        this._leased -= amount;
    }
    nextLeaseId() {
        const leaseId = `${this.name}:${this._nextLease}`;
        this._nextLease += 1;
        return leaseId;
    }
}
function requirePoolFields(fields) {
    if (fields !== null && typeof fields === 'object') {
        return fields;
    }
    throw new MemoryBudgetError('WarpMemoryPool requires object fields', {
        code: 'E_MEMORY_BUDGET_INVALID',
        context: { field: 'fields' },
    });
}
function requireLeaseRequest(request) {
    if (request !== null && typeof request === 'object') {
        return request;
    }
    throw new MemoryBudgetError('Memory lease request must be an object', {
        code: 'E_MEMORY_BUDGET_INVALID',
        context: { field: 'request' },
    });
}
function requireMemoryBudget(value) {
    if (value instanceof MemoryBudget) {
        return value;
    }
    throw new MemoryBudgetError('WarpMemoryPool requires a MemoryBudget', {
        code: 'E_MEMORY_BUDGET_INVALID',
        context: { field: 'budget' },
    });
}
function requireNonEmptyString(value, field) {
    if (typeof value === 'string' && value.trim().length > 0) {
        return value;
    }
    throw new MemoryBudgetError('WarpMemoryPool requires non-empty identity fields', {
        code: 'E_MEMORY_BUDGET_INVALID',
        context: { field },
    });
}
function requirePositiveInteger(value, field) {
    if (Number.isInteger(value) && value > 0) {
        return value;
    }
    throw new MemoryBudgetError('Memory lease amount must be a positive integer', {
        code: 'E_MEMORY_BUDGET_INVALID',
        context: { field, value },
    });
}
