import type { AutoGameSettingsPayload, BetOption, SlotModel } from './types';
import Locator from "../services/Locator";
import {ANALYTICS_EVENTS} from "../services";

/** View model exposing formatted values for the main HUD. */
export default class UIViewModel
{
    private readonly model: SlotModel;

    private _isSpinButtonPressed = false;

    private _isSpinButtonHold = false;

    private _isMinusButtonPressed = false;

    private _isPlusButtonPressed = false;

    private _isMaxButtonPressed = false;

    private _isTurboButtonPressed = false;

    private _isSoundOnButtonPressed = false;

    private _isSoundOffButtonPressed = false;

    private _isAutoGameSettingsModeButtonPressed = false;

    private _isBuyBonusButtonPressed = false;

    private _isHomeButtonPressed = false;

    private _isPaytableButtonPressed = false;

    private _isRulesButtonPressed = false;

    private _isAutoGameButtonPressed = false;

    private _isStopAutoGameButtonPressed = false;

    private _isSettingsButtonPressed = false;

    private _isEnableSpacebarButtonPressed = false;

    private _isMusicButtonPressed = false;

    private _isSFXButtonPressed = false;

    private _isVolumeChanging = false;

    private _isUserInputLocked = false;

    private _lastSpinClickTime = 0;
    private static readonly DOUBLE_CLICK_THRESHOLD_MS = 300;

    private _bets: number[] = [];

    private _formattedBets: BetOption[] = [];

    private _betFeatureActive?: boolean;

    private _autoRoundsIssued = 0; // should be number (Infinity is option)

    private _spinBtnHoldingTimeoutId?: ReturnType<typeof setTimeout>;

    public isAutoGameBlockedByBO() {
        return this.model.isAutoGameBlockedByBO();
    }

    public isAutoGameSettingsShown = false;

    public isAdvancedAutoGameSettings = false;

    public isAnyWinChecked = false;

    public isSingleWinExceedsChecked = false;

    public isBonusWonChecked = false;

    public isBalanceIncreaseChecked = false;

    public isBalanceDecreaseChecked = false;

    public winExceedsTextField = '';

    public balanceIncreaseTextField = '';

    public balanceDecreaseTextField = '';

    public isSettingsShown = false;

    constructor(model: SlotModel)
    {
        this.model = model;
    }

    get isUserInputLocked(): boolean
    {
        return this._isUserInputLocked;
    }

    set isUserInputLocked(isLocked: boolean)
    {
        this._isUserInputLocked = Boolean(isLocked);
    }

    get title(): string
    {
        return this.model.title || 'unnamed game';
    }

    get balance(): string
    {
        return this.model.currency.getFormattedValue(this.model.balance);
    }

    get currency()
    {
        return this.model.currency;
    }

    get win(): string | null
    {
        return !this.model.win
            ? null
            : this.model.currency.getFormattedValue(this.model.win);
    }

    set win(value: number | null)
    {
        this.model.win = value;
    }

    get totalWin(): string | null
    {
        return !this.model.totalWin
            ? null
            : this.model.currency.getFormattedValue(this.model.totalWin);
    }

    get isRoundStarted(): boolean
    {
        return this.model.isRoundStarted();
    }

    get isSpinButtonPressed(): boolean
    {
        return this._isSpinButtonPressed;
    }

    set isSpinButtonPressed(value: boolean)
    {
        if (!this._isSpinButtonPressed && value)
        {
            const now = Date.now();
            const interval = now - this._lastSpinClickTime;

            if (interval > 0 && interval < UIViewModel.DOUBLE_CLICK_THRESHOLD_MS)
            {
                Locator.analytics.track(ANALYTICS_EVENTS.SPIN_BUTTON_DOUBLE_CLICK, {
                    intervalMs: interval,
                });
            }

            this._lastSpinClickTime = now;

            this.model.performSpinButtonAction();
            this._bets = [];
            this._startHoldingTimeout();
            this.isSettingsShown = false;
        }
        else if (!value)
        {
            this.isSpinButtonHold = false;
            this.model.performSpinButtonReleaseUp();
            this._resetHoldingTimeout();
        }

        this._isSpinButtonPressed = value;
    }

    get isSpinButtonHold(): boolean
    {
        return this._isSpinButtonHold;
    }

    set isSpinButtonHold(value: boolean)
    {
        if (!this._isSpinButtonHold && value)
        {
            this.model.handleSpinButtonHolding();
        }
        else if (this._isSpinButtonHold && !value)
        {
            this.model.handleSpinButtonReleasing();
        }

        this._isSpinButtonHold = value;
    }

    get canBuyBonus(): boolean
    {
        return this.model.canBuyBonus();
    }

    get isBuyBonusButtonPressed(): boolean
    {
        return this._isBuyBonusButtonPressed;
    }

    set isBuyBonusButtonPressed(value: boolean)
    {
        if (this._isBuyBonusButtonPressed && !value)
        {
            this.model.performBuyBonusButtonAction();
        }

        this._isBuyBonusButtonPressed = value;
    }

    get bet(): string
    {
        const stake = this.model.resolveFeatureStake(this.model.bet);

        return this.model.currency.getFormattedValue(stake);
    }

    set bet(index: number)
    {
        this.model.setBetByValue(this._bets[index]);
        this._bets = [];
    }

    get bets(): BetOption[]
    {
        const isBetMultiplier = this.model.betFeatureActive;

        if (this._betFeatureActive !== isBetMultiplier)
        {
            this._bets = [];
        }
        this._betFeatureActive = isBetMultiplier;

        if (this.model.bets !== this._bets)
        {
            this._bets = this.model.bets;
            this._formattedBets = this.model.bets.map((bet) =>
            {
                const stake = this.model.resolveFeatureStake(bet);

                return {
                    value: this.model.currency.getFormattedValue(stake),
                    selected: bet === this.model.bet,
                    available: stake <= this.model.balance
                };
            });
        }

        return this._formattedBets;
    }

    get isMinusButtonPressed(): boolean
    {
        return this._isMinusButtonPressed;
    }

    set isMinusButtonPressed(value: boolean)
    {
        if (this._isMinusButtonPressed && !value)
        {
            this.model.decreaseBet();
            Locator.analytics.track(ANALYTICS_EVENTS.BET_SIZE_REDUCED, {
                totalBet: this.model.bet
            });
            this._bets = [];
        }

        this._isMinusButtonPressed = value;
    }


    get isMinBet(): boolean
    {
        return this.model.bets.indexOf(this.model.bet) === 0;
    }

    get isPlusButtonPressed(): boolean
    {
        return this._isPlusButtonPressed;
    }

    set isPlusButtonPressed(value: boolean)
    {
        if (this._isPlusButtonPressed && !value)
        {
            this.model.increaseBet();
            Locator.analytics.track(ANALYTICS_EVENTS.BET_SIZE_INCREASED, {
                totalBet: this.model.bet
            });
            this._bets = [];
        }

        this._isPlusButtonPressed = value;
    }

    get isMaxBet(): boolean
    {
        const betIndex = this.model.bets.indexOf(this.model.bet);
        const maxIndex = this.model.bets.length - 1;
        const isLastBet = betIndex === maxIndex;

        if (isLastBet) return true;

        const isNextBetMoreThenBalance = this.model.bets[betIndex + 1] > this.model.balance;

        if (isNextBetMoreThenBalance) return true;

        return false;
    }

    get isMaxButtonPressed(): boolean
    {
        return this._isMaxButtonPressed;
    }

    set isMaxButtonPressed(value: boolean)
    {
        if (this._isMaxButtonPressed && !value)
        {
            this.model.setBetByValue(Math.max(...this.model.bets.filter((bet) => bet <= this.model.balance)));
            Locator.analytics.track(ANALYTICS_EVENTS.MAX_BET_SELECTED, {
                totalBet: this.model.bet
            });
            this._bets = [];
        }

        this._isMaxButtonPressed = value;
    }


    get areBetsLocked(): boolean
    {
        return this.model.areBetsLocked();
    }

    get isTurboModeActive(): boolean
    {
        return this.model.turboMode;
    }

    get isTurboButtonPressed(): boolean
    {
        return this._isTurboButtonPressed;
    }

    set isTurboButtonPressed(value: boolean)
    {
        if (this._isTurboButtonPressed && !value)
        {
            this.model.turboMode = !this.model.turboMode;
            Locator.analytics.track(ANALYTICS_EVENTS.TURBOSPIN_CHANGED, {
                enabled: this.model.turboMode
            });
        }

        this._isTurboButtonPressed = value;
    }

    get isCasinoRoundsActive(): boolean
    {
        return !this.model.waitingForModeSwitch && this.model.currentMode.isCasinoRoundsMode();
    }

    get casinoRoundsIssued(): number
    {
        return this.model.currentMode.casinoRoundsIndicatorData.issued;
    }

    get casinoRoundsLeft(): number
    {
        return this.model.currentMode.casinoRoundsIndicatorData.left;
    }

    get bonusGameActive(): boolean
    {
        return !this.model.waitingForModeSwitch && this.model.currentMode.isBonusMode();
    }

    get bonusGameIndicatorTitle(): string
    {
        return this.model.currentMode.bonusIndicatorData.title;
    }

    get bonusGameIndicatorIssued(): number
    {
        return this.model.currentMode.bonusIndicatorData.issued;
    }

    get bonusGameIndicatorLeft(): number
    {
        return this.model.currentMode.bonusIndicatorData.left;
    }

    get betFeatureActive(): boolean
    {
        return this.model.betFeatureActive;
    }

    get isBuyButtonLocked(): boolean
    {
        return false;
    }

    get isMute(): boolean
    {
        return this.model.isMute();
    }

    get isSoundOnButtonPressed(): boolean
    {
        return this._isSoundOnButtonPressed;
    }

    set isSoundOnButtonPressed(value: boolean)
    {
        if (this._isSoundOnButtonPressed && !value)
        {
            this.model.muteAll();
            Locator.analytics.track(ANALYTICS_EVENTS.VOLUME_TOGGLED_MAIN, {
                muted: true,
            });
        }

        this._isSoundOnButtonPressed = value;
    }

    get isSoundOffButtonPressed(): boolean
    {
        return this._isSoundOffButtonPressed;
    }


    set isSoundOffButtonPressed(value: boolean)
    {
        if (this._isSoundOffButtonPressed && !value)
        {
            this.model.unmuteAll();
            Locator.analytics.track(ANALYTICS_EVENTS.VOLUME_TOGGLED_MAIN, {
                muted: false,
            });
        }

        this._isSoundOffButtonPressed = value;
    }

    get isHomeButtonPressed(): boolean
    {
        return this._isHomeButtonPressed;
    }

    set isHomeButtonPressed(value: boolean)
    {
        if (!this._isHomeButtonPressed && value)
        {
            this.model.performHomeButtonAction();
        }

        this._isHomeButtonPressed = value;
    }

    get isLobbyLink(): string | undefined
    {
        const returnAction = __OPTIONS__?.actions?.return as { link?: string } | undefined;

        return returnAction?.link;
    }

    get isPaytableButtonPressed(): boolean
    {
        return this._isPaytableButtonPressed;
    }

    set isPaytableButtonPressed(value: boolean)
    {
        if (this._isPaytableButtonPressed && !value)
        {
            this.model.performPaytableAction();
            Locator.analytics.track(ANALYTICS_EVENTS.PAYTABLE_OPENED);
        }

        this._isPaytableButtonPressed = value;
    }

    get isRulesButtonPressed(): boolean
    {
        return this._isRulesButtonPressed;
    }

    set isRulesButtonPressed(value: boolean)
    {
        if (this._isRulesButtonPressed && !value)
        {
            this.model.performRulesAction();
            Locator.analytics.track(ANALYTICS_EVENTS.RULES_OPENED);
        }

        this._isRulesButtonPressed = value;
    }

    get isAutoGameAvailable(): boolean
    {
        return !this.isAutoGameBlockedByBO() && this.model.canStartAutoGame();
    }

    get isAutoGameActive(): boolean
    {
        return this.model.isAutoGameActive();
    }

    get autoRoundsLeft(): number
    {
        return this.model.autoRoundsLeft;
    }

    get isAutoGameSettingsModeButtonPressed(): boolean
    {
        return this._isAutoGameSettingsModeButtonPressed;
    }

    set isAutoGameSettingsModeButtonPressed(value: boolean)
    {
        if (this._isAutoGameSettingsModeButtonPressed && !value)
        {
            this.isAdvancedAutoGameSettings = !this.isAdvancedAutoGameSettings;
        }

        this._isAutoGameSettingsModeButtonPressed = value;
    }

    get isStopAutoGameButtonPressed(): boolean
    {
        return this._isStopAutoGameButtonPressed;
    }

    set isStopAutoGameButtonPressed(value: boolean)
    {
        if (this._isStopAutoGameButtonPressed && !value)
        {
            this.model.stopAutoGame();
        }

        this._isStopAutoGameButtonPressed = value;
    }

    get isAutoGameButtonPressed(): boolean
    {
        return this._isAutoGameButtonPressed;
    }

    set isAutoGameButtonPressed(value: boolean)
    {
        if (this._isAutoGameButtonPressed && !value)
        {
            this.isAutoGameSettingsShown = !this.isAutoGameSettingsShown;
        }

        this._isAutoGameButtonPressed = value;
    }

    get autoRoundsIssued(): number
    {
        return this._autoRoundsIssued;
    }

    set autoRoundsIssued(value: number)
    {
        this._autoRoundsIssued = value;
        this.model.performAutoGame(this._getAutoGameSettings());
        this.isAutoGameSettingsShown = false;

        Locator.analytics.track(ANALYTICS_EVENTS.AUTOPLAY_STARTED, {
            rounds: value,
            balance: this.model.balance,
            bet: this.model.bet,
        });
    }

    get isSettingsButtonPressed(): boolean
    {
        return this._isSettingsButtonPressed;
    }

    set isSettingsButtonPressed(value: boolean)
    {
        if (this._isSettingsButtonPressed && !value)
        {
            this.isSettingsShown = !this.isSettingsShown;
        }

        this._isSettingsButtonPressed = value;
    }

    get isEnableSpaceBarButtonPressed(): boolean
    {
        return this._isEnableSpacebarButtonPressed;
    }

    set isEnableSpaceBarButtonPressed(value: boolean)
    {
        if (this._isEnableSpacebarButtonPressed && !value)
        {
            this.model.toggleSpaceBarHandling();
        }

        this._isEnableSpacebarButtonPressed = value;
    }

    get isSpaceBarHandlingActive(): boolean
    {
        return this.model.isSpaceBarHandlingActive;
    }

    get isMusicButtonPressed(): boolean
    {
        return this._isMusicButtonPressed;
    }

    set isMusicButtonPressed(value: boolean)
    {
        if (this._isMusicButtonPressed && !value)
        {
            this.model.toggleMusicMute();
        }

        this._isMusicButtonPressed = value;
    }

    get isMusicMute(): boolean
    {
        return this.model.isMusicMute();
    }

    get isSFXButtonPressed(): boolean
    {
        return this._isSFXButtonPressed;
    }

    set isSFXButtonPressed(value: boolean)
    {
        if (this._isSFXButtonPressed && !value)
        {
            this.model.toggleSFXMute();
        }

        this._isSFXButtonPressed = value;
    }

    get isSFXMute(): boolean
    {
        return this.model.isSFXMute();
    }

    get volume(): number
    {
        return this.model.getVolume();
    }

    set volume(value: number)
    {
        this.model.setVolume(value);
    }

    set isVolumeChanging(value: boolean)
    {
        if (this._isVolumeChanging && !value)
        {
            this.model.saveVolume();
        }

        this._isVolumeChanging = value;
    }

    private _getAutoGameSettings(): AutoGameSettingsPayload
    {
        const advanced = !this.isAdvancedAutoGameSettings
            ? {}
            : {
                anyWin: this.isAnyWinChecked,
                bonusGameWon: this.isBonusWonChecked,
                singleWin: {
                    enabled: this.isSingleWinExceedsChecked,
                    threshold: Number(this.winExceedsTextField) * this.model.currency.subunits,
                },
                balanceIncrease: {
                    enabled: this.isBalanceIncreaseChecked,
                    threshold: Number(this.balanceIncreaseTextField) * this.model.currency.subunits,
                },
                balanceDecrease: {
                    enabled: this.isBalanceDecreaseChecked,
                    threshold: Number(this.balanceDecreaseTextField) * this.model.currency.subunits,
                }
            };

        return {
            issued: this._autoRoundsIssued,
            startingBalance: this.model.balance,
            advanced
        };
    }

    private _startHoldingTimeout(): void
    {
        this._spinBtnHoldingTimeoutId = setTimeout(() =>
        {
            this.isSpinButtonHold = true;
        }, 500);
    }

    private _resetHoldingTimeout(): void
    {
        if (!this._spinBtnHoldingTimeoutId) return;

        clearTimeout(this._spinBtnHoldingTimeoutId);
        this._spinBtnHoldingTimeoutId = undefined;
    }
}
