import { findEvent } from "./state-machine.js";
import { InvalidTransitionError } from "./types.js";
export const defaultStateMachine = {
    initial: "draft",
    states: ["draft", "planned", "in-progress", "done", "archived"],
    events: {
        plan: { from: ["draft"], to: "planned" },
        start: { from: ["planned"], to: "in-progress" },
        complete: { from: ["in-progress"], to: "done" },
        archive: { from: "*", to: "archived" }
    }
};
Object.freeze(defaultStateMachine.states);
for (const event of Object.values(defaultStateMachine.events)) {
    if (event.from !== "*") {
        Object.freeze(event.from);
    }
    Object.freeze(event);
}
Object.freeze(defaultStateMachine.events);
Object.freeze(defaultStateMachine);
function deriveLegacyTransitions(machine) {
    const transitions = Object.fromEntries(machine.states.map((state) => [state, new Set()]));
    for (const fromState of machine.states) {
        for (const eventName of Object.keys(machine.events)) {
            const event = findEvent(machine, fromState, eventName);
            if (event !== undefined) {
                transitions[fromState].add(event.to);
            }
        }
    }
    const terminalState = machine.events.archive.to;
    const activeStates = machine.states.filter((state) => state !== terminalState);
    for (let index = 1; index < activeStates.length; index += 1) {
        transitions[activeStates[index]].add(activeStates[index - 1]);
    }
    return transitions;
}
const defaultTransitions = deriveLegacyTransitions(defaultStateMachine);
export function resolveStateMachine(stateMachine) {
    return stateMachine ?? defaultStateMachine;
}
export function assertEvent(machine, fromState, eventName) {
    const event = findEvent(machine, fromState, eventName);
    if (event === undefined) {
        throw new InvalidTransitionError(`Cannot fire event "${eventName}" from task state "${fromState}".`);
    }
    return event;
}
function canTransition(machine, fromState, toState) {
    if (Object.is(machine, defaultStateMachine)) {
        return defaultTransitions[fromState]?.has(toState) ?? false;
    }
    for (const eventName of Object.keys(machine.events)) {
        if (findEvent(machine, fromState, eventName)?.to === toState) {
            return true;
        }
    }
    return false;
}
export function assertTransition(machineOrFrom, fromOrTo, maybeTo) {
    const machine = maybeTo === undefined
        ? defaultStateMachine
        : machineOrFrom;
    const fromState = maybeTo === undefined ? machineOrFrom : fromOrTo;
    const toState = maybeTo === undefined ? fromOrTo : maybeTo;
    if (!canTransition(machine, fromState, toState)) {
        throw new InvalidTransitionError(`Cannot transition task from "${fromState}" to "${toState}".`);
    }
}
