const bossCoroutines = new Map(); // Map to track coroutines by boss ID
const recallCallbacks = new Map();

/**
 * Starts a coroutine function associated with a specific boss ID.
 * @param {Function} func - Generator function to run as a coroutine.
 * @param {Function|null} [recall=null] - Optional callback function to execute after coroutine completion.
 * @param {string} bossId - The ID of the boss associated with this coroutine.
 */
export function startCoroutineForBoss(func, recall = null, bossId) {
    Coroutine.start(func, recall, bossId);
}

/**
 * Stops all coroutines associated with a specific boss.
 * @param {string} bossId - The ID of the boss whose coroutines should be stopped.
 */
export function stopBossCoroutines(bossId) {
    if (bossCoroutines.has(bossId)) {
        for (const coroutine of bossCoroutines.get(bossId)) {
            //console.warn(`Deleting coroutine for boss ${bossId}: ${coroutine}`);
            Coroutine.stop(coroutine, bossId);
        }
        bossCoroutines.delete(bossId); // Clean up after stopping
    }
}

export class Coroutine {
    /**
     * Processes active coroutines for a specific boss, updating their state or stopping them if completed or errored.
     * @param {string} bossId - The ID of the boss to process coroutines for.
     */
    static tickForBoss(bossId) {
        //console.warn(bossId)
        if (!bossCoroutines.has(bossId)) return;

        const endedCoroutines = [];
        const coroutines = bossCoroutines.get(bossId);

        for (const [key, state] of coroutines) {
            // Check if we need to decrement a delay counter
            if (state.value > 0) {
                state.value -= 1;
                continue;
            }

            // Process coroutine and check if it should end
            try {
                const result = key.next();
                if (result.done) {
                    endedCoroutines.push(key); // Mark for cleanup if done
                } else {
                    coroutines.set(key, { value: result.value || 0, done: false });
                }
            } catch (error) {
                //console.warn(`Coroutine for boss ${bossId} encountered an error and will be stopped:`, error);
                endedCoroutines.push(key); // Stop immediately on error
            }
        }

        // Clean up ended coroutines
        endedCoroutines.forEach(key => this.stop(key, bossId));
    }

    /**
     * Stops a specific coroutine for a given boss and invokes its recall callback if provided.
     * @param {Generator} key - The generator key of the coroutine to stop.
     * @param {string} bossId - The ID of the boss associated with the coroutine.
     */
    static stop(key, bossId) {
        if (!bossCoroutines.has(bossId)) return;

        const coroutines = bossCoroutines.get(bossId);
        coroutines.delete(key);

        if (recallCallbacks.has(key)) {
            recallCallbacks.get(key)();
            recallCallbacks.delete(key);
        }

        // Clean up if no coroutines are left for this boss
        if (coroutines.size === 0) {
            bossCoroutines.delete(bossId);
        }
    }

    /**
     * Starts a coroutine, associating it with a specified boss ID.
     * @param {Function} func - The generator function to run as a coroutine.
     * @param {Function|null} [recall=null] - Optional callback to call upon completion.
     * @param {string} bossId - The ID of the boss associated with this coroutine.
     */
    static start(func, recall = null, bossId) {
        const coroutine = func(); // Create generator from function
        if (recall) {
            recallCallbacks.set(coroutine, recall);
        }
        const initial = coroutine.next();
        const coroutineState = { value: initial.value || 0, done: initial.done };

        // Add coroutine to the boss-specific coroutines map
        if (!bossCoroutines.has(bossId)) {
            bossCoroutines.set(bossId, new Map());
        }
        bossCoroutines.get(bossId).set(coroutine, coroutineState);

        //console.warn(`Started coroutine for boss ${bossId}: ${coroutine}`);
    }
}
