/**
 * Personal Deal decider — pure state machine (FDS §6.6).
 *
 * Validates each personal-deal request event against pre-loaded context and emits
 * a deterministic effect list. NO IO, NO Date.now(), NO crypto.
 *
 *   request_created:
 *     requires: source deal exists, dealState = SOLD_OUT
 *     emits: create-request + send-vendor-push
 *
 *   vendor_accepted:
 *     requires: request PENDING, vendor owns it, deadline not passed
 *     emits: create-personal-deal + mark-accepted + send-user-push
 *
 *   vendor_rejected:
 *     requires: request PENDING, vendor owns it
 *     emits: mark-rejected + send-user-push
 *
 *   expiry_passed:
 *     requires: request PENDING
 *     emits: mark-expired + send-user-push
 *
 * Pure module: no imports from @/server/db, do-client, outbox-producer, payments, fetch.
 */

import type {
  PersonalDealEvent,
  RequestCreatedEvent,
  VendorAcceptedEvent,
  VendorRejectedEvent,
  ExpiryPassedEvent,
  PersonalDealRequestStatus,
} from './events.js';
import type { PersonalDealEffect } from './effects.js';

export type PersonalDealErrorCode =
  | 'DEAL_NOT_FOUND'
  | 'DEAL_NOT_SOLD_OUT'
  | 'REQUEST_NOT_FOUND'
  | 'REQUEST_NOT_PENDING'
  | 'REQUEST_EXPIRED'
  | 'VENDOR_NOT_OWNER';

export interface SourceDealContext {
  id: string;
  vendorId: string;
  dealState: string;
}

export interface PersonalDealRequestContext {
  id: string;
  userId: string;
  vendorId: string;
  sourceDealId: string;
  status: PersonalDealRequestStatus;
  responseDeadline: Date;
}

export type PersonalDealDecisionContext =
  | { kind: 'create'; sourceDeal: SourceDealContext | null }
  | { kind: 'accept'; request: PersonalDealRequestContext | null }
  | { kind: 'reject'; request: PersonalDealRequestContext | null }
  | { kind: 'expire'; request: PersonalDealRequestContext | null };

export type PersonalDealDecision =
  | { ok: true; effects: PersonalDealEffect[] }
  | { ok: false; error: PersonalDealErrorCode; message: string };

export function decide(
  ctx: PersonalDealDecisionContext,
  event: PersonalDealEvent,
): PersonalDealDecision {
  switch (event.kind) {
    case 'request_created':
      if (ctx.kind !== 'create') {
        return { ok: false, error: 'DEAL_NOT_FOUND', message: 'Context kind mismatch' };
      }
      return decideCreate(ctx.sourceDeal, event);
    case 'vendor_accepted':
      if (ctx.kind !== 'accept') {
        return { ok: false, error: 'REQUEST_NOT_FOUND', message: 'Context kind mismatch' };
      }
      return decideAccept(ctx.request, event);
    case 'vendor_rejected':
      if (ctx.kind !== 'reject') {
        return { ok: false, error: 'REQUEST_NOT_FOUND', message: 'Context kind mismatch' };
      }
      return decideReject(ctx.request, event);
    case 'expiry_passed':
      if (ctx.kind !== 'expire') {
        return { ok: false, error: 'REQUEST_NOT_FOUND', message: 'Context kind mismatch' };
      }
      return decideExpire(ctx.request, event);
  }
}

function decideCreate(
  sourceDeal: SourceDealContext | null,
  event: RequestCreatedEvent,
): PersonalDealDecision {
  if (!sourceDeal) {
    return { ok: false, error: 'DEAL_NOT_FOUND', message: `Deal ${event.sourceDealId} not found` };
  }
  if (sourceDeal.dealState !== 'SOLD_OUT') {
    return {
      ok: false,
      error: 'DEAL_NOT_SOLD_OUT',
      message: 'Personal deal requests are only allowed for SOLD_OUT deals',
    };
  }
  return {
    ok: true,
    effects: [
      {
        kind: 'create-request',
        userId: event.userId,
        vendorId: sourceDeal.vendorId,
        sourceDealId: event.sourceDealId,
        responseDeadline: event.responseDeadline,
      },
      { kind: 'send-vendor-push', vendorId: sourceDeal.vendorId, variant: 'request-created' },
    ],
  };
}

function decideAccept(
  request: PersonalDealRequestContext | null,
  event: VendorAcceptedEvent,
): PersonalDealDecision {
  if (!request) {
    return {
      ok: false,
      error: 'REQUEST_NOT_FOUND',
      message: 'Personal deal request not found or already responded',
    };
  }
  if (request.vendorId !== event.vendorId) {
    return { ok: false, error: 'VENDOR_NOT_OWNER', message: 'Vendor does not own this request' };
  }
  if (request.status !== 'PENDING') {
    return {
      ok: false,
      error: 'REQUEST_NOT_PENDING',
      message: 'Personal deal request not found or already responded',
    };
  }
  if (request.responseDeadline.getTime() < event.at.getTime()) {
    return { ok: false, error: 'REQUEST_EXPIRED', message: 'Response deadline has passed' };
  }
  return {
    ok: true,
    effects: [
      {
        kind: 'create-personal-deal',
        vendorId: event.vendorId,
        forUserId: request.userId,
        sourceDealId: request.sourceDealId,
        input: event.newDealInput,
      },
      { kind: 'mark-accepted', requestId: request.id },
      { kind: 'send-user-push', userId: request.userId, variant: 'accepted' },
    ],
  };
}

function decideReject(
  request: PersonalDealRequestContext | null,
  event: VendorRejectedEvent,
): PersonalDealDecision {
  if (!request) {
    return {
      ok: false,
      error: 'REQUEST_NOT_FOUND',
      message: 'Personal deal request not found or already responded',
    };
  }
  if (request.vendorId !== event.vendorId) {
    return { ok: false, error: 'VENDOR_NOT_OWNER', message: 'Vendor does not own this request' };
  }
  if (request.status !== 'PENDING') {
    return {
      ok: false,
      error: 'REQUEST_NOT_PENDING',
      message: 'Personal deal request not found or already responded',
    };
  }
  return {
    ok: true,
    effects: [
      { kind: 'mark-rejected', requestId: request.id },
      {
        kind: 'send-user-push',
        userId: request.userId,
        variant: 'rejected',
        contextId: request.id,
      },
    ],
  };
}

function decideExpire(
  request: PersonalDealRequestContext | null,
  _event: ExpiryPassedEvent,
): PersonalDealDecision {
  if (!request) {
    return { ok: false, error: 'REQUEST_NOT_FOUND', message: 'Request not found' };
  }
  if (request.status !== 'PENDING') {
    return {
      ok: false,
      error: 'REQUEST_NOT_PENDING',
      message: 'Request is no longer pending',
    };
  }
  return {
    ok: true,
    effects: [
      { kind: 'mark-expired', requestId: request.id },
      {
        kind: 'send-user-push',
        userId: request.userId,
        variant: 'expired',
        contextId: request.id,
      },
    ],
  };
}
