/**
 * Zod schemas for group-deal API routes.
 *
 * Covers the full group-deal lifecycle:
 *   - Vendor creation and extension/honor/relaunch
 *   - Registered-user and guest join/waitlist
 *   - Reservation cancellation
 *
 * Import the schema to safeParse() and the inferred type for TypeScript.
 */

import { z } from 'zod';
import { fillRule } from '@/lib/enums/fill-rule';
import { cancellationPolicy } from '@/lib/enums/cancellation-policy';
import { groupDealState } from '@/lib/enums/group-deal-state';
import type { GroupDealState } from '@/lib/enums/group-deal-state';

// ─── Shared primitives ────────────────────────────────────────────────────────

/** Positive ILS price string, e.g. "49.90" */
const priceSchema = z
  .string()
  .trim()
  .regex(/^\d+(\.\d{1,2})?$/, 'Price must be a positive decimal with up to 2 decimal places')
  .refine((v) => Number(v) > 0, 'Price must be greater than 0');

/** Discount percent: 0-99 inclusive */
const discountPercentSchema = z.number().int().min(0).max(99);

/** Time string in HH:MM format */
const timeStringSchema = z
  .string()
  .trim()
  .regex(/^\d{2}:\d{2}$/, 'Time must be in HH:MM format')
  .optional();

/** UUID field */
const uuidSchema = z.uuid('Must be a valid UUID');

/** Idempotency key: 8-128 characters */
const idempotencyKeySchema = z.string().min(8).max(128);

// ─── Tier schema (used inside createGroupDealBodySchema) ──────────────────────

/**
 * A single tiered-pricing band.
 * Bands are sorted ascending by minParticipants on the server.
 */
const tierSchema = z.object({
  minParticipants: z.number().int().min(2, 'Minimum participants per tier must be at least 2'),
  pricePerUnit: priceSchema,
  discountPercent: discountPercentSchema,
});

// ─── Create group deal (vendor) ───────────────────────────────────────────────

/**
 * POST /api/group-deals
 * Vendor creates a new group deal.
 */
export const createGroupDealBodySchema = z
  .object({
    // Standard deal fields
    title: z.string().trim().min(1, 'Title is required').max(120),
    description: z.string().trim().max(2000).optional().default(''),
    category: z.string().trim().max(50).optional(),
    categoryId: uuidSchema.optional(),
    tagIds: z.array(uuidSchema).max(20).optional().default([]),
    originalPrice: priceSchema,
    discountPercent: discountPercentSchema,
    discountedPrice: priceSchema,
    specialInstructions: z.string().trim().max(1000).optional(),

    // Group-specific fields
    fillRule: fillRule.schema,
    minGroupSize: z.number().int().min(2, 'Group must have at least 2 participants').max(10000),
    maxGroupSize: z.number().int().min(2, 'Group must have at least 2 participants').max(10000),
    perCustomerLimit: z
      .number()
      .int()
      .min(1, 'Per-customer limit must be at least 1')
      .max(100)
      .default(1),
    deadline: z.iso.datetime({
      offset: true,
      message: 'Deadline must be a valid ISO 8601 datetime',
    }),

    // Cancellation
    cancellationPolicy: cancellationPolicy.schema.default('LEGAL_ONLY'),
    cancellationWindowHours: z.number().int().min(1).max(720).optional(),

    // Optional features
    tieredPricingEnabled: z.boolean().default(false),
    tiers: z.array(tierSchema).max(5).optional(),
    earlyBirdEnabled: z.boolean().default(false),
    earlyBirdSlots: z.number().int().min(1).optional(),
    earlyBirdDiscountPercent: z
      .number()
      .int()
      .min(1, 'Early bird discount must be at least 1%')
      .max(50, 'Early bird extra discount may not exceed 50%')
      .optional(),
    socialSharingEnabled: z.boolean().default(false),
    socialSharingReward: z.string().trim().max(500).optional(),
    bulkPickupEnabled: z.boolean().default(false),
    bulkPickupDetails: z.string().trim().max(1000).optional(),

    // Pickup / delivery (same as other deal types)
    pickupStart: timeStringSchema,
    pickupEnd: timeStringSchema,
    pickupAddress: z.string().trim().max(300).optional(),
  })
  .refine((data) => data.minGroupSize <= data.maxGroupSize, {
    message: 'minGroupSize must be less than or equal to maxGroupSize',
    path: ['minGroupSize'],
  })
  .refine((data) => !data.tieredPricingEnabled || (data.tiers && data.tiers.length > 0), {
    message: 'At least one tier is required when tiered pricing is enabled',
    path: ['tiers'],
  })
  .refine(
    (data) =>
      data.cancellationPolicy !== 'CUSTOM_WINDOW' || data.cancellationWindowHours !== undefined,
    {
      message: 'cancellationWindowHours is required for CUSTOM_WINDOW cancellation policy',
      path: ['cancellationWindowHours'],
    },
  )
  .refine(
    (data) =>
      !data.earlyBirdEnabled ||
      (data.earlyBirdSlots !== undefined && data.earlyBirdDiscountPercent !== undefined),
    {
      message:
        'earlyBirdSlots and earlyBirdDiscountPercent are required when early bird is enabled',
      path: ['earlyBirdSlots'],
    },
  )
  .refine((data) => !data.bulkPickupEnabled || Boolean(data.bulkPickupDetails), {
    message: 'bulkPickupDetails is required when bulk pickup is enabled',
    path: ['bulkPickupDetails'],
  })
  .refine(
    (data) =>
      Math.round(Number(data.originalPrice) * (1 - data.discountPercent / 100) * 100) / 100 >= 3,
    { message: 'Deal price after discount must be at least ₪3', path: ['discountPercent'] },
  );

export type CreateGroupDealBody = z.infer<typeof createGroupDealBodySchema>;

// ─── Join group deal (registered user) ───────────────────────────────────────

/**
 * POST /api/group-deals/[id]/join
 * Registered user reserves a spot (card hold, not charged yet).
 */
export const joinGroupDealBodySchema = z.object({
  quantity: z.number().int().min(1).max(100).default(1),
  paymentMethodId: uuidSchema,
  idempotencyKey: idempotencyKeySchema,
  /** CSRF token - validated server-side against session */
  csrfToken: z.string().min(1).optional(),
});

export type JoinGroupDealBody = z.infer<typeof joinGroupDealBodySchema>;

// ─── Join group deal (guest) ──────────────────────────────────────────────────

/**
 * POST /api/group-deals/[id]/join/guest
 * Guest user reserves a spot using a one-time payment token.
 */
export const joinGroupDealGuestBodySchema = z.object({
  quantity: z.number().int().min(1).max(100).default(1),
  /** One-time payment token from the payment provider SDK */
  paymentMethodId: z.string().min(1).max(512),
  email: z.email('Invalid email address').trim().max(254),
  phone: z
    .string()
    .trim()
    .regex(/^\+?[0-9]{9,15}$/, 'Invalid phone number format'),
  idempotencyKey: idempotencyKeySchema,
  /** CSRF token - validated server-side against session */
  csrfToken: z.string().min(1).optional(),
});

export type JoinGroupDealGuestBody = z.infer<typeof joinGroupDealGuestBodySchema>;

// ─── Cancel reservation ───────────────────────────────────────────────────────

/**
 * DELETE /api/group-deals/[id]/reservations/[reservationId]
 * User cancels their reservation; hold is released subject to cancellation policy.
 */
export const cancelReservationBodySchema = z.object({
  /** CSRF token - validated server-side against session */
  csrfToken: z.string().min(1).optional(),
});

export type CancelReservationBody = z.infer<typeof cancelReservationBodySchema>;

// ─── Extend group deal (vendor) ───────────────────────────────────────────────

/**
 * PATCH /api/group-deals/[id]/extend
 * Vendor pushes the deadline forward to give more time to collect participants.
 */
export const extendGroupDealBodySchema = z.object({
  newDeadline: z.iso.datetime({ offset: true, message: 'Must be a valid ISO 8601 datetime' }),
  /** CSRF token - validated server-side against session */
  csrfToken: z.string().min(1).optional(),
});

export type ExtendGroupDealBody = z.infer<typeof extendGroupDealBodySchema>;

// ─── Honor partial execution (vendor) ────────────────────────────────────────

/**
 * POST /api/group-deals/[id]/honor
 * Vendor elects to honor the group deal even though minimum was not reached.
 * Must be submitted within the 24-hour decision window.
 */
export const honorGroupDealBodySchema = z.object({
  /** CSRF token - validated server-side against session */
  csrfToken: z.string().min(1).optional(),
});

export type HonorGroupDealBody = z.infer<typeof honorGroupDealBodySchema>;

// ─── Join waitlist (registered user) ─────────────────────────────────────────

/**
 * POST /api/group-deals/[id]/waitlist
 * Registered user joins the waitlist for a full group deal.
 * A hold is placed and converted if a spot opens.
 */
export const joinWaitlistBodySchema = z.object({
  quantity: z.number().int().min(1).max(100).default(1),
  paymentMethodId: uuidSchema,
  /** CSRF token - validated server-side against session */
  csrfToken: z.string().min(1).optional(),
});

export type JoinWaitlistBody = z.infer<typeof joinWaitlistBodySchema>;

// ─── Join waitlist (guest) ────────────────────────────────────────────────────

/**
 * POST /api/group-deals/[id]/waitlist/guest
 * Guest user joins the waitlist using a one-time payment token.
 */
export const joinWaitlistGuestBodySchema = z.object({
  quantity: z.number().int().min(1).max(100).default(1),
  /** One-time payment token from the payment provider SDK */
  paymentMethodId: z.string().min(1).max(512),
  email: z.email('Invalid email address').trim().max(254),
  phone: z
    .string()
    .trim()
    .regex(/^\+?[0-9]{9,15}$/, 'Invalid phone number format'),
  /** CSRF token - validated server-side against session */
  csrfToken: z.string().min(1).optional(),
});

export type JoinWaitlistGuestBody = z.infer<typeof joinWaitlistGuestBodySchema>;

// ─── Relaunch group deal (vendor) ─────────────────────────────────────────────

/**
 * POST /api/group-deals/[id]/relaunch
 * Vendor relaunches a failed or expired group deal, optionally adjusting the
 * deadline and/or maximum group size.
 */
export const relaunchGroupDealBodySchema = z.object({
  deadline: z.iso.datetime({ offset: true }).optional(),
  maxGroupSize: z.number().int().min(2, 'Group must have at least 2 participants').optional(),
  /** CSRF token - validated server-side against session */
  csrfToken: z.string().min(1).optional(),
});

export type RelaunchGroupDealBody = z.infer<typeof relaunchGroupDealBodySchema>;

// ─── Reserve response schemas ─────────────────────────────────────────────────

export const groupDealStateSchema = groupDealState.schema;

export type { GroupDealState };

export const reservationSchema = z.object({
  id: uuidSchema,
  groupDealId: uuidSchema,
  dealId: uuidSchema,
  userId: uuidSchema,
  paymentMethodId: uuidSchema,
  quantity: z.number().int().positive(),
  unitPrice: z.string(),
  totalAmount: z.string(),
  idempotencyKey: z.string(),
  createdAt: z.iso.datetime(),
});

export type Reservation = z.infer<typeof reservationSchema>;

export const updatedGroupDealSchema = z.object({
  id: uuidSchema,
  groupState: groupDealStateSchema,
  currentReservationCount: z.number().int().nonnegative(),
  minGroupSize: z.number().int().positive(),
  maxGroupSize: z.number().int().positive(),
  deadline: z.iso.datetime(),
});

export type UpdatedGroupDeal = z.infer<typeof updatedGroupDealSchema>;

export const reserveResponseSchema = z.object({
  ok: z.literal(true),
  reservation: reservationSchema,
  updatedGroupDeal: updatedGroupDealSchema,
});

export type ReserveResponse = z.infer<typeof reserveResponseSchema>;
