import PaytableModel, {
    type PaytableData,
    type PaytableMachineConfig,
} from './PaytableModel';
import {
    describe,
    expect,
    it,
} from '@jest/globals';

describe('PaytableModel', () =>
{
    const machineConfig: PaytableMachineConfig = {
        symbolsKeys: ['A', 'K', 'W', 'S'],
        wilds: ['W'],
        scatters: ['S'],
    };

    const paytableData: PaytableData = [
        [0, 2, 5],
        [0, 4, 10],
        [0, 0, 0],
        [0, 0, 0],
    ];

    it('exposes machine metadata', () =>
    {
        const model = new PaytableModel(paytableData, 10, machineConfig);

        expect(model.symbols).toEqual(['A', 'K', 'W', 'S']);
        expect(model.wildSymbols).toEqual(['W']);
        expect(model.scatterSymbols).toEqual(['S']);
        expect(model.betDivider).toBe(10);
    });

    it('returns cloned payout rows', () =>
    {
        const model = new PaytableModel(paytableData, 20, machineConfig);

        const payouts = model.get('A');

        payouts.push(99);

        expect(model.get('A')).toEqual([0, 2, 5]);
        expect(payouts).toEqual([0, 2, 5, 99]);
    });

    it('throws for missing symbols', () =>
    {
        const model = new PaytableModel(paytableData, 10, machineConfig);

        expect(() => model.get('Z')).toThrow('Element Z is not found in the array of symbols.');
    });

    it('requires paytable rows for each symbol', () =>
    {
        const config: PaytableMachineConfig = {
            symbolsKeys: ['A', 'K'],
        };

        const rows: PaytableData = [[0, 1, 2]];

        expect(() => new PaytableModel(rows, 10, config)).toThrow(
            'Paytable data count must match machine config symbols count.',
        );
    });

    it('validates payout entries', () =>
    {
        const config: PaytableMachineConfig = {
            symbolsKeys: ['A'],
        };

        const rows = [[0, Number.NaN, 5]] as unknown as PaytableData;

        expect(() => new PaytableModel(rows, 10, config)).toThrow(
            'Paytable row 0 has invalid payout at index 1.',
        );
    });

    it('requires a positive bet divider', () =>
    {
        const config: PaytableMachineConfig = {
            symbolsKeys: ['A'],
        };

        expect(() => new PaytableModel([[0, 1, 2]], 0, config)).toThrow(
            'betDivider must be a positive finite number.',
        );
    });

    it('rejects duplicates and unknown specials', () =>
    {
        const duplicateConfig: PaytableMachineConfig = {
            symbolsKeys: ['A', 'A'],
        };

        expect(() => new PaytableModel([[0], [1]], 5, duplicateConfig)).toThrow(
            'Machine config symbols must be unique.',
        );

        const specialsConfig: PaytableMachineConfig = {
            symbolsKeys: ['A'],
            wilds: ['W'],
        };

        expect(() => new PaytableModel([[0]], 5, specialsConfig)).toThrow(
            'wilds symbol W must exist in symbolsKeys.',
        );
    });
});
