// Copyright (c) 2026 Hellmai Ltd
// SPDX-License-Identifier: AGPL-3.0-only

/**
 * Software Delivery State Machine (WU-2664, INIT-058 Layer 1 Cohort B)
 *
 * Enforces canonical WU state transitions according to LumenFlow §2.4.
 *
 * The generic state-transition pattern (canonical state names, alias
 * resolution, transition table evaluation) lives in
 * `@lumenflow/kernel/primitives/state-machine`. This module keeps the
 * SDLC-specific WU state names and surfaces error codes that callers of
 * the software-delivery pack already depend on.
 *
 * WU state names (SDLC-specific):
 * - ready → in_progress (claim)
 * - in_progress → blocked (block)
 * - in_progress → waiting (implementation complete, awaiting sign-off)
 * - in_progress → done (direct completion)
 * - in_progress → ready (release — WU-1080: orphan recovery)
 * - blocked → in_progress (unblock)
 * - blocked → done (blocker resolved, direct completion)
 * - waiting → in_progress (changes requested)
 * - waiting → done (approved)
 * - done → (terminal, no transitions)
 */

import {
  assertTransition as assertKernelTransition,
  TASK_LIFECYCLE_STATES,
  type TaskStateAliases,
} from '@lumenflow/kernel/primitives/state-machine';

import { createError, ErrorCodes } from '@lumenflow/kernel/primitives/error-handler';

/**
 * SDLC-specific WU state names surfaced by the software-delivery pack.
 * The canonical kernel state name for `in_progress` is `active`; the alias
 * below bridges the two vocabularies.
 */
type WuState = 'ready' | 'in_progress' | 'blocked' | 'waiting' | 'done';

const VALID_STATES: ReadonlySet<WuState> = new Set<WuState>([
  'ready',
  'in_progress',
  'blocked',
  'waiting',
  'done',
]);

/**
 * Alias map used to translate SDLC WU state names to the kernel's canonical
 * state vocabulary. Defined once per module so the kernel lookup table is
 * built predictably.
 */
const SDLC_STATE_ALIASES: TaskStateAliases = {
  [TASK_LIFECYCLE_STATES.ACTIVE]: 'in_progress',
};

function throwInvalidState(
  label: 'from' | 'to',
  value: string | null | undefined,
  other: string | null | undefined,
  wuid: string,
  reason?: string,
): never {
  if (value === null || value === undefined || value === '') {
    throw createError(ErrorCodes.STATE_ERROR, `Invalid state: ${value}`, {
      wuid,
      from: label === 'from' ? value : other,
      to: label === 'to' ? value : other,
      reason: reason ?? `${label} state is null/undefined/empty`,
    });
  }
  throw createError(ErrorCodes.STATE_ERROR, `Invalid state: ${value}`, {
    wuid,
    from: label === 'from' ? value : other,
    to: label === 'to' ? value : other,
    validStates: Array.from(VALID_STATES),
  });
}

/**
 * Validates a SDLC WU state transition and throws if illegal.
 *
 * Delegates graph evaluation to `@lumenflow/kernel/primitives/state-machine`
 * and rewraps kernel errors with the `STATE_ERROR` error code that core
 * callers already rely on.
 *
 * @param from  Current WU status (e.g., `in_progress`, `blocked`).
 * @param to    Desired WU status.
 * @param wuid  Work Unit ID (e.g., `WU-416`) used in error messages.
 * @throws Error with `code === 'STATE_ERROR'` for invalid/illegal transitions.
 */
export function assertTransition(
  from: string | null | undefined,
  to: string | null | undefined,
  wuid: string,
): void {
  if (from === null || from === undefined || from === '') {
    throwInvalidState('from', from, to, wuid);
  }
  if (to === null || to === undefined || to === '') {
    throwInvalidState('to', to, from, wuid);
  }
  if (!VALID_STATES.has(from as WuState)) {
    throwInvalidState('from', from, to, wuid);
  }
  if (!VALID_STATES.has(to as WuState)) {
    throwInvalidState('to', to, from, wuid);
  }

  try {
    assertKernelTransition(from, to, wuid, SDLC_STATE_ALIASES);
  } catch (err) {
    const message = err instanceof Error ? err.message : String(err);
    throw createError(ErrorCodes.STATE_ERROR, message, { wuid, from, to });
  }
}
