import { Consent } from './airwallex';
import { Intent } from './cardNumber';
import { PaymentMethodType } from './element';
// Elements events
export type DropInElementEventCode =
  | 'ready'
  | 'success'
  | 'error'
  | 'cancel'
  | 'clickConfirmButton'
  | 'switchMethod'
  | 'pendingVerifyAccount';

export type ApplePayButtonEventCode =
  | 'ready'
  | 'success'
  | 'error'
  | 'cancel'
  | 'shippingMethodChange'
  | 'shippingAddressChange'
  | 'click'
  | 'validateMerchant'
  | 'authorized';

export type GooglePayButtonEventCode =
  | 'ready'
  | 'success'
  | 'error'
  | 'cancel'
  | 'shippingMethodChange'
  | 'shippingAddressChange'
  | 'click'
  | 'authorized';

/**
 * CardNumberElement, CvcElement and ExpiryElement event codes
 */
export type SplitElementEventCode = 'ready' | 'change' | 'focus' | 'blur' | 'pressArrowKey' | 'submit';

export type CardElementEventCode = 'ready' | 'change' | 'focus' | 'blur' | 'submit';

type PressArrowKeyEvent = {
  detail: {
    arrowKey: string;
  };
};

type InputEvent = {
  detail: {
    /**
     * The form data
     */
    completed: boolean;
    empty: boolean;
    error: {
      /**
       * The unexpected error code, for example: UNKNOWN_ERROR.
       */
      code: string;
      /**
       * The Error message
       */
      message: string;
    };
  };
};

type ErrorEvent = {
  detail: {
    error: {
      /**
       * The unexpected error code, for example: UNKNOWN_ERROR.
       */
      code: string;
      /**
       * The Error details
       */
      details?: Record<string, unknown>;
      /**
       * The Error message
       */
      message?: string;
    };
  };
};

export type SuccessEvent = {
  detail: {
    /**
     * The intent of the payment
     */
    intent?: Intent;
    /**
     * The consent of the payment
     */
    consent?: Consent;
  };
};

export type SwitchMethodEvent = {
  detail: {
    /**
     * The method of the payment
     */
    method: PaymentMethodType;
    /**
     * The name of the method
     */
    methodName: string;
    /**
     * The surcharge fee of the method
     */
    surchargeFee: number;
  };
};

export type PendingVerifyAccountEvent = {
  detail: {
    /**
     * The intent of the payment
     */
    intent: Intent;
    /**
     * The url of the payment
     */
    url: string;
  };
};

export type DropInElementEventHandler<T extends DropInElementEventCode> = (eventData: DropInElementEvent[T]) => void;

/**
 * The event object you can listen to.
 */
type DropInElementEvent = {
  /**
   * This event will be fired when the element is ready to interact with the user.
   *
   * @example
   * ```ts
   * element.on('ready', () => void);
   * ```
   */
  ready: undefined;
  /**
   * This event will be fired when the form encounters an unexpected error.
   *
   * @example
   * ```ts
   * element.on('error', (e) => {
   *   const { error } = e.detail;
   *   console.error('There is an error', error);
   * });
   * ```
   */
  error: ErrorEvent;
  /**
   * This event will be fired when the payment is successful.
   *
   * @example
   * ```ts
   * element.on('success', (e) => {
   *   const { intent, consent } = e.detail;
   *   console.log('Payment is successful', { intent, consent });
   * });
   * ```
   */
  success: SuccessEvent;
  /**
   * This event will be fired when the payment is cancelled.
   *
   * @example
   * ```ts
   * element.on('cancel', () => void);
   * ```
   */
  cancel: undefined;
  /**
   * This event will be fired when the user clicks the confirm button.
   *
   * @example
   * ```ts
   * element.on('clickConfirmButton', () => void);
   * ```
   */
  clickConfirmButton: undefined;
  /**
   * This event will be fired when the user switches the payment method.
   *
   * @example
   * ```ts
   * element.on('switchMethod', (e) => {
   *   const { method, methodName, surchargeFee } = e.detail;
   *   console.log('User switched to', { method, methodName, surchargeFee });
   * });
   * ```
   */
  switchMethod: SwitchMethodEvent;
  /**
   * This event will be fired when the user is pending to verify their account. It is only used in the direct debit payment method.
   *
   * @example
   * ```ts
   * element.on('pendingVerifyAccount', (e) => {
   *   const { intent, url } = e.detail;
   *   console.log('User is pending to verify their account', { intent, url });
   * });
   * ```
   */
  pendingVerifyAccount: PendingVerifyAccountEvent;
};

interface ApplePayDateComponents {
  /**
   * A number that represents a day.
   */
  days: number;

  /**
   * A number between 1 and 12 that represents a month.
   */
  months: number;

  /**
   * A number that represents a year.
   */
  years: number;

  /**
   * A number that represents an hour.
   */
  hours: number;
}

type ApplePayButtonShippingMethodChangeEvent = {
  detail: {
    shippingMethod: {
      /**
       * A short description of the shipping method.
       */
      label: string;

      /**
       * A further description of the shipping method.
       */
      detail: string;

      /**
       * The amount associated with this shipping method.
       */
      amount: string;

      /**
       * A client-defined identifier.
       */
      identifier: string;

      /**
       * A dictionary that specifies the start and end dates for a range of time.
       */
      dateComponentsRange?: {
        /**
         * The start date and time of the range.
         */
        startDateComponents: ApplePayDateComponents;
        /**
         * The end date and time of the range.
         */
        endDateComponents: ApplePayDateComponents;
      };
    };
  };
};

type ApplePayPaymentContact = {
  /**
   * An email address for the contact.
   */
  emailAddress?: string | undefined;

  /**
   * The contact's family name.
   */
  familyName?: string | undefined;

  /**
   * The contact's given name.
   */
  givenName?: string | undefined;

  /**
   * A phone number for the contact.
   */
  phoneNumber?: string | undefined;

  /**
   * The phonetic spelling of the contact's family name.
   */
  phoneticFamilyName?: string | undefined;

  /**
   * The phonetic spelling of the contact's given name.
   */
  phoneticGivenName?: string | undefined;

  /**
   * The street portion of the address for the contact.
   */
  addressLines?: string[] | undefined;

  /**
   * The city for the contact.
   */
  locality?: string | undefined;

  /**
   * Additional information associated with the location, typically defined at the city or town level (such as district or neighborhood), in a postal address.
   */
  subLocality?: string | undefined;

  /**
   * The state for the contact.
   */
  administrativeArea?: string | undefined;

  /**
   * The subadministrative area (such as a county or other region) in a postal address.
   */
  subAdministrativeArea?: string | undefined;

  /**
   * The zip code or postal code, where applicable, for the contact.
   */
  postalCode?: string | undefined;

  /**
   * The name of the country for the contact.
   */
  country?: string | undefined;

  /**
   * The contact's two-letter ISO 3166 country code.
   */
  countryCode?: string | undefined;
};

type ApplePayButtonShippingAddressChangeEvent = {
  detail: {
    shippingAddress: ApplePayPaymentContact;
  };
};

type ApplePayPaymentToken = {
  /**
   * An object containing the encrypted payment data.
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  paymentData: any;

  /**
   * Information about the card used in the transaction.
   */
  paymentMethod: Record<string, unknown>;

  /**
   * A unique identifier for this payment.
   */
  transactionIdentifier: string;
};

type ApplePayAuthorizedEvent = {
  detail: {
    paymentData: {
      /**
       * The encrypted information for an authorized payment.
       */
      token: ApplePayPaymentToken;

      /**
       * The billing contact selected by the user for this transaction.
       */
      billingContact?: ApplePayPaymentContact | undefined;

      /**
       * The shipping contact selected by the user for this transaction.
       */
      shippingContact?: ApplePayPaymentContact | undefined;
    };
  };
};

type ApplePayValidateMerchantEvent = {
  detail: {
    validationURL: string;
  };
};

export type ApplePayButtonEventHandler<T extends ApplePayButtonEventCode> = (eventData: ApplePayButtonEvent[T]) => void;

export type ApplePayButtonEvent = {
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('ready',( ) => void);
   *  ```
   */

  ready: undefined;
  /**
   * This event will be fired when form encounters an unexpected error.
   *
   * @example
   *  ```ts
   *   element.on('error', (e) => {
   *     const { error } = e.detail;
   *      console.error('There is an error', error);
   *   });
   *  ```
   */
  error: ErrorEvent;
  /**
   * This event will be fired when payment is successful.
   *
   * @example
   *  ```ts
   *   element.on('success', (e) => {
   *     const { intent, consent } = e.detail;
   *     console.log('Payment is successful', { intent, consent });
   *   });
   *  ```
   */
  success: SuccessEvent;
  /**
   * This event will be fired when payment is cancelled.
   *
   * @example
   *  ```ts
   *   element.on('cancel', ( ) => void);
   *  ```
   */
  cancel: undefined;
  /**
   * This event will be fired when the shipping method changes.
   *
   * @example
   *  ```ts
   *   element.on('shippingMethodChange', (e) => {
   *     const { shippingMethod } = e.detail;
   *     console.log('Shipping method changed', shippingMethod);
   *   });
   *  ```
   */
  shippingMethodChange: ApplePayButtonShippingMethodChangeEvent;
  /**
   * This event will be fired when the shipping address changes.
   *
   * @example
   *  ```ts
   *   element.on('shippingAddressChange', (e) => {
   *     const { shippingAddress } = e.detail;
   *     console.log('Shipping address changed', shippingAddress);
   *   });
   *  ```
   */
  shippingAddressChange: ApplePayButtonShippingAddressChangeEvent;
  /**
   * This event will be fired when the user clicks the button.
   *
   * @example
   *  ```ts
   *   element.on('click', () => void);
   *  ```
   */
  click: undefined;
  /**
   * This event will be fired when the user validates the merchant.
   *
   * @example
   *  ```ts
   *   element.on('validateMerchant', (e) => {
   *     const { validationURL } = e.detail;
   *     console.log('Validation URL', validationURL);
   *   });
   *  ```
   */
  validateMerchant: ApplePayValidateMerchantEvent;
  /**
   * This event will be fired when the user authorizes the payment.
   *
   * @example
   *  ```ts
   *   element.on('authorized', (e) => {
   *     const { paymentData } = e.detail;
   *     console.log('Payment authorized', paymentData);
   *   });
   *  ```
   */
  authorized: ApplePayAuthorizedEvent;
};

type GooglePayAuthorizedEvent = {
  detail: {
    paymentData: google.payments.api.PaymentData;
  };
};

type GooglePayShippingAddressChangeEvent = {
  detail: {
    shippingAddress: google.payments.api.Address;
  };
};

type GooglePayIntermediatePaymentData = {
  detail: {
    intermediatePaymentData: google.payments.api.IntermediatePaymentData;
  };
};

export type GooglePayButtonEventHandler<T extends GooglePayButtonEventCode> = (
  eventData: GooglePayButtonEvent[T],
) => void;

export type GooglePayButtonEvent = {
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('ready',( ) => void);
   *  ```
   */
  ready: undefined;
  /**
   * This event will be fired when form encounters an unexpected error.
   *
   * @example
   *  ```ts
   *   element.on('error', (e) => {
   *     const { error } = e.detail;
   *      console.error('There is an error', error);
   *   });
   *  ```
   */
  error: ErrorEvent;
  /**
   * This event will be fired when payment is successful. It can be used to redirect the user to the success page.
   *
   * @example
   *  ```ts
   *   element.on('success', (e) => {
   *     const { intent, consent } = e.detail;
   *     console.log('Payment is successful', { intent, consent });
   *   });
   *  ```
   */
  success: SuccessEvent;
  /**
   * This event will be fired when payment is cancelled.
   *
   * @example
   *  ```ts
   *   element.on('cancel', ( ) => void);
   *  ```
   */
  cancel: undefined;
  /**
   * This event will be fired when the user clicks the button.
   *
   * @example
   *  ```ts
   *   element.on('click', () => void);
   *  ```
   */
  click: undefined;
  /**
   * This event will be fired when the user authorizes the payment.
   *
   * @example
   *  ```ts
   *   element.on('authorized', (e) => {
   *     const { paymentData } = e.detail;
   *     console.log('Payment authorized', paymentData);
   *   });
   *  ```
   */
  authorized: GooglePayAuthorizedEvent;
  /**
   * This event will be fired when the shipping method changes.
   *
   * @example
   *  ```ts
   *   element.on('shippingMethodChange', (e) => {
   *     const { intermediatePaymentData } = e.detail;
   *     console.log('Shipping method changed', intermediatePaymentData);
   *   });
   *  ```
   */
  shippingMethodChange: GooglePayIntermediatePaymentData;
  /**
   * This event will be fired when the shipping address changes.
   *
   * @example
   *  ```ts
   *   element.on('shippingAddressChange', (e) => {
   *     const { intermediatePaymentData } = e.detail;
   *     console.log('Shipping address changed', intermediatePaymentData);
   *   });
   *  ```
   */
  shippingAddressChange: GooglePayIntermediatePaymentData;
};

export type SplitElementEventHandler<T extends SplitElementEventCode> = (eventData: SplitElementEvent[T]) => void;

type SplitElementEvent = {
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('ready',( ) => void);
   *  ```
   */
  ready: undefined;
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('change',( ) => {
   *     const { completed, empty, error } = e.detail;
   *     console.log('Form changed', { completed, empty, error });
   *   });
   *  ```
   */
  change: InputEvent;
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('blur',( ) => {
   *     const { completed, empty, error } = e.detail;
   *     console.log('Form blurred', { completed, empty, error });
   *   });
   *  ```
   */
  blur: InputEvent;
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('focus',( ) => void);
   *  ```
   */
  focus: InputEvent;
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('pressArrowKey',( ) => {
   *     const { arrowKey } = e.detail;
   *     console.log('Arrow key pressed', arrowKey);
   *   });
   *  ```
   */
  pressArrowKey: PressArrowKeyEvent;
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('submit',( ) => {
   *     console.log('Form submitted');
   *   });
   *  ```
   */
  submit: undefined;
};

export type CardElementEventHandler<T extends CardElementEventCode> = (eventData: CardElementEvent[T]) => void;

type CardElementEvent = {
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('ready',( ) => void);
   *  ```
   */
  ready: undefined;
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('change',(e) => {
   *     const { completed, empty, error } = e.detail;
   *     console.log('Form changed', { completed, empty, error });
   *   });
   *  ```
   */
  change: InputEvent;
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('focus',(e) => {
   *     const { completed, empty, error } = e.detail;
   *     console.log('Form focused', { completed, empty, error });
   *   });
   *  ```
   */
  focus: InputEvent;
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('blur',( ) =&gt; {
   *     const { completed, empty, error } = e.detail;
   *     console.log('Form blurred', { completed, empty, error });
   *   });
   *  ```
   */
  blur: InputEvent;
  /**
   * This event will be fired when form is ready to interact with the user.
   *
   * @example
   *  ```ts
   *   element.on('submit',( ) => void);
   *  ```
   */
  submit: undefined;
};
