import BaseMachine from '../slotMachines/BaseMachine';
import { CanPlayIdle, Entity, Updatable } from '../types/Entity';
import AnimationProgressHook from '../utils/hooks/AnimationProgressHook';
import HooksChain from '../utils/hooks/HooksChain';
import BaseSystem from './BaseSystem';

export type IdleSystemCompatibleEntity = Entity & Updatable & CanPlayIdle;

export interface IdleSystemSettings
{
    maxAnimatedSymbols: number;
}

/**
 * @class IdleSystem
 * @extends BaseSystem
 * @description Manages idle animations for random symbols in the slot machine.
 * When the system is idle, it triggers an idle animation on a randomly chosen symbol.
 */
export default class IdleSystem extends BaseSystem<IdleSystemSettings>
{
    /** Required flags for the system to operate. */
    static override requiredFlags: string[] = [];

    /** Required maps for the system to operate. */
    static override requiredMaps: string[] = ['symbols'];

    /** Required inputs for the system to operate. */
    static override requiredInputs: string[] = [];

    /** Required methods that `machine` should implement. */
    static override requiredMethods: string[] = ['getChildByName'];

    /** Required utilities for the system to operate. */
    static override requiredUtils: string[] = [];

    /**
     * A hook for tracking the progress of the current animation.
     * @private
     */
    private animationHooks: HooksChain[] = [];

    private animatedSymbols: string[];

    static defaultSettings: IdleSystemSettings = {
        maxAnimatedSymbols: 3,
    };

    /**
     * @class
     * @param {BaseMachine} machine - The slot machine instance.
     */
    constructor(machine: BaseMachine)
    {
        super(machine);
        this.setSettings(IdleSystem.defaultSettings);
    }

    /** Called when the system is activated. Resets the animation progress hook. */
    override onEnter(): void
    {
        this.animatedSymbols = [];
        this.animationHooks = [];
    }

    /**
     * Updates the system, checking the progress of the current animation.
     * If no animation is in progress or the current animation is complete, triggers a new animation.
     * @param dt
     */
    override update(dt: number): void
    {
        this.updateHooks(dt);
        this.handleAnimatedSymbols();
    }

    private updateHooks(dt: number)
    {
        this.animationHooks = this.animationHooks.filter((hook) =>
        {
            hook.update(dt);

            return !hook.isComplete();
        });
    }

    private handleAnimatedSymbols()
    {
        if (this.animatedSymbols.length < this.settings.maxAnimatedSymbols)
        {
            this._animateRandomSymbol();
        }
    }

    /**
     * Selects a random symbol from the machine and triggers its idle animation.
     * @private
     */
    private _animateRandomSymbol()
    {
        const { reels, slots } = this.machine.config;
        const randomIndex = Math.floor(Math.random() * reels * slots);
        const name = this.machine.maps.symbols[randomIndex];
        const symbol: IdleSystemCompatibleEntity = this.machine.getChildByName(name);

        if (!symbol)
        {
            throw new Error(`Symbol with name ${name} not found.`);
        }
        if (this.animatedSymbols.includes(symbol.name)) return;
        this.animatedSymbols.push(symbol.name);
        this.animationHooks.push(
            new HooksChain()
                .addHook(new AnimationProgressHook(() => symbol.idle()))
                .setOnComplete(() =>
                {
                    const index = this.animatedSymbols.indexOf(symbol.name);

                    if (index < 0) return;
                    this.animatedSymbols.splice(index, 1);
                })
        );
    }

    onLeave(): void
    {
    }
}
