/**
 * GroupReservation pure decider.
 *
 * PURITY CONTRACT (enforced by _purity-check.sh group-reservation):
 *   - No imports from '@/server/db'
 *   - No imports from 'do-client'
 *   - No imports from 'outbox-producer'
 *   - No imports from '@/server/payments'
 *   - No imports from '@/server/storage'
 *   - No imports from '@/server/email'
 *   - No imports from '@/server/push'
 *   - No Date.now() calls (caller provides `at` via event)
 *
 * Transition table:
 *
 *   HELD + cancel_requested(within_14d) → CANCELLED
 *     effects: [mark-cancelled, release-hold?, decrement-count, enqueue-outbox(cancelled)]
 *
 *   HELD + cancel_requested(past_14d, FLEXIBLE) → CANCELLED
 *     effects: [mark-cancelled, release-hold?, decrement-count, enqueue-outbox(cancelled)]
 *
 *   HELD + cancel_requested(past_14d, LEGAL_ONLY) → rejected
 *     error: CANCELLATION_NOT_ALLOWED
 *
 *   * + reservation_placed(newCount >= minGroupSize, prev=COLLECTING) → fan-out:
 *     effects: [record-threshold-met]
 *
 *   * + reservation_placed(newCount >= maxGroupSize) → fan-out:
 *     effects: [record-threshold-met?, enqueue-outbox(execute_requested)]
 *
 *   * + cancellation_finalized(newCount < minGroupSize, group=THRESHOLD_MET) → fan-out:
 *     effects: [record-threshold-lost]
 *
 *   * + promote_requested(no_next) → no-op
 *     effects: []
 *
 *   * + promote_requested(has_next, user) → promoted
 *     effects: [promote-waitlist, enqueue-outbox(waitlist_promoted), send-user-push]
 *
 *   * + promote_requested(has_next, guest) → promoted
 *     effects: [promote-waitlist, enqueue-outbox(waitlist_promoted)]
 *
 * Non-state-machine work (card check, hold placement, encryption,
 * reservation row insert, group-deal counter increment, payment-method lookup)
 * stays in the workflow orchestrator — these are non-idempotent I/O or
 * cross-aggregate operations.
 */

import type { GroupReservationEvent, GroupState } from './events.js';
import type { GroupReservationEffect } from './effects.js';

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

/**
 * Represents the reservation.status states relevant to this workflow.
 */
export type ReservationState =
  | 'HELD'
  | 'CANCELLED'
  | 'CAPTURED'
  | 'RELEASED'
  | 'AWAITING_FANOUT'
  | 'AWAITING_PROMOTION';

// ─── Context ─────────────────────────────────────────────────────────────────

export interface GroupReservationContext {
  state: ReservationState;
}

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

export type DecideResult =
  | {
      ok: true;
      nextState: ReservationState;
      effects: GroupReservationEffect[];
    }
  | {
      ok: false;
      error: 'INVALID_STATE' | 'ALREADY_TERMINAL' | 'CANCELLATION_NOT_ALLOWED';
      from: ReservationState;
    };

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

const TERMINAL_RESERVATION_STATES = new Set<ReservationState>([
  'CANCELLED',
  'CAPTURED',
  'RELEASED',
]);

// ─── Decider ─────────────────────────────────────────────────────────────────

/**
 * Pure decider — takes (state, event), returns next state + effects.
 * Never throws; returns typed error result on invalid transition.
 */
export function decide(ctx: GroupReservationContext, event: GroupReservationEvent): DecideResult {
  switch (event.kind) {
    case 'cancel_requested': {
      if (ctx.state !== 'HELD') {
        if (TERMINAL_RESERVATION_STATES.has(ctx.state)) {
          return { ok: false, error: 'ALREADY_TERMINAL', from: ctx.state };
        }
        return { ok: false, error: 'INVALID_STATE', from: ctx.state };
      }

      const isWithin14Days = event.daysSinceReservation <= 14;
      if (!isWithin14Days && event.cancellationPolicy === 'LEGAL_ONLY') {
        return { ok: false, error: 'CANCELLATION_NOT_ALLOWED', from: ctx.state };
      }

      const effects: GroupReservationEffect[] = [
        { kind: 'mark-cancelled', reservationId: event.reservationId },
      ];

      if (event.hasHold && event.providerAuthorizationId !== null) {
        effects.push({
          kind: 'release-hold',
          reservationId: event.reservationId,
          providerHoldId: event.providerAuthorizationId,
        });
      }

      effects.push({
        kind: 'decrement-count',
        groupDealId: event.groupDealId,
        quantity: event.quantity,
      });

      effects.push({
        kind: 'enqueue-outbox',
        aggregateType: 'group_reservation',
        aggregateId: event.reservationId,
        eventType: 'group_reservation.cancelled',
        payload: {
          reservationId: event.reservationId,
          userId: event.userId,
          groupDealId: event.groupDealId,
          dealTitle: event.dealTitle,
        },
      });

      return { ok: true, nextState: 'CANCELLED', effects };
    }

    case 'reservation_placed': {
      const effects: GroupReservationEffect[] = [];

      const reachedThreshold =
        event.previousGroupState === 'COLLECTING' && event.newCount >= event.minGroupSize;
      const reachedMax = event.newCount >= event.maxGroupSize;

      if (reachedThreshold) {
        effects.push({
          kind: 'record-threshold-met',
          groupDealId: event.groupDealId,
          dealId: event.dealId,
          vendorId: event.vendorId,
          newCount: event.newCount,
        });
        // Vendor push on threshold-met — registered path only (guest path
        // intentionally suppresses this notification per pre-split workflow).
        if (event.isRegisteredPath) {
          effects.push({
            kind: 'send-vendor-push',
            vendorId: event.vendorId,
            title: 'עסקת קבוצה הגיעה למינימום!',
            body: `${event.dealTitle} - ${event.newCount} משתתפים`,
            url: `/vendor/deals/${event.dealId}`,
            tag: 'group_threshold_met',
            data: { groupDealId: event.groupDealId },
          });
        }
      }

      if (reachedMax) {
        // If group not yet threshold-met (race), force one before execute.
        if (!reachedThreshold && event.previousGroupState === 'COLLECTING') {
          effects.push({
            kind: 'record-threshold-met',
            groupDealId: event.groupDealId,
            dealId: event.dealId,
            vendorId: event.vendorId,
            newCount: event.newCount,
          });
        }
        effects.push({
          kind: 'enqueue-outbox',
          aggregateType: 'group_deal',
          aggregateId: event.groupDealId,
          eventType: 'group_deal.execute_requested',
          payload: {
            groupDealId: event.groupDealId,
            dealId: event.dealId,
            reason: 'max_group_size_reached',
          },
        });
      }

      return { ok: true, nextState: ctx.state, effects };
    }

    case 'cancellation_finalized': {
      const effects: GroupReservationEffect[] = [];

      const droppedBelowMin =
        event.currentGroupState === 'THRESHOLD_MET' && event.newCount < event.minGroupSize;

      if (droppedBelowMin) {
        effects.push({
          kind: 'record-threshold-lost',
          groupDealId: event.groupDealId,
          dealId: event.dealId,
          vendorId: event.vendorId,
          newCount: event.newCount,
          minGroupSize: event.minGroupSize,
        });
      }

      return { ok: true, nextState: ctx.state, effects };
    }

    case 'promote_requested': {
      const effects: GroupReservationEffect[] = [];

      if (event.promotedWaitlistId === null) {
        return { ok: true, nextState: ctx.state, effects };
      }

      effects.push({
        kind: 'promote-waitlist',
        groupDealId: event.groupDealId,
      });

      effects.push({
        kind: 'enqueue-outbox',
        aggregateType: 'group_deal',
        aggregateId: event.groupDealId,
        eventType: 'group_deal.waitlist_promoted',
        payload: {
          waitlistId: event.promotedWaitlistId,
          groupDealId: event.groupDealId,
          userId: event.promotedUserId,
          dealTitle: event.dealTitle,
          dealId: event.dealId,
        },
      });

      if (event.promotedUserId !== null) {
        effects.push({
          kind: 'send-user-push',
          userId: event.promotedUserId,
          title: 'מקום פנוי בעסקת הקבוצה!',
          body: `${event.dealTitle} - מקום פנוי, הזדרז/י!`,
          url: `/deals/${event.dealId}`,
          tag: 'group_waitlist_promoted',
          data: { groupDealId: event.groupDealId, dealTitle: event.dealTitle },
        });
      }

      return { ok: true, nextState: ctx.state, effects };
    }

    default: {
      const _exhaustive: never = event;
      void _exhaustive;
      return { ok: false, error: 'INVALID_STATE', from: ctx.state };
    }
  }
}

// Re-export commonly used types
export type { GroupState };
