import { SCENES } from '../../constants/constants';
import BackendError from '../../errors/BackendError';
import Locator from '../../services/Locator';
import { saveLastSetUserBet } from '../../utils/utils';
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';

/** Main/base slot mode that plays regular spins. */
export default class MainMode extends Mode
{
    public constructor(betConfig: ModeBetConfig, gameData: BaseGameData, lines: Lines | LinesConfig)
    {
        super('MainMode', betConfig, gameData, lines);
        this._betFeatureActive = Locator.storage.get<boolean>('betFeatureActive') || false;
        Locator.storage.set('lastUserBet', this.betModel.bet);
    }

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

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

        this._isRoundStarted = true;
        this.resetWin();
        this.resetTotalWin();
        this.lockBonusBuy();
        this.lockBets();
        this.lockAutoGame();
        this._gameData = null as unknown as BaseGameData;
        this.autoGame?.startSpin();

        saveLastSetUserBet(this.bet, false);

        const rawData = await (this._betFeatureActive
            ? Locator.network.wildSpin(this.bet)
            : Locator.network.bet(this.bet)) as SpinResp | undefined;

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

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

        balanceModel.setBalance(rawData.balance.wallet);

        this.saveResponseToStorage(rawData);
        this._gameData = Mode.createGameData(rawData);
    }

    public override async buyFreespins(balanceModel: BalanceModel, multiplier = 1): Promise<void>
    {
        this._isRoundStarted = true;
        this.lockBonusBuy();
        this.resetWin();
        this.resetTotalWin();
        this.lockBets();
        balanceModel.subtract(this.bet * multiplier);
        saveLastSetUserBet(this.bet, false);

        this._gameData = null as unknown as BaseGameData;
        const rawData = await Locator.network.buyFreespins(this.bet) 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);
        this.saveResponseToStorage(rawData);
        balanceModel.setBalance(rawData.balance.wallet);
    }

    public override async buyBonus(balanceModel: BalanceModel, multiplier = 1): Promise<void>
    {
        this._isRoundStarted = true;
        this.lockBonusBuy();
        this.resetWin();
        this.resetTotalWin();
        this.lockBets();
        balanceModel.subtract(this.bet * multiplier);
        this._gameData = null as unknown as BaseGameData;

        const rawData = await Locator.network.buyBonus(this.bet) 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);
        this.saveResponseToStorage(rawData);
        balanceModel.setBalance(rawData.balance.wallet);
    }

    /** Exposes whether the bonus chance stake is active. */
    public override get betFeatureActive(): boolean
    {
        return this._betFeatureActive;
    }

    /** Persists the bonus chance toggle state. */
    public override set betFeatureActive(value: boolean)
    {
        this._betFeatureActive = value;
        Locator.storage.set('betFeatureActive', value);
    }

    public override toggleBetFeatureActive(): void
    {
        this.betFeatureActive = !this.betFeatureActive;
    }

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

    public override increaseBet(): void
    {
        super.increaseBet();
        saveLastSetUserBet(this.betModel.bet, true);
    }

    public override decreaseBet(): void
    {
        super.decreaseBet();
        saveLastSetUserBet(this.betModel.bet, true);
    }

    public override setToMaxBet(): void
    {
        super.setToMaxBet();
        saveLastSetUserBet(this.betModel.bet, true);
    }

    public override setBetByValue(value: number): void
    {
        super.setBetByValue(value);
        saveLastSetUserBet(this.betModel.bet, true);
    }
}
