/**
 * Action event helper functions — type guards, validators, and data creation.
 *
 * @module
 */
import { action_event_step_transitions, action_event_phase_by_kind, action_event_phase_transitions, } from './action_event_types.js';
// Type guards for action kinds
export const is_request_response = (data) => data.kind === 'request_response';
export const is_remote_notification = (data) => data.kind === 'remote_notification';
export const is_local_call = (data) => data.kind === 'local_call';
// Type guards for specific states
export const is_send_request = (data) => data.kind === 'request_response' && data.phase === 'send_request';
export const is_receive_request = (data) => data.kind === 'request_response' && data.phase === 'receive_request';
export const is_send_response = (data) => data.kind === 'request_response' && data.phase === 'send_response';
export const is_receive_response = (data) => data.kind === 'request_response' && data.phase === 'receive_response';
export const is_notification_send = (data) => data.kind === 'remote_notification' && data.phase === 'send';
export const is_notification_receive = (data) => data.kind === 'remote_notification' && data.phase === 'receive';
export const is_execute = (data) => data.kind === 'local_call' && data.phase === 'execute';
// Step state guards
export const is_initial = (data) => data.step === 'initial';
export const is_parsed = (data) => data.step === 'parsed';
export const is_handling = (data) => data.step === 'handling';
export const is_handled = (data) => data.step === 'handled';
export const is_failed = (data) => data.step === 'failed';
// Combined type guards for specific states with parsed input
// These check for 'parsed' or 'handling' steps since protocol messages
// are created when transitioning from 'parsed' to 'handling'
export const is_send_request_with_parsed_input = (data) => is_send_request(data) && (data.step === 'parsed' || data.step === 'handling');
export const is_notification_send_with_parsed_input = (data) => is_notification_send(data) && (data.step === 'parsed' || data.step === 'handling');
/**
 * Validate that a step transition is legal per `action_event_step_transitions`.
 *
 * @throws Error if `from → to` is not a permitted transition
 */
export const validate_step_transition = (from, to) => {
    if (!action_event_step_transitions[from].includes(to)) {
        throw new Error(`Invalid step transition from '${from}' to '${to}'`);
    }
};
/**
 * Validate that `phase` is one of the phases allowed for `kind` per
 * `action_event_phase_by_kind`.
 *
 * @throws Error if `phase` is not valid for `kind`
 */
export const validate_phase_for_kind = (kind, phase) => {
    if (!action_event_phase_by_kind[kind].includes(phase)) {
        throw new Error(`Invalid phase '${phase}' for ${kind} action`);
    }
};
/**
 * Validate that a phase chain is legal per `action_event_phase_transitions`.
 *
 * @throws Error if `from → to` is not the permitted next phase (or `from` is terminal)
 */
export const validate_phase_transition = (from, to) => {
    const expected = action_event_phase_transitions[from];
    if (expected !== to) {
        throw new Error(`Invalid phase transition from '${from}' to '${to}'`);
    }
};
export const get_initial_phase = (kind, initiator, executor) => {
    if (initiator !== 'both' && initiator !== executor)
        return null;
    switch (kind) {
        case 'request_response':
            return 'send_request';
        case 'remote_notification':
            return 'send';
        case 'local_call':
            return 'execute';
    }
};
export const should_validate_output = (kind, phase) => (kind === 'request_response' && (phase === 'receive_request' || phase === 'receive_response')) ||
    (kind === 'local_call' && phase === 'execute');
export const is_action_complete = (data) => {
    if (data.step === 'failed')
        return true;
    if (data.step !== 'handled')
        return false;
    // Check if in terminal phase
    const next_phase = action_event_phase_transitions[data.phase];
    return next_phase === null;
};
export const create_initial_data = (kind, phase, method, executor, input) => ({
    kind,
    phase,
    step: 'initial',
    method,
    executor,
    input,
    output: null,
    error: null,
    progress: null,
    request: null,
    response: null,
    notification: null,
});
/**
 * Pull the terminal `Result` from an action event.
 *
 * `data.error` populated → error path (covers both explicit `failed` and
 * the unhandled `receive_error` / `send_error` case where no handler was
 * registered for the error phase). `step === 'handled'` → success path.
 *
 * @throws Error if the event is in a non-terminal state (programming error —
 *   callers should check `is_action_complete` first)
 */
export const extract_action_result = (event) => {
    const { data } = event;
    // `data.error` populated → error path. This covers two cases:
    // 1. `step === 'failed'` — explicit terminal failure.
    // 2. `phase === 'receive_error' | 'send_error'` reached `step === 'handled'`
    //    because no handler was registered for the error phase. The dispatcher
    //    silently transitions to `handled` in that case but leaves `data.error`
    //    populated. Reading `step === 'handled'` first would return
    //    `{ok: true, value: null}` and surprise every caller that doesn't
    //    register an error-phase handler. Preferring `data.error` lets
    //    consumers skip the boilerplate `receive_error` rethrow stub.
    if (data.error) {
        return { ok: false, error: data.error };
    }
    if (data.step === 'handled') {
        return { ok: true, value: data.output };
    }
    // `step === 'failed'` with `data.error === null` is a malformed event;
    // type narrowing accepts it, runtime never produces it.
    throw new Error(`cannot extract result: event in non-terminal state (step: ${data.step})`);
};
