import BalanceModel, { BalanceModelConfig } from './BalanceModel';
import { beforeEach, describe, expect, it } from '@jest/globals';

describe('BalanceModel', () =>
{
    let balanceModel: BalanceModel;

    beforeEach(() =>
    {
        balanceModel = new BalanceModel();
    });

    it('initialises with a default balance of 0', () =>
    {
        expect(balanceModel.getBalance()).toBe(0);
    });

    it('initialises with a specified starting balance', () =>
    {
        balanceModel = new BalanceModel(100);

        expect(balanceModel.getBalance()).toBe(100);
    });

    it('supports the configuration overload', () =>
    {
        const config: BalanceModelConfig = { initialBalance: 75 };

        balanceModel = new BalanceModel(config);

        expect(balanceModel.balance).toBe(75);
    });

    it('throws when the initial balance is invalid', () =>
    {
        expect(() => new BalanceModel(-1)).toThrow('Balance cannot be negative.');
        expect(() => new BalanceModel({ initialBalance: Number.NaN })).toThrow('Initial balance must be a finite number.');
    });

    it('sets the balance to a specific value', () =>
    {
        balanceModel.setBalance(200);

        expect(balanceModel.getBalance()).toBe(200);
    });

    it('rejects invalid balance assignments', () =>
    {
        expect(() => balanceModel.setBalance(-50)).toThrow('Balance cannot be negative.');
        expect(() => balanceModel.setBalance(Number.POSITIVE_INFINITY)).toThrow('Balance must be a finite number.');
    });

    it('adds a positive amount to the balance', () =>
    {
        balanceModel.setBalance(100);
        balanceModel.add(50);

        expect(balanceModel.getBalance()).toBe(150);
    });

    it('throws when adding zero or a negative amount', () =>
    {
        expect(() => balanceModel.add(0)).toThrow('Amount to add must be greater than zero.');
        expect(() => balanceModel.add(-50)).toThrow('Amount to add must be greater than zero.');
        expect(() => balanceModel.add(Number.NaN)).toThrow('Amount to add must be greater than zero.');
    });

    it('subtracts a positive amount from the balance', () =>
    {
        balanceModel.setBalance(100);
        balanceModel.subtract(50);

        expect(balanceModel.getBalance()).toBe(50);
    });

    it('throws when subtracting zero, a negative amount, or unaffordable value', () =>
    {
        balanceModel.setBalance(100);

        expect(() => balanceModel.subtract(0)).toThrow('Amount to subtract must be greater than zero.');
        expect(() => balanceModel.subtract(-50)).toThrow('Amount to subtract must be greater than zero.');
        expect(() => balanceModel.subtract(200)).toThrow('Insufficient balance.');
    });

    it('returns affordability state for valid amounts', () =>
    {
        balanceModel.setBalance(50);

        expect(balanceModel.canAfford(100)).toBe(false);
        expect(balanceModel.canAfford(50)).toBe(true);
        expect(balanceModel.canAfford(0)).toBe(true);
    });

    it('returns false for invalid affordability checks', () =>
    {
        expect(balanceModel.canAfford(Number.NaN)).toBe(false);
        expect(balanceModel.canAfford(Number.POSITIVE_INFINITY)).toBe(false);
    });
});
