import { system } from "@minecraft/server";
import { sleep } from "./Time.js";

class StateMachine {
    constructor(config) {
        /**@private*/
        this._config = config;
        /**@private*/
        this._currentState = config.initialState;
        /**@private*/
        this._running = true;

        this._runMachine();
    }

    /** @private */
    async _runMachine() {
        while (this._running) {
            try {
                const state = this._config.states[this._currentState];
                this.stateContext = state;

                // Call onEntry if it exists
                if (state.onEntry) await state.onEntry.call(this);

                // Ensure that transitions exist
                if (!state.transitions) return;

                // Create an array of promises for the transitions
                const nextState = await new Promise((resolve) => {
                    const transitions = state.transitions.map(transition => {
                        // Ensure that transition.condition is invoked as a function
                        return transition.condition().then((result) => {
                            if (!result) {
                                resolve(null);  // Resolve with null if condition is false
                            } else {
                                return transition.nextState;
                            }
                        });
                    });

                    // Use Promise.race to resolve the first transition that completes
                    Promise.race(transitions).then((nextState) => {
                        resolve(nextState);
                    }).catch(error => {
                        console.error('Error during state transition:', error);
                        resolve(null);  // Stay in the current state on error
                    });
                });

                // If the next state is null, break out of the loop
                if (!nextState) {
                    this.destroy();
                    return;
                }

                // Call onExit if it exists
                if (state.onExit) await state.onExit.call(this);

                if (!this._running) return;

                // Update the current state
                this._currentState = nextState;
            } catch (error) {
                return;
            }
        }
    }

    stop() {
        this._running = false;
        this._currentState = null;
        this._config = null;
    }
}

/**
 * Run a new statemachine
 * @param {StateMachineConfig} config 
 * @returns {StateMachine}
 */
export function createMachine(config) {
    return new StateMachine(config);
}

export function condition(condition) {
    return () => {
        return new Promise((resolve) => {
            const handler = system.runInterval(() => {
                try {
                    if (condition()) {
                        system.clearRun(handler);
                        resolve(true);
                    }
                } catch (e) {
                    system.clearRun(handler);
                    resolve(false);
                }
            });
        });
    }
}

export function delay(delay) {
    return () => {
        return new Promise((resolve) => sleep(delay).then(() => {
            resolve(true)
        }))
    }
}

export function delayedCondition(condition, delay) {
    return () => {
        return new Promise((resolve) => {
            sleep(delay).then(() => {
                const handler = system.runInterval(() => {
                    try {
                        if (condition()) {
                            system.clearRun(handler);
                            resolve(true);
                        }
                    } catch (e) {
                        system.clearRun(handler);
                        resolve(false);
                    }
                });
            });
        });
    }
}



/**
 * @typedef {Object} StateMachine
 * @property {() => void} stop - Stop the state machine
 */



/**
 * @typedef {Object} StateMachineConfig
 * @property {string} initialState
 * @property {Object.<string, StateMachineState>} states
 */

/**
 * @typedef {Object} StateMachineState
 * @property {() => Promise<void>} onEntry
 * @property {() => Promise<void>} onExit
 * @property {StateMachineTransition[]} transitions
 */

/**
 * @typedef {Object} StateMachineTransition
 * @property {string} nextState
 * @property {Promise<boolean>} condition
 */




/**
const machine = createMachine({
    initialState: 'default',
    states: {
        default: {
            onEntry: async () => {
                console.warn('default state');
            },
            transitions: [
                {
                    nextState: 'end',
                    condition: new Promise((resolve) => { resolve(true); })
                },
                {
                    nextState: 'tutorial',
                    condition: new Promise((resolve) => {
                        system.runTimeout(() => {
                            resolve(true);
                        }, 100);
                    })
                }
            ]
        },
        tutorial: {
            onEntry: async () => {
                console.warn('tutorial started');
            },
            transitions: [
                {
                    nextState: 'end',
                    condition: new Promise((resolve) => { resolve(true); })
                }
            ]
        },
        end: {
            onEntry: async () => {
                console.warn('entered end');
            }
        }
    }
});
 */