/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable max-len */
import './style.css'

import { ThemeOptions } from '@mui/material'
import Alert from '@mui/material/Alert'
import CircularProgress from '@mui/material/CircularProgress'
import Container from '@mui/material/Container'
import { createTheme, ThemeProvider } from '@mui/material/styles'
import { CSSProperties } from '@mui/styles'
import { Elements, PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js'
import {
  loadStripe,
  StripeConstructorOptions,
  StripeElementsOptions,
} from '@stripe/stripe-js'
import { StripePaymentElementOptions } from '@stripe/stripe-js'
import { AxiosError } from 'axios'
import _get from 'lodash/get'
import _identity from 'lodash/identity'
import _isEmpty from 'lodash/isEmpty'
import _map from 'lodash/map'
import React, { useCallback, useEffect, useMemo, useState } from 'react'

import { getPaymentData } from '../../api'
import { FEES_STYLES } from '../../constants'
import { usePixel } from '../../hooks/usePixel'
import { createFixedFloatNormalizer, currencyNormalizerCreator } from '../../normalizers'
import { IAddOn, IOrderData, IPaymentField } from '../../types'
import {
  IPaymentPlanConfig,
  IPaymentPlanConfigCard,
} from '../../types/payment-plan-configuration'
import { CONFIGS, isBrowser } from '../../utils'
import { getQueryVariable } from '../../utils/getQueryVariable'
import { Checkbox, Loader } from '../common/index'
import TimerWidget from '../timerWidget'
import { handlePaymentMiddleWare } from './handlePayment'
import { PaymentPlanSection } from './PaymentPlanSection'

// Wrapper component to access Stripe hooks inside Elements context
const StripeWrapper = ({
  options,
  onStripeReady,
  onPaymentElementChange,
}: {
  options?: StripePaymentElementOptions;
  onStripeReady: (stripe: any, elements: any) => void;
  onPaymentElementChange?: (event: any) => void;
}) => {
  const stripe = useStripe()
  const elements = useElements()

  useEffect(() => {
    if (stripe && elements) {
      onStripeReady(stripe, elements)
    }
  }, [stripe, elements, onStripeReady])

  return <PaymentElement options={options} onChange={onPaymentElementChange} />
}

export interface IPaymentPage {
  paymentFields: IPaymentField[];
  handlePayment: any;
  checkoutData: any;
  formTitle?: string;
  errorText?: string;
  onErrorClose?: () => void;
  onGetPaymentDataSuccess: (value: any) => void;
  onGetPaymentDataError: (value: AxiosError) => void;
  onPaymentError: (value: AxiosError, slug?: string) => void;
  themeOptions?: ThemeOptions & {
    input?: CSSProperties;
    checkbox?: CSSProperties;
  };
  elementsOptions?: StripeElementsOptions;
  paymentElementOptions?: StripePaymentElementOptions;
  onCountdownFinish?: () => void;
  enableTimer?: boolean;
  paymentInfoLabel?: string;
  orderInfoLabel?: string;
  displayPaymentButton?: boolean;
  hidePaymentForm?: boolean;
  hideFieldsBlock?: boolean;
  isSinglePageCheckout?: boolean;
  stripePublishableKey?: string;
  stripeAccountId?: string;
  onStripeReady?: (stripe: any, elements: any) => void;
  onPaymentElementChange?: (event: any) => void;
  enablePaymentPlan?: boolean;
}

const initialPaymentPlanConfiguration: IPaymentPlanConfig = {
  requires_deposit: false,
  deposit: 0,
  interval: 0,
  non_refundable_amount: 0,
  non_refundable_type: null,
  has_admin_fee: false,
  admin_fee: 0,
  total_installments: 0,
  price_per_installment: 0,
  stripe_setup_intent_secret: '',
  total: 0,
  saved_card: {
    last_4_digits: null,
    stripe_payment_method_id: null,
  } as IPaymentPlanConfigCard,
}

const initialOrderValues: IOrderData = {
  id: '',
  product_name: '',
  ticketType: '',
  quantity: '',
  price: '',
  total: '',
  currency: 'USD',
  guest_count: '',
  pay_now: '',
  add_ons: [] as IAddOn[],
  cost: '',
}

const initialReviewValues = {
  order_details: {
    id: '',
    order_hash: '',
  },
  payment_method: {
    stripe_client_secret: '',
    stripe_payment_plan_enabled: false,
    stripe_payment_plan_configuration: {} as any,
    stripe_publishable_key: '',
    id: '',
    name: '',
    stripeConnectedAccount: '',
  },
  billing_info: {},
  event_details: {
    flagSeatMapAllowed: false,
    slug: '',
  },
}

export const PaymentContainer = ({
  paymentFields = [],
  handlePayment,
  formTitle = 'Get Your Tickets',
  errorText,
  checkoutData,
  onErrorClose = _identity,
  onGetPaymentDataSuccess = _identity,
  onGetPaymentDataError = _identity,
  onPaymentError = _identity,
  themeOptions,
  elementsOptions,
  paymentElementOptions,
  onCountdownFinish = _identity,
  enableTimer = false,
  orderInfoLabel = 'Order Review',
  paymentInfoLabel = 'Order Confirmation',
  displayPaymentButton = true,
  hidePaymentForm = false,
  hideFieldsBlock = false,
  isSinglePageCheckout = false,
  stripePublishableKey,
  stripeAccountId,
  onStripeReady = _identity,
  onPaymentElementChange,
  enablePaymentPlan = true,
}: IPaymentPage) => {
  const [reviewData, setReviewData] = useState(initialReviewValues)
  const [orderData, setOrderData] = useState(initialOrderValues)
  const [error, setError] = useState(null)
  const [paymentIsLoading, setPaymentIsLoading] = useState(false)
  const [paymentDataIsLoading, setPaymentDataIsLoading] = useState(true)
  const [currency, setCurrency] = useState('')
  const [showPaymentPlanSection, setShowPaymentPlanSection] = useState(false)

  const [paymentPlanIsAvailable, setPaymentPlanIsAvailable] = useState(false)
  const [paymentPlanConfig, setPaymentPlanConfig] = useState(
    initialPaymentPlanConfiguration
  )
  const [paymentPlanUseSavedCard, setPaymentPlanUseSavedCard] = useState(true)

  const showFormTitle = Boolean(formTitle)
  const showErrorText = Boolean(errorText)

  const eventId =
    getQueryVariable('event_id') || _get(reviewData, 'cart[0].product_id') || ''
  const { hash, total } = checkoutData
  const isFreeTickets = useMemo(
    () => (!Number(total) && !Number(orderData.total)) || !Number(orderData.pay_now),
    [total, orderData]
  )

  const pageUrl = isBrowser ? window.location.href.split('?')[0] : ''
  usePixel(eventId, { page: 'review', pageUrl })


  useEffect(() => {
    const fetchPaymentData = async () => {
      try {
        const paymentDataResponse = await getPaymentData(hash)
        if (paymentDataResponse.success) {
          const attributes = paymentDataResponse?.data?.attributes
          setReviewData(attributes)
          const { cart, order_details } = attributes
          const {
            tickets: [ticket],
          } = order_details

          const orderDataArray = _map(order_details.tickets, item => ({
            product_name: cart[0]?.product_name,
            ticketType: item?.name,
            quantity: item?.guest_count,
            price: item?.price,
            cost: item?.cost,
            id: item.id,
            count: item?.quantity,
          }))

          const orderData = {
            id: order_details?.id,
            product_name: cart[0]?.product_name,
            ticketType: ticket?.name,
            quantity: ticket?.quantity,
            price: ticket?.price,
            total: order_details?.total,
            currency: order_details?.currency,
            add_ons: order_details?.add_ons || [],
            pay_now: order_details?.pay_now || '',
            guest_count: order_details?.guest_count || '',
            debt: order_details?.debt || null,
            tableTypes: orderDataArray,
            cost: ticket?.cost,
            subtotal: order_details?.subtotal,
            fees: order_details?.fees,
          }
          setOrderData(orderData)
          setCurrency(order_details?.currency)
          onGetPaymentDataSuccess(paymentDataResponse.data)
        }
      } catch (e) {
        setError(_get(e, 'response.data.message', null))
        onGetPaymentDataError((e as Record<string, unknown>).response as AxiosError)
      } finally {
        setPaymentDataIsLoading(false)
      }
    }

    if (isSinglePageCheckout) {
      if (!orderData?.total) {
        setOrderData(current => ({ ...current, pay_now: 1, total: 1 }))
        setPaymentDataIsLoading(false)
      }
    } else {
      fetchPaymentData()
    }
  }, [
    orderData,
    hash,
    isSinglePageCheckout,
    onGetPaymentDataError,
    onGetPaymentDataSuccess,
  ])

  const showPaymentForm = () => {
    if (hidePaymentForm) {
      return false
    }

    let showPaymentForm = !isFreeTickets

    if (
      paymentPlanIsAvailable &&
      showPaymentPlanSection &&
      !!paymentPlanConfig.saved_card?.stripe_payment_method_id
    ) {
      showPaymentForm = !paymentPlanUseSavedCard
    }

    return showPaymentForm
  }

  const getPublishableKey = () =>
    stripePublishableKey || _get(reviewData, 'payment_method.stripe_publishable_key')

  const getStripePromise = useCallback(() => {
    const stripePublishableKey = getPublishableKey()
    const stripeAccount =
      stripeAccountId || _get(reviewData, 'payment_method.stripe_connected_account')

    const options: StripeConstructorOptions = {}

    if (stripeAccount) {
      options.stripeAccount = stripeAccount
    }

    return loadStripe(stripePublishableKey, options)
  }, [reviewData, stripePublishableKey])

  const themeMui = createTheme(themeOptions)
  const hasTableTypes = Boolean(Number(orderData.guest_count))
  const paymentFieldsData = hasTableTypes
    ? [
        {
          label: 'Event',
          id: 'product_name',
        },
        {
          label: '',
          id: 'tableTypes',
        },
        {
          label: 'Add-ons',
          id: 'add_ons',
        },
        {
          label: 'Total (incl. fees, card processing and taxes)',
          id: 'total',
          normalizer: (value: string, currency: string) =>
            currencyNormalizerCreator(
              createFixedFloatNormalizer(2)(parseFloat(value)),
              currency
            ),
        },
        {
          label: 'Pay Now',
          id: 'pay_now',
          normalizer: (value: string, currency: string) =>
            currencyNormalizerCreator(
              createFixedFloatNormalizer(2)(parseFloat(value)),
              currency
            ),
        },
        {
          label: 'Pay On Check-in',
          id: 'debt',
          normalizer: (value: string, currency: string) =>
            currencyNormalizerCreator(
              createFixedFloatNormalizer(2)(parseFloat(value)),
              currency
            ),
        },
      ]
    : paymentFields
  const isTable = orderData?.guest_count

  useEffect(() => {
    const paymentMethod = reviewData.payment_method || {}
    const paymentPlanAvailable =
      paymentMethod.stripe_payment_plan_enabled && enablePaymentPlan
    setPaymentPlanIsAvailable(paymentPlanAvailable)

    if (paymentPlanAvailable) {
      const paymentPlanConfig =
        paymentMethod.stripe_payment_plan_configuration || initialPaymentPlanConfiguration
      setPaymentPlanConfig(paymentPlanConfig)
      setPaymentPlanUseSavedCard(!!paymentPlanConfig.saved_card?.stripe_payment_method_id)
      if (isBrowser) {
        const sessionData = window.localStorage.getItem('paymentConfiguration')
        const session = sessionData ? JSON.parse(sessionData) : {}
        if (!!session && session?.orderId == reviewData.order_details?.id) {
          setPaymentPlanUseSavedCard(session.paymentPlanUseSavedCard ?? false)
          setShowPaymentPlanSection(session.showPaymentPlanSection ?? false)
        }
      }
    }
  }, [enablePaymentPlan, reviewData])

  useEffect(() => {
    if (isBrowser && !!orderData?.id) {
      window.localStorage.setItem(
        'paymentConfiguration',
        JSON.stringify({
          paymentPlanUseSavedCard,
          showPaymentPlanSection,
          orderId: orderData?.id,
        })
      )
    }
  }, [showPaymentPlanSection, paymentPlanUseSavedCard, orderData?.id])

  return (
    <ThemeProvider theme={themeMui}>
      <div
        className={`payment_page ${isSinglePageCheckout ? 'payment_page_single' : ''}`}
      >
        {enableTimer && (
          <TimerWidget
            expires_at={_get(reviewData, 'expires_at', 0)}
            buyLoading={paymentIsLoading}
            onCountdownFinish={onCountdownFinish}
          />
        )}
        {isSinglePageCheckout
          ? null
          : error && (
              <Alert severity="error" onClose={onErrorClose} variant="filled">
                {error}
              </Alert>
            )}
        {paymentDataIsLoading && <Loader />}
        {!paymentDataIsLoading && (
          <Container maxWidth="md">
            {showFormTitle && <h1>{isTable ? 'Get Your Tables' : formTitle}</h1>}
            <div className="order_info_text">{orderInfoLabel}</div>
            {!hideFieldsBlock && (
              <div
                className="order_info_section"
                style={{ display: hasTableTypes ? 'block' : 'grid' }}
              >
                {_map(paymentFieldsData, field => {
                  const { id, label, className = '', normalizer = _identity } = field
                  let value = orderData[id as keyof IOrderData] || ''
                  let component = null

                  if (field.id === 'add_ons' && _isEmpty(value)) {
                    return false
                  }

                  if (
                    field.id === 'total' &&
                    paymentPlanIsAvailable &&
                    showPaymentPlanSection
                  ) {
                    value = '' + paymentPlanConfig.total
                  }

                  if (field.id === 'tableTypes') {
                    const valueArray = value as Array<any>

                    component = (
                      <div
                        key={id}
                        className="order_info_block"
                        style={{
                          display: 'flex',
                          flexDirection: 'column',
                        }}
                      >
                        {_map(valueArray, tableTypeItem => (
                          <div
                            key={tableTypeItem.id}
                            style={{
                              display: 'grid',
                              gridTemplateColumns: '33% 33% 33%',
                              gridColumnGap: '10%',
                            }}
                          >
                            <div className="order_info_block">
                              <div className="order_info_title">Table Type</div>
                              <div className={`${className} order_info_text`}>
                                {tableTypeItem.ticketType}
                              </div>
                            </div>
                            <div className="order_info_block">
                              <div className="order_info_title">Number of Tables</div>
                              <div className={`${className} order_info_text`}>
                                {tableTypeItem.count}
                              </div>
                            </div>
                            <div className="order_info_block">
                              <div className="order_info_title">Guest Count</div>
                              <div className={`${className} order_info_text`}>
                                {tableTypeItem.quantity}
                              </div>
                            </div>
                          </div>
                        ))}
                      </div>
                    )
                  }

                  return (
                    component || (
                      <div key={id} className={`order_info_block ${className}`}>
                        <div className="order_info_title">{label}</div>
                        <div className={`${className} order_info_text`}>
                          {typeof value === 'string' || typeof value === 'number'
                            ? normalizer(value, currency, orderData)
                            : _map(value, item => (
                                <div>
                                  <div key={item.id} className="add-on-container">
                                    <span>{item.quantity}</span>
                                    <span className="add-on-x">{' x '}</span>
                                    <span>
                                      {item.groupName ? item.groupName + ' - ' : ''}
                                    </span>
                                    <span>{item.name}</span>
                                    <span>{' - '}</span>
                                    <span>
                                      {CONFIGS.FEES_STYLE === FEES_STYLES.TRADITIONAL &&
                                        currencyNormalizerCreator(
                                          createFixedFloatNormalizer(2)(
                                            parseFloat(item.price)
                                          ),
                                          currency
                                        ) + ' (incl. fees)'}
                                      {CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH &&
                                        currencyNormalizerCreator(
                                          createFixedFloatNormalizer(2)(
                                            parseFloat(item.cost)
                                          ),
                                          currency
                                        )}
                                      {CONFIGS.FEES_STYLE === FEES_STYLES.FINAL_WITH_BREAKDOWN &&
                                        currencyNormalizerCreator(
                                          createFixedFloatNormalizer(2)(
                                            parseFloat(item.price)
                                          ),
                                          currency
                                        )}
                                    </span>
                                    <span className="add-on-each">{' each'}</span>
                                  </div>
                                  {CONFIGS.FEES_STYLE === FEES_STYLES.DISPLAY_BOTH && (
                                    <p className="fees">
                                      {`(${currencyNormalizerCreator(
                                        createFixedFloatNormalizer(2)(
                                          parseFloat(String(item.price))
                                        ),
                                        currency
                                      )} with fees)`}
                                    </p>
                                  )}
                                  {CONFIGS.FEES_STYLE === FEES_STYLES.FINAL_WITH_BREAKDOWN && parseFloat(item.price) - parseFloat(item.cost) > 0 && (
                                    <p className="fees">
                                      {`(${currencyNormalizerCreator(createFixedFloatNormalizer(2)(parseFloat(item.cost)), currency)} + ${currencyNormalizerCreator(createFixedFloatNormalizer(2)(parseFloat(item.price) - parseFloat(item.cost)), currency)} fee)`}
                                    </p>
                                  )}
                                </div>
                              ))}
                        </div>
                      </div>
                    )
                  )
                })}
              </div>
            )}
            {!isFreeTickets && paymentPlanIsAvailable && (
              <div className="payment_toggle">
                <Checkbox
                  label={
                    <span className="payment_plan_toggle_label">
                      Click to checkout using Payment Plan
                    </span>
                  }
                  required={true}
                  onChange={() => {
                    setShowPaymentPlanSection(!showPaymentPlanSection)
                  }}
                  checked={showPaymentPlanSection}
                />
              </div>
            )}
            {showPaymentPlanSection && (
              <PaymentPlanSection
                paymentPlanConfig={paymentPlanConfig}
                currency={currency}
                paymentPlanUseSavedCard={paymentPlanUseSavedCard}
                setPaymentPlanUseSavedCard={setPaymentPlanUseSavedCard}
              />
            )}
            {showPaymentForm() && !!getPublishableKey() ? (
              <div className="payment_info">
                <div className="payment_info_label">{paymentInfoLabel}</div>
                {showErrorText && <p className="payment_info__error">{errorText}</p>}
                <div>
                  {elementsOptions && (
                    <Elements stripe={getStripePromise()} options={elementsOptions}>
                      <StripeWrapper
                        onStripeReady={onStripeReady}
                        options={paymentElementOptions}
                        onPaymentElementChange={onPaymentElementChange}
                      />
                    </Elements>
                  )}
                </div>
              </div>
            ) : displayPaymentButton ? (
              <div
                className={`payment_button ${
                  paymentIsLoading ? 'disabled-payment-button' : ''
                }`}
              >
                <button
                  disabled={paymentIsLoading}
                  type="button"
                  onClick={() => {
                    setPaymentIsLoading(true)

                    let data

                    if (
                      paymentPlanIsAvailable &&
                      showPaymentPlanSection &&
                      paymentPlanUseSavedCard
                    ) {
                      data = {
                        paymentMethodId:
                          paymentPlanConfig.saved_card?.stripe_payment_method_id,
                      }
                    }

                    handlePaymentMiddleWare(
                      data,
                      {},
                      {
                        reviewData,
                        isFreeTickets,
                        paymentPlanIsAvailable,
                        showPaymentPlanSection,
                        handlePayment,
                        setPaymentIsLoading,
                        setError,
                        orderData,
                        eventId,
                        isBrowser,
                        onPaymentError,
                      }
                    )
                  }}
                >
                  {paymentIsLoading ? (
                    <CircularProgress size={26} />
                  ) : isFreeTickets ? (
                    'Complete Registration'
                  ) : (
                    'Confirm Payment Plan'
                  )}
                </button>
              </div>
            ) : null}
          </Container>
        )}
      </div>
    </ThemeProvider>
  )
}
