/**
 * GroupDeal pure state machine.
 *
 * PURITY CONTRACT (enforced by machine.test.ts import-text assertion):
 *   - No imports from '@/server/db'
 *   - No imports from 'do-client'
 *   - No imports from 'outbox-producer'
 *   - No Date.now() calls (caller provides `at` via event)
 *
 * Transition table (matches golden baseline in regression/state-machine-equivalence/group-deal.test.ts):
 *
 *   COLLECTING    + threshold_met                    → THRESHOLD_MET    [disarm deadline if extended, enqueue outbox]
 *   COLLECTING    + window_expired_below_threshold   → PARTIAL_PENDING  [arm partial_decision alarm, enqueue outbox]
 *   COLLECTING    + vendor_extend                    → EXTENDED         [arm deadline alarm, enqueue outbox]
 *   COLLECTING    + fail_requested                   → FAILED           [disarm alarm, release-reservations, enqueue outbox]
 *   THRESHOLD_MET + window_expired_above_threshold   → SUCCEEDED        [disarm alarm, execute-deal, enqueue outbox]
 *   THRESHOLD_MET + vendor_extend                    → EXTENDED         [arm deadline alarm, enqueue outbox]
 *   THRESHOLD_MET + fail_requested                   → FAILED           [disarm alarm, release-reservations, enqueue outbox]
 *   EXTENDED      + threshold_met                    → THRESHOLD_MET    [enqueue outbox]
 *   EXTENDED      + threshold_lost                   → COLLECTING       [enqueue outbox] (count drops, not a named public fn but handled)
 *   EXTENDED      + window_expired_above_threshold   → SUCCEEDED        [disarm alarm, execute-deal, enqueue outbox]
 *   EXTENDED      + window_expired_below_threshold   → PARTIAL_PENDING  [arm partial_decision alarm, enqueue outbox]
 *   EXTENDED      + fail_requested                   → FAILED           [disarm alarm, release-reservations, enqueue outbox]
 *   PARTIAL_PENDING + vendor_honor_partial           → VENDOR_HONORED   [disarm alarm, enqueue outbox]
 *   PARTIAL_PENDING + fail_requested                 → FAILED           [disarm alarm, release-reservations, enqueue outbox]
 *   PARTIAL_PENDING + partial_decision_expired       → FAILED           [disarm alarm, release-reservations, enqueue outbox]
 *   VENDOR_HONORED  + window_expired_above_threshold → SUCCEEDED        [execute-deal, enqueue outbox]  (alias: execute)
 *   SUCCEEDED / FAILED / EXECUTING / CANCELLED       → ALREADY_TERMINAL
 *
 * threshold_lost (THRESHOLD_MET → COLLECTING) is a count-driven transition
 * fired by group-reservation.ts when a reservation is cancelled and count
 * drops below minGroupSize.
 */

import type { GroupDealEvent } from './events.js';
import type { GroupDealEffect } from './effects.js';

// ─── State type ───────────────────────────────────────────────────────────────

/**
 * Mirrors groupDealStateEnum from db/schema.ts.
 * EXECUTING and CANCELLED exist in the DB enum but have no outgoing transitions
 * in the workflow — they are terminal.
 */
export type GroupDealState =
  | 'COLLECTING'
  | 'THRESHOLD_MET'
  | 'EXECUTING'
  | 'PARTIAL_PENDING'
  | 'SUCCEEDED'
  | 'FAILED'
  | 'VENDOR_HONORED'
  | 'EXTENDED';

// ─── Input context ────────────────────────────────────────────────────────────

export interface GroupDealContext {
  state: GroupDealState;
  groupDealId: string;
  dealId: string;
  vendorId: string;
  minGroupSize: number;
  currentReservationCount: number;
  extensionCount: number;
}

// ─── Result type ─────────────────────────────────────────────────────────────

export type TransitionResult =
  | { ok: true; nextState: GroupDealState; effects: GroupDealEffect[] }
  | {
      ok: false;
      error: 'INVALID_TRANSITION' | 'ALREADY_TERMINAL';
      from: GroupDealState;
      event: GroupDealEvent;
    };

// ─── Terminal states ──────────────────────────────────────────────────────────

const TERMINAL_STATES = new Set<GroupDealState>(['SUCCEEDED', 'FAILED', 'EXECUTING']);

// ─── Machine ──────────────────────────────────────────────────────────────────

/**
 * Referentially transparent transition function.
 * Takes current context + event, returns next state + effects list.
 * Never throws — returns a typed error result instead.
 */
export function transition(ctx: GroupDealContext, event: GroupDealEvent): TransitionResult {
  const { state, groupDealId, dealId, vendorId } = ctx;

  if (TERMINAL_STATES.has(state)) {
    return { ok: false, error: 'ALREADY_TERMINAL', from: state, event };
  }

  switch (event.kind) {
    // ── threshold_met ─────────────────────────────────────────────────────────
    case 'threshold_met': {
      if (state !== 'COLLECTING' && state !== 'EXTENDED') {
        return { ok: false, error: 'INVALID_TRANSITION', from: state, event };
      }
      const effects: GroupDealEffect[] = [
        {
          kind: 'enqueue-outbox',
          eventType: 'group_deal.threshold_met',
          aggregateType: 'group_deal',
          aggregateId: groupDealId,
          payload: { groupDealId, dealId, vendorId },
        },
      ];
      return { ok: true, nextState: 'THRESHOLD_MET', effects };
    }

    // ── threshold_lost ────────────────────────────────────────────────────────
    case 'threshold_lost': {
      if (state !== 'THRESHOLD_MET') {
        return { ok: false, error: 'INVALID_TRANSITION', from: state, event };
      }
      const effects: GroupDealEffect[] = [
        {
          kind: 'enqueue-outbox',
          eventType: 'group_deal.below_threshold',
          aggregateType: 'group_deal',
          aggregateId: groupDealId,
          payload: { groupDealId, dealId, vendorId },
        },
      ];
      return { ok: true, nextState: 'COLLECTING', effects };
    }

    // ── window_expired_above_threshold ────────────────────────────────────────
    case 'window_expired_above_threshold': {
      if (state !== 'THRESHOLD_MET' && state !== 'VENDOR_HONORED' && state !== 'EXTENDED') {
        return { ok: false, error: 'INVALID_TRANSITION', from: state, event };
      }
      const effects: GroupDealEffect[] = [
        { kind: 'disarm-alarm', groupDealId },
        { kind: 'execute-deal', groupDealId },
        {
          kind: 'enqueue-outbox',
          eventType: 'group_deal.succeeded',
          aggregateType: 'group_deal',
          aggregateId: groupDealId,
          payload: { groupDealId, dealId, vendorId },
        },
      ];
      return { ok: true, nextState: 'SUCCEEDED', effects };
    }

    // ── window_expired_below_threshold ────────────────────────────────────────
    case 'window_expired_below_threshold': {
      if (state !== 'COLLECTING' && state !== 'EXTENDED') {
        return { ok: false, error: 'INVALID_TRANSITION', from: state, event };
      }
      const effects: GroupDealEffect[] = [
        {
          kind: 'arm-alarm',
          alarmType: 'partial_decision',
          groupDealId,
          at: event.at,
        },
        {
          kind: 'enqueue-outbox',
          eventType: 'group_deal.partial_pending',
          aggregateType: 'group_deal',
          aggregateId: groupDealId,
          payload: { groupDealId, dealId, vendorId },
        },
      ];
      return { ok: true, nextState: 'PARTIAL_PENDING', effects };
    }

    // ── vendor_extend ─────────────────────────────────────────────────────────
    case 'vendor_extend': {
      if (state !== 'COLLECTING' && state !== 'THRESHOLD_MET') {
        return { ok: false, error: 'INVALID_TRANSITION', from: state, event };
      }
      const effects: GroupDealEffect[] = [
        {
          kind: 'arm-alarm',
          alarmType: 'deadline',
          groupDealId,
          at: event.newDeadline,
        },
        {
          kind: 'enqueue-outbox',
          eventType: 'group_deal.extended',
          aggregateType: 'group_deal',
          aggregateId: groupDealId,
          payload: { groupDealId, dealId, newDeadline: event.newDeadline.toISOString() },
        },
      ];
      return { ok: true, nextState: 'EXTENDED', effects };
    }

    // ── vendor_honor_partial ──────────────────────────────────────────────────
    case 'vendor_honor_partial': {
      if (state !== 'PARTIAL_PENDING') {
        return { ok: false, error: 'INVALID_TRANSITION', from: state, event };
      }
      const effects: GroupDealEffect[] = [
        { kind: 'disarm-alarm', groupDealId },
        { kind: 'execute-deal', groupDealId },
        {
          kind: 'enqueue-outbox',
          eventType: 'group_deal.succeeded',
          aggregateType: 'group_deal',
          aggregateId: groupDealId,
          payload: { groupDealId, dealId, vendorId },
        },
      ];
      return { ok: true, nextState: 'SUCCEEDED', effects };
    }

    // ── fail_requested ────────────────────────────────────────────────────────
    case 'fail_requested': {
      // Valid from all non-terminal states
      const effects: GroupDealEffect[] = [
        { kind: 'disarm-alarm', groupDealId },
        { kind: 'release-reservations', groupDealId },
        {
          kind: 'enqueue-outbox',
          eventType: 'group_deal.failed',
          aggregateType: 'group_deal',
          aggregateId: groupDealId,
          payload: { groupDealId, dealId, vendorId, reason: event.reason },
        },
      ];
      return { ok: true, nextState: 'FAILED', effects };
    }

    // ── partial_decision_expired ──────────────────────────────────────────────
    case 'partial_decision_expired': {
      if (state !== 'PARTIAL_PENDING') {
        return { ok: false, error: 'INVALID_TRANSITION', from: state, event };
      }
      const effects: GroupDealEffect[] = [
        { kind: 'disarm-alarm', groupDealId },
        { kind: 'release-reservations', groupDealId },
        {
          kind: 'enqueue-outbox',
          eventType: 'group_deal.failed',
          aggregateType: 'group_deal',
          aggregateId: groupDealId,
          payload: { groupDealId, dealId, vendorId, reason: 'partial_decision_expired' },
        },
      ];
      return { ok: true, nextState: 'FAILED', effects };
    }

    default: {
      // TypeScript exhaustiveness guard
      const _exhaustive: never = event;
      void _exhaustive;
      return {
        ok: false,
        error: 'INVALID_TRANSITION',
        from: state,
        event: event as GroupDealEvent,
      };
    }
  }
}
