<template>
  <div>
    <div v-show="squareLoading" class="am-fs__square-loading" :style="cssVars">
      <!-- Skeleton -->
      <el-skeleton animated>
        <el-skeleton-item />
      </el-skeleton>
      <!-- /Skeleton -->
    </div>
    <!-- Credit Card via Square-->
    <div v-show="!squareLoading" class="am-fs__payment-square" :style="cssVars">
      <div class="am-fs__payment-square__google-pay">
        <div id="google-pay-button" />
      </div>

      <div v-if="applePayReady" class="am-fs__payment-square__apple-pay">
        <div id="apple-pay-button" />
      </div>

      <div class="am-fs__payment-divider">
        <span class="am-divider-text">{{ amLabels.payment_or_pay_with_card }}</span>
      </div>

      <div id="payment-status-container"></div>
      <div id="card-container"></div>
    </div>
    <!-- /Credit Card via Square-->
  </div>
</template>

<script setup>
import { computed, inject, onMounted, ref, watchEffect, nextTick, watch } from 'vue'
import {
  getErrorMessage,
  useBookingData,
  useCreateBooking,
  useCreateBookingError,
  useCreateBookingSuccess,
} from '../../../../assets/js/public/booking.js'
import { useColorTransparency } from '../../../../assets/js/common/colorManipulation.js'
import { useStore } from 'vuex'
import httpClient from '../../../../plugins/axios'
import { useScrollTo } from '../../../../assets/js/common/scrollElements'

// * Global settings
const amSettings = inject('settings')
const store = useStore()

// * Labels
const amLabels = inject('amLabels')

// * Colors
let amColors = inject('amColors')

// * Css variables
let cssVars = computed(() => {
  return {
    '--am-c-pay-text': amColors.value.colorMainText,
    '--am-c-pay-text-op60': useColorTransparency(amColors.value.colorMainText, 0.6),
  }
})

// * Components Emits
const emits = defineEmits(['payment-error'])

const { nextStep, footerButtonReset, footerButtonClicked } = inject('changingStepsFunctions', {
  nextStep: () => {},
  footerButtonReset: () => {},
  footerButtonClicked: {
    value: false,
  },
})

const cardInstance = ref(null)

async function continueWithBooking() {
  footerButtonReset()
  store.commit('booking/setLoading', true)

  if (!cardInstance.value) {
    store.commit('booking/setLoading', true)
  }

  const totalAmount = await payingNow()
  const token = await squareTokenize(cardInstance.value, totalAmount.formattedAmount)
  if (!token) {
    store.commit('booking/setLoading', false)
    return
  }
  await createSquarePayment(token)
}

// * Watching when footer button was clicked
watchEffect(() => {
  if (footerButtonClicked.value) {
    if (!store.getters['booking/getCouponValidated']) {
      footerButtonReset()
      emits('payment-error', amLabels.value.coupon_mandatory)
    } else {
      continueWithBooking()
    }
  }
})

const cardReady = ref(false)
const googlePayReady = ref(false)
const applePayReady = ref(true)
const squareLoading = computed(() => !cardReady.value)
const paymentTotalAmount = ref(null)

let paymentRequest = null
const walletInstance = ref({ googlePay: null, applePay: null })

// * Watch coupon changes and payment deposit changes to update paymentRequest amount
// Apple Pay - No async operations can be called between the user gesture (click) and tokenize().
watch(
  [() => store.getters['booking/getCoupon'], () => store.getters['booking/getPaymentDeposit']],
  async () => {
    if (paymentRequest && store.getters['booking/getCouponValidated']) {
      // Wait for next tick to ensure DOM is updated
      await nextTick()
      paymentRequest = await buildPaymentRequest()

      const initWallet = async (method, target, onError) => {
        try {
          walletInstance.value[target] = await payments[method](paymentRequest)
        } catch (err) {
          console.log(err)
          if (onError) onError()
        }
      }

      await initWallet('googlePay', 'googlePay')
      await initWallet('applePay', 'applePay', () => {
        applePayReady.value = false
      })
    }
  },
)

async function getAmountToPay() {
  let checkoutPaymentData = null

  await httpClient
    .post('/payments/amount', useBookingData(store, null, true, {}, null)['data'])
    .then((response) => {
      checkoutPaymentData = response.data.data
    })
    .catch((e) => {
      const message = e?.response?.data?.message || e.message || 'Unknown error'
      emits('payment-error', message)
    })

  const totalPriceParts = new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: checkoutPaymentData.currency,
  }).formatToParts(checkoutPaymentData.amount)

  const IntegerPart = totalPriceParts.find((part) => part.type === 'integer')?.value || ''
  const fractionPart = totalPriceParts.find((part) => part.type === 'fraction')?.value || ''
  const decimalPart = totalPriceParts.find((part) => part.type === 'decimal')?.value || ''
  const formattedAmount = `${IntegerPart}${decimalPart}${fractionPart}`
  paymentTotalAmount.value = formattedAmount

  return {
    formattedAmount,
    rawAmount: checkoutPaymentData.amount,
    countryCode: checkoutPaymentData.countryCode,
  }
}

async function payingNow() {
  return await getAmountToPay()
}

onMounted(async () => {
  // Defer mounting logic until DOM is visible
  cardReady.value = false
  googlePayReady.value = false

  await nextTick()
  await initSquarePayment()
})

const squareLocationId = amSettings.payments.square.locationId
const squareClientId = amSettings.payments.square.testMode
  ? amSettings.payments.square.clientTestId
  : amSettings.payments.square.clientLiveId

const bookingData = useBookingData(store, null, true, {}, null)

let paymentStepRef = inject('paymentRef')

const payments = window.Square.payments(squareClientId, squareLocationId)

async function buildPaymentRequest() {
  try {
    const paymentInfo = await payingNow()
    if (!paymentInfo) return null
    return payments.paymentRequest({
      countryCode: paymentInfo.countryCode,
      currencyCode: bookingData.data.payment.currency,
      total: {
        amount: paymentInfo.formattedAmount.toString(),
        label: 'Total',
      },
    })
  } catch (e) {
    console.log(e)
    return null
  }
}

async function setupDigitalWallet({ buttonId, type, readyRef }) {
  try {
    const btnEl = document.getElementById(buttonId)
    if (!btnEl) return

    if (type === 'googlePay') {
      btnEl.innerHTML = ''
    }

    if (!paymentRequest) return

    walletInstance.value[type] = await payments[type](paymentRequest)

    // Google Pay needs to render its own button via attach
    if (type === 'googlePay') {
      await walletInstance.value[type].attach(`#${buttonId}`)
    }

    readyRef.value = true

    btnEl.addEventListener('click', async () => {
      store.commit('setLoading', true)
      store.commit('booking/setLoading', true)

      const token = await squareTokenize(walletInstance.value[type], paymentTotalAmount.value)
      if (!token) {
        store.commit('setLoading', false)
        store.commit('booking/setLoading', false)
        return
      }
      await createSquarePayment(token)
    })
  } catch (e) {
    console.log(e)
    if (type === 'applePay') {
      readyRef.value = false
    }
  }
}

async function initSquarePayment() {
  const squareCardStyle = {
    '.input-container': {
      borderColor: '#d9d9d9',
      borderRadius: '6px',
    },
  }

  try {
    const cardContainer = document.getElementById('card-container')
    if (cardContainer) {
      cardContainer.innerHTML = ''
    }
    const card = await payments.card({
      style: squareCardStyle,
    })
    await card.attach('#card-container')
    cardInstance.value = card

    cardReady.value = true
  } catch (e) {
    const statusContainer = document.getElementById('payment-status-container')
    console.error(e)
    store.commit('setLoading', false)
    store.commit('booking/setLoading', false)

    if (statusContainer) {
      statusContainer.className = 'missing-credentials'
      statusContainer.style.visibility = 'visible'
    }
  }

  paymentRequest = await buildPaymentRequest()
  await setupDigitalWallet({
    buttonId: 'google-pay-button',
    type: 'googlePay',
    readyRef: googlePayReady,
  })
  await setupDigitalWallet({
    buttonId: 'apple-pay-button',
    type: 'applePay',
    readyRef: applePayReady,
  })
}

const squareTokenize = async (payments, totalAmount) => {
  try {
    const { token, status, errors } = await payments.tokenize({
      amount: totalAmount.toString(),
      billingContact: {
        familyName: bookingData.data.bookings[0].customer.lastName,
        givenName: bookingData.data.bookings[0].customer.firstName,
        email: bookingData.data.bookings[0].customer.email,
        phone: bookingData.data.bookings[0].customer.phone,
      },
      customerInitiated: true,
      sellerKeyedIn: false,
      currencyCode: bookingData.data.payment.currency,
      intent: 'CHARGE',
    })

    if (status === 'OK') {
      return token
    } else if (status === 'Invalid' && errors.length > 0) {
      const messages = errors.map((err) => err.message)
      emits('payment-error', messages.join(', '))
      useScrollTo(paymentStepRef.value, paymentStepRef.value, 20, 300)
      return ''
    }
  } catch (e) {
    console.log(e)
  }
}

const createSquarePayment = async (token) => {
  if (!token) {
    return
  }
  useCreateBooking(
    store,
    useBookingData(
      store,
      null,
      false,
      {
        locationId: squareLocationId,
        sourceId: token,
        idempotencyKey: window.crypto.randomUUID(),
      },
      null,
    ),
    function (response) {
      successBooking(response)
    },
    (response) => {
      errorBooking(response)
    },
  )
}

function successBooking(response) {
  useCreateBookingSuccess(store, response, function () {
    nextStep()
  })
}

function getSquareBookingErrorResponse(error) {
  if (error?.response?.data) {
    return error.response.data
  }

  if (error?.data) {
    return error
  }

  const labels = amLabels.value || amLabels

  return {
    data: {
      message: error?.message || labels.payment_error,
    },
  }
}

function errorBooking(error) {
  useCreateBookingError(store, getSquareBookingErrorResponse(error), () => {
    emits('payment-error', getErrorMessage())
  })
}
</script>

<script>
export default {
  name: 'PaymentSquare',
}
</script>

<style lang="scss">
.amelia-v2-booking {
  #amelia-container {
    .am-fs__square-loading {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      width: 100%;

      .el-skeleton {
        display: flex;
        align-items: center;
        flex-direction: column;
        gap: 10px;
        width: 100%;

        &__item {
          width: 100%;
          height: 40px;
        }
      }
    }

    .am-fs__payment-square {
      display: flex;
      flex-direction: column;
      gap: 6px;

      span {
        display: inline-flex;
        font-size: 14px;
        font-weight: 500;
        line-height: 1.42857;
        color: #6772e5;
      }

      &__google-pay {
        #google-pay-button {
          width: 100%;
          div {
            button {
              width: 100%;
              display: flex;
              justify-content: center;
              align-items: center;
            }
          }
        }
      }

      // Apple Pay button styling
      &__apple-pay {
        #apple-pay-button {
          height: 40px;
          width: 100%;
          font-size: 15px;
          display: inline-block;
          -webkit-appearance: -apple-pay-button;
          -apple-pay-button-type: plain;
          -apple-pay-button-style: black;
        }
      }

      #card-container {
        .sq-card-iframe-container {
          border: 1px solid #d9d9d9;
        }
      }

      .am-fs__payment-divider {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        margin: 16px 0;
        text-align: center;
      }

      .am-fs__payment-divider::before,
      .am-fs__payment-divider::after {
        content: '';
        flex-grow: 1;
        height: 1px;
        background-color: var(--am-c-pay-text-op60);
        margin: 0 8px;
      }

      .am-divider-text {
        font-size: 14px;
        color: var(--am-c-pay-text-op60);
        text-transform: uppercase;
        line-height: 1.33333;
        font-weight: 500;
      }
    }
  }
}
</style>
