/**
 * Redemption pure decider.
 *
 * PURITY CONTRACT (enforced by _purity-check.sh redemption):
 *   - No imports from '@/server/db', 'do-client', 'outbox-producer',
 *     '@/server/payments', '@/server/storage', '@/server/email', '@/server/push'
 *   - No Date.now() calls (caller passes `at` via event)
 *   - No crypto.randomUUID() calls
 *
 * Transition table:
 *
 *   UNREDEEMED + redeem_attempted(matched + within expiry + vendor ok) → REDEEMED
 *     effects: [send-user-push?]  (state write via platform redeemVoucher)
 *
 *   UNREDEEMED + redeem_attempted(REDEEMED in DB)   → rejected ALREADY_REDEEMED
 *   UNREDEEMED + redeem_attempted(EXPIRED in DB)    → rejected EXPIRED
 *   UNREDEEMED + redeem_attempted(expiresAt<=at)    → rejected EXPIRED
 *   UNREDEEMED + redeem_attempted(CANCELLED in DB)  → rejected INVALID
 *   UNREDEEMED + redeem_attempted(vendor mismatch)  → rejected WRONG_VENDOR
 *
 *   UNREDEEMED + expiry_passed                       → EXPIRED
 *     effects: [mark-expired, enqueue-outbox(expired_split)]
 *
 * Non-state-machine work (HMAC token verification, token hash lookup,
 * customer display-name fallback) stays in the workflow orchestrator.
 */

import type { RedemptionEvent, RedemptionState } from './events.js';
import type { RedemptionEffect } from './effects.js';

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

export interface RedemptionContext {
  state: RedemptionState;
}

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

export type DecideResult =
  | { ok: true; nextState: RedemptionState; effects: RedemptionEffect[] }
  | {
      ok: false;
      error: 'ALREADY_REDEEMED' | 'EXPIRED' | 'WRONG_VENDOR' | 'INVALID' | 'INVALID_STATE';
      from: RedemptionState;
    };

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

export function decide(ctx: RedemptionContext, event: RedemptionEvent): DecideResult {
  switch (event.kind) {
    case 'redeem_attempted': {
      // Trust the event's currentState (snapshot from DB read).
      if (event.currentState === 'REDEEMED') {
        return { ok: false, error: 'ALREADY_REDEEMED', from: event.currentState };
      }
      if (event.currentState === 'EXPIRED' || event.expiresAt <= event.at) {
        return { ok: false, error: 'EXPIRED', from: event.currentState };
      }
      if (event.currentState === 'CANCELLED') {
        return { ok: false, error: 'INVALID', from: event.currentState };
      }
      if (event.currentState !== 'UNREDEEMED') {
        return { ok: false, error: 'INVALID_STATE', from: event.currentState };
      }
      if (event.scanningVendorId !== event.purchaseVendorId) {
        return { ok: false, error: 'WRONG_VENDOR', from: event.currentState };
      }

      const effects: RedemptionEffect[] = [];

      if (event.purchaseUserId !== null) {
        effects.push({
          kind: 'send-user-push',
          userId: event.purchaseUserId,
          purchaseId: event.purchaseId,
          dealId: event.dealId,
        });
      }

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

    case 'expiry_passed': {
      if (event.currentState !== 'UNREDEEMED') {
        return { ok: false, error: 'INVALID_STATE', from: event.currentState };
      }

      const half = (parseFloat(event.amountPaid) / 2).toFixed(2);
      const effects: RedemptionEffect[] = [
        { kind: 'mark-expired', purchaseId: event.purchaseId },
        {
          kind: 'enqueue-outbox',
          aggregateType: 'purchase',
          aggregateId: event.purchaseId,
          eventType: 'purchase.expired_split',
          payload: {
            purchaseId: event.purchaseId,
            vendorId: event.vendorId,
            totalAmount: event.amountPaid,
            platformSplit: half,
            vendorSplit: half,
          },
        },
      ];

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

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