import { SCENES } from '../../constants/constants';
import BackendError from '../../errors/BackendError';
import Locator from '../../services/Locator';
import Mode, { ModeBetConfig } from './Mode';

import type { SpinResp } from '../../types/api';
import type BalanceModel from '../BalanceModel';
import type BaseGameData from '../BaseGameData';
import type Lines from '../Lines';
import type { LinesConfig } from '../Lines';

interface BonusIndicator
{
    title: string;
    issued: number;
    left: number;
}

/** Bonus mode for respin rounds. */
export default class RespinsMode extends Mode
{
    private readonly _bonusIndicatorData: BonusIndicator;

    public constructor(betConfig: ModeBetConfig, gameData: BaseGameData, lines: Lines | LinesConfig)
    {
        super('RespinsMode', betConfig, gameData, lines);

        this.betModel.unlock();
        this.betModel.setBetByValue(betConfig.bets[0]);
        this.betModel.lock();

        this._bonusIndicatorData = {
            title: 'respins_label',
            issued: gameData.bonusSpins.issued,
            left: gameData.bonusSpins.left,
        };

        this._totalWin = gameData.gameTotalWin - gameData.winInRound;
    }

    public override get scene(): string
    {
        return SCENES.RESPINS_GAME_SCENE;
    }

    public override get bonusIndicatorData(): BonusIndicator
    {
        return this._bonusIndicatorData;
    }

    public override isBonusMode(): boolean
    {
        return true;
    }

    public override async startRound(balanceModel: BalanceModel): Promise<void>
    {
        if (this._isRoundStarted)
        {
            return;
        }

        this._isRoundStarted = true;
        this._gameData = null as unknown as BaseGameData;
        this.resetWin();
        this._bonusIndicatorData.left -= 1;
        const rawData = await Locator.network.respin() as SpinResp | undefined;

        if (!rawData)
        {
            throw new Error('network problems');
        }

        if (rawData.errors?.length)
        {
            throw new BackendError('backend errors', this.mapErrors(rawData.errors));
        }

        this._gameData = Mode.createGameData(rawData);

        if (!(this._gameData.bonusSpins.left === 0 && this._gameData.isCasinoRoundsActive && __OPTIONS__.freespinsType === 'transactional'))
        {
            balanceModel.setBalance(rawData.balance.wallet);
        }

        this.saveResponseToStorage(rawData);
    }

    public override endRound(balanceModel: BalanceModel): void
    {
        this._bonusIndicatorData.left = this._gameData.bonusSpins.left;
        this._bonusIndicatorData.issued = this._gameData.bonusSpins.issued;
        super.endRound(balanceModel);
    }

    public override afterRoundEnding(_balanceModel: BalanceModel): void {}

    public override canBuyBonus(): boolean
    {
        return false;
    }

    public override unlockBets(): void
    {
        throw new Error('Bets cannot be unlocked in Respins Mode.');
    }

    public override canStartAutoGame(): boolean
    {
        return false;
    }

    public override shouldAutoSpin(): boolean
    {
        return !this._gameData.isRespinsGameRestored;
    }

    public override addWinAmount(value: number): void
    {
        super.addWinAmount(value);
        this._totalWin += value;
    }

    public override isExitConditionMet(): boolean
    {
        if (this.gameData.isWinLimitReached)
        {
            return true;
        }

        return this.gameData.isRespinsGameFinished;
    }
}
