export class VirtualLedger {
    private cashUsd: number;
    private reservedUsd = 0;
    private realizedPnlUsd = 0;

    constructor(
        private readonly initialCapitalUsd: number,
        private readonly allowNegativeCash: boolean = false
    ) {
        if (!Number.isFinite(initialCapitalUsd) || initialCapitalUsd <= 0) {
            throw new Error(`Invalid initial capital: ${initialCapitalUsd}`);
        }
        this.cashUsd = initialCapitalUsd;
    }

    public getInitialCapitalUsd(): number {
        return this.initialCapitalUsd;
    }

    public getCashUsd(): number {
        return this.cashUsd;
    }

    public getReservedUsd(): number {
        return this.reservedUsd;
    }

    public getAvailableCashUsd(): number {
        return this.cashUsd - this.reservedUsd;
    }

    public getRealizedPnlUsd(): number {
        return this.realizedPnlUsd;
    }

    public reserveCapital(amountUsd: number): boolean {
        if (!Number.isFinite(amountUsd) || amountUsd <= 0) return false;
        const nextReserved = this.reservedUsd + amountUsd;
        if (!this.allowNegativeCash && nextReserved > this.cashUsd + 1e-8) {
            return false;
        }
        this.reservedUsd = nextReserved;
        return true;
    }

    public releaseReserved(amountUsd: number): void {
        if (!Number.isFinite(amountUsd) || amountUsd <= 0) return;
        this.reservedUsd = Math.max(0, this.reservedUsd - amountUsd);
    }

    public consumeReserved(amountUsd: number): void {
        if (!Number.isFinite(amountUsd) || amountUsd <= 0) return;
        const consumed = Math.min(this.reservedUsd, amountUsd);
        this.reservedUsd = Math.max(0, this.reservedUsd - consumed);
        this.cashUsd -= consumed;
        this.assertCashInvariant();
    }

    public adjustCash(amountUsd: number): void {
        if (!Number.isFinite(amountUsd) || amountUsd === 0) return;
        this.cashUsd += amountUsd;
        this.assertCashInvariant();
    }

    public recordRealizedPnl(pnlUsd: number): void {
        if (!Number.isFinite(pnlUsd)) return;
        this.realizedPnlUsd += pnlUsd;
    }

    private assertCashInvariant(): void {
        if (this.allowNegativeCash) return;
        if (this.cashUsd < -1e-8) {
            throw new Error(`Backtest invariant violation: cash became negative (${this.cashUsd}).`);
        }
    }
}

