import { DataAccessError } from '@isa/common/data-access';

/**
 * Error codes specific to checkout completion failures.
 */
export type CheckoutCompletionErrorCode =
  | 'SHOPPING_CART_NOT_FOUND'
  | 'SHOPPING_CART_EMPTY'
  | 'CHECKOUT_NOT_FOUND'
  | 'INVALID_AVAILABILITY'
  | 'MISSING_BUYER'
  | 'MISSING_REQUIRED_DATA'
  | 'CHECKOUT_CONFLICT'
  | 'ORDER_CREATION_FAILED'
  | 'DOWNLOAD_UNAVAILABLE'
  | 'SHIPPING_AVAILABILITY_FAILED';

/**
 * Error thrown during checkout completion process.
 *
 * @remarks
 * This error provides specific error codes for different checkout failure scenarios,
 * allowing consumers to handle specific error cases appropriately.
 *
 * @example
 * ```typescript
 * try {
 *   await checkoutService.complete(params);
 * } catch (error) {
 *   if (error instanceof CheckoutCompletionError) {
 *     if (error.code === 'CHECKOUT_CONFLICT') {
 *       // Handle conflict case
 *       console.log('Orders:', error.details);
 *     }
 *   }
 * }
 * ```
 */
export class CheckoutCompletionError extends DataAccessError<CheckoutCompletionErrorCode> {
  constructor(
    code: CheckoutCompletionErrorCode,
    message: string,
    public readonly details?: unknown,
  ) {
    super(code, message);
    this.name = 'CheckoutCompletionError';
  }

  /**
   * Creates an error for when the shopping cart is not found.
   *
   * @param shoppingCartId - ID of the shopping cart that wasn't found
   */
  static shoppingCartNotFound(
    shoppingCartId: number,
  ): CheckoutCompletionError {
    return new CheckoutCompletionError(
      'SHOPPING_CART_NOT_FOUND',
      `Shopping cart with ID ${shoppingCartId} not found`,
      { shoppingCartId },
    );
  }

  /**
   * Creates an error for when the shopping cart is empty.
   *
   * @param shoppingCartId - ID of the empty shopping cart
   */
  static shoppingCartEmpty(shoppingCartId: number): CheckoutCompletionError {
    return new CheckoutCompletionError(
      'SHOPPING_CART_EMPTY',
      `Shopping cart ${shoppingCartId} is empty`,
      { shoppingCartId },
    );
  }

  /**
   * Creates an error for when the checkout entity is not found.
   *
   * @param checkoutId - ID of the checkout that wasn't found
   */
  static checkoutNotFound(checkoutId: number): CheckoutCompletionError {
    return new CheckoutCompletionError(
      'CHECKOUT_NOT_FOUND',
      `Checkout with ID ${checkoutId} not found`,
      { checkoutId },
    );
  }

  /**
   * Creates an error for availability validation failures.
   *
   * @param message - Detailed error message about the availability issue
   * @param itemIds - Optional array of item IDs that failed validation
   */
  static invalidAvailability(
    message: string,
    itemIds?: number[],
  ): CheckoutCompletionError {
    return new CheckoutCompletionError('INVALID_AVAILABILITY', message, {
      itemIds,
    });
  }

  /**
   * Creates an error for order creation conflicts (HTTP 409).
   *
   * @param existingOrders - The orders that already exist
   */
  static checkoutConflict(existingOrders: unknown): CheckoutCompletionError {
    return new CheckoutCompletionError(
      'CHECKOUT_CONFLICT',
      'Checkout already completed with existing orders',
      { existingOrders },
    );
  }

  /**
   * Creates an error for missing required data.
   *
   * @param fieldName - Name of the missing required field
   * @param reason - Optional reason why the field is required
   */
  static missingRequiredData(
    fieldName: string,
    reason?: string,
  ): CheckoutCompletionError {
    const message = reason
      ? `${fieldName} is required: ${reason}`
      : `${fieldName} is required`;

    return new CheckoutCompletionError('MISSING_REQUIRED_DATA', message, {
      fieldName,
      reason,
    });
  }

  /**
   * Creates an error for when download items are unavailable.
   *
   * @param itemIds - Array of shopping cart item IDs that are unavailable
   */
  static downloadItemsUnavailable(itemIds: number[]): CheckoutCompletionError {
    return new CheckoutCompletionError(
      'DOWNLOAD_UNAVAILABLE',
      `Download items are unavailable: ${itemIds.join(', ')}`,
      { itemIds },
    );
  }

  /**
   * Creates an error for shipping availability update failures.
   *
   * @param reason - Reason for the failure
   */
  static shippingAvailabilityFailed(reason: string): CheckoutCompletionError {
    return new CheckoutCompletionError(
      'SHIPPING_AVAILABILITY_FAILED',
      `Failed to update shipping availability: ${reason}`,
      { reason },
    );
  }
}
