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 CasinoRoundsIndicator
{
    issued: number;
    left: number;
}

/** Casino rounds mode accumulating wins until all rounds finish. */
export default class CasinoRoundsMode extends Mode
{
    private readonly _casinoRoundsIndicatorData: CasinoRoundsIndicator;

    private _freeRoundsTotalWin: number;

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

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

        this._casinoRoundsIndicatorData = {
            issued: gameData.casinoRounds.issued,
            left: gameData.casinoRounds.left,
        };

        this._totalWin = 0;
        this._freeRoundsTotalWin = gameData.freeRoundsTotalWin;
    }

    public override get totalWin(): number
    {
        if (this._totalWin > 0)
        {
            return this._totalWin;
        }

        return this._freeRoundsTotalWin;
    }

    public override set totalWin(totalWin: number)
    {
        this._freeRoundsTotalWin = totalWin;
    }

    public override get translatedTotalWin(): number
    {
        return this._freeRoundsTotalWin;
    }

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

    public override get casinoRoundsIndicatorData(): CasinoRoundsIndicator
    {
        return this._casinoRoundsIndicatorData;
    }

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

    public override onEnter(): void
    {
        this._totalWin = 0;
    }

    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.lockAutoGame();
        this.casinoRoundsIndicatorData.left -= 1;
        this.autoGame?.startSpin();
        const rawData = await Locator.network.bet(this.bet, true) 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);
        balanceModel.setBalance(rawData.balance.wallet);
        this.saveResponseToStorage(rawData);
    }

    public override afterRoundEnding(balanceModel: BalanceModel): void
    {
        this.checkAutoGame(balanceModel);
        this.resetWin();
        this.unlockAutoGame();
    }

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

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

    public override shouldAutoSpin(): boolean
    {
        return this.autoGame?.isActive() ?? false;
    }

    public override addWinAmount(value: number): void
    {
        super.addWinAmount(value);

        if (this.gameData.isBonusGameTriggered)
        {
            this._totalWin += value;
        }
        else
        {
            this._freeRoundsTotalWin += value;
        }
    }

    public override isExitConditionMet(): boolean
    {
        return this._gameData.casinoRounds.left === 0;
    }
}
