import React, { useCallback, useEffect, useRef, useState } from 'react'
import { debounce, throttle } from 'throttle-debounce'
import { UStoreProvider } from '@ustore/core'
import { t } from '$themelocalization'
import { Router } from '$routes'
import LoadingDots from '$core-components/LoadingDots'
import Popper from '$core-components/Popper'
import DynamicForm from '$core-components/DynamicForm'
import urlGenerator from '$ustoreinternal/services/urlGenerator'
import theme from '$styles/_theme.scss'
import themeContext from '$ustoreinternal/services/themeContext'
import { isServer } from '$ustoreinternal/services/utils'
import ImageCarousel from '../carousel/ImageCarousel'
import ProductDetails from '../product/ProductDetails'
import ProductDeliveryMethod from '../product/ProductDeliveryMethod'
import ProductOrderSummary from '../product/ProductOrderSummary'
import ProductStickyPrice from '../product/ProductStickyPrice'
import ProductLayout from '../product/ProductLayout'
import Proof from './Proof'
import Price from './Price'
import {
  convertPropertiesFromApiToPropertiesObject,
  getDependenciesObject,
  getPriceOrderItem, getReorder, pushCart,
  pushOrderItem,
  pushProperties,
  pushPropertiesState,
  pushSavedForLater
} from './utils'
import { convertProductPropertiesFormIntoArray, preparingFormDataToSendToServer } from '../../services/utils'
import { isOutOfStock } from '../Inventory'
import ProductQuantity from '../product/ProductQuantity'
import ProductProof from '../product/ProductProof'
import ProductApproval from '../product/ProductApproval'
import './StaticProduct.scss'
import './ProductProperties.scss'
import useErrors from './useErrors'

const State = {
  loading: 'LOADING',
  calculatingPrice: 'CALCULATING_PRICE',
  initial: 'INITIAL',
  clickedAddToCart: 'CLICKED_ADD_TO_CART',
  error: 'ERROR'
}

const StaticProduct = ({
  customState,
  router: { asPath, push: pushRoute },
  state: {
    currentCurrency,
    currentStore: { TaxFormatType, StoreType }
  }
}) => {
  const [pageState, setPageState] = useState(State.loading)
  const [orderItem, setOrderItem] = useState(customState.currentOrderItem || {})
  const [product, setProduct] = useState(customState.currentProduct || {})
  const [productThumbnails, setProductThumbnails] = useState({})
  const [quantity, setQuantity] = useState(1)
  const [price, setPrice] = useState({})
  const [properties, setProperties] = useState({})
  const [propertiesObject, setPropertiesObject] = useState({})
  const [excelPricingEnabled, setExcelPricingEnabled] = useState(false)
  const [productDeliveries, setProductDeliveries] = useState(customState.currentDeliveryServices || null)
  const [deliveryMethod, setDeliveryMethod] = useState(null)
  const [deliveryService, setDeliveryService] = useState(null)
  const [priceError, setPriceError] = useState(null)
  const [quantityError, setQuantityError] = useState(null)
  const [proofUrl, setProofUrl] = useState(null)
  const [proofModalOpen, setProofModalOpen] = useState(false)
  const [approvalModalOpen, setApprovalModalOpen] = useState(false)
  const [popperError, setPopperError] = useState(null)
  const [postLoadingProcedures, setPostLoadingProcedures] = useState(false)
  const [forceAddToCartButtonPopper, setForceAddToCartButtonPopper] = useState(false)
  const [nextTabbableElement, setNextTabbableElement] = useState(null)
  const dynamicFormRef = useRef()
  const serverCallsCount = useRef(0)
  const {
    errors,
    processErrorsOnApiResponse,
    processErrorsOnFormChange,
    processErrorsOnAddToCart,
    showAllErrors,
    excelPricingError
  } = useErrors(properties)

  useEffect(() => {
    window.addEventListener('scroll', onScroll, true)
    window.addEventListener('beforeunload', cleanCustomState, true)
    if (dynamicFormRef.current) {
      dynamicFormRef.current.addEventListener('focus', onChangeFocus, true)
      dynamicFormRef.current.addEventListener('mousedown', onFormMouseDown, true)
      dynamicFormRef.current.addEventListener('keydown', onFormKeyDown, true)
    }

    onScroll()
    loadProductData()

    return () => {
      cleanCustomState()
      window.removeEventListener('scroll', onScroll, true)
      window.removeEventListener('beforeunload', cleanCustomState, true)
      dynamicFormRef.current.removeEventListener('focus', onChangeFocus, true)
      dynamicFormRef.current.removeEventListener('mousedown', onFormMouseDown, true)
      dynamicFormRef.current.removeEventListener('keydown', onFormKeyDown, true)
    }
  }, [])

  const onFormKeyDown = (e) => {
    if (e.keyCode === 9) {
      setNextTabbableElement(e.target)
    }
  }

  const onFormMouseDown = (e) => {
    if (e.target.classList.contains('tabbable')) {
      setNextTabbableElement(e.target)
    }
  }

  const onChangeFocus = (e) => {
    if (e.target.classList.contains('tabbable')) {
      setNextTabbableElement(e.target)
    }
  }

  useEffect(() => {
    setExcelPricingEnabled(product
      .Attributes
      .find((attribute) => attribute.Name === 'PartialPriceCalculationEnabled')
      .Value === 'true')
  }, [product])

  const demiState = {}

  useEffect(() => {
    window.triggerFormPropertyChange = (propertyId, propertyValue) => {
      if (propertiesObject[propertyId]) demiState[propertyId] = propertyValue
      handleFormChange(null, null, null, demiState)
    }
    return () => { window.triggerFormPropertyChange = undefined }
  }, [properties, propertiesObject, errors])

  useEffect(() => {
    if (pageState !== State.loading && pageState !== State.calculatingPrice && postLoadingProcedures) {
      if (!forceAddToCartButtonPopper) {
        if (priceError === 'invalid') {
          setPopperError('GET_PRICE')
        } else if (priceError === 'can not recalculate') {
          setPopperError('PRICE_CAN_NOT_BE_UPDATED')
        } else if (priceError === null) {
          setPopperError(null)
        }
      }
      setPostLoadingProcedures(false)
    }
  }, [
    priceError,
    quantityError,
    pageState,
    excelPricingEnabled,
    postLoadingProcedures,
    forceAddToCartButtonPopper,
    price
  ])

  useEffect(() => {
    if (pageState !== State.loading && pageState !== State.calculatingPrice) setPostLoadingProcedures(true)
    else setPostLoadingProcedures(false)
  }, [pageState])

  const onScroll = useCallback(throttle(250, false, (event = null) => {
    const stickyPanel = document.querySelector('.sticky-price')
    const topMarker = document.querySelector('.static-product-price')
    const header = document.querySelector('.header')
    const bottomMarker = document.querySelector('.add-to-cart-button')

    const headerHeight = header ? header.getBoundingClientRect().height : 0

    if (stickyPanel && bottomMarker) {
      if ((topMarker && topMarker.getBoundingClientRect().top + topMarker.getBoundingClientRect().height >= headerHeight) ||
          bottomMarker.getBoundingClientRect().top <= window.innerHeight) {
        stickyPanel.style.height = '0'
        stickyPanel.style.padding = '0'
        stickyPanel.style.overflow = 'hidden'
      } else {
        stickyPanel.style.height = 'auto'
        stickyPanel.style.padding = '7.5px 0'
        stickyPanel.style.boxShadow = '0 -2px 16px var(--color-line)'
        stickyPanel.style.transition = 'all .3s ease'
        stickyPanel.style.overflow = 'visible'
      }
    }
  }), [])

  const cleanCustomState = () => {
    ['currentProduct',
      'currentOrderItem',
      'currentOrderItemId',
      'currentOrderItemPriceModel',
      'lastOrder',
      'currentProductThumbnails',
      'currentDeliveryServices',
      'CurrentProductDucErrors',
      'ducData',
      'currentProductProperties',
      'isLoadingData'
    ].forEach((property) => {
      UStoreProvider.state.customState.delete(property)
    })
  }

  const checkIfThereArePropertyErrors = (updatedErrors = null, updatedProperties = null) => {
    const productErrors = updatedErrors ? { ...updatedErrors } : { ...errors }
    const propertiesToUse = updatedProperties ? convertPropertiesFromApiToPropertiesObject(updatedProperties) : propertiesObject
    return Object.keys(productErrors)
      .some((propertyId) =>
        productErrors[propertyId] &&
        productErrors[propertyId].errors &&
        productErrors[propertyId].errors.length &&
        propertiesToUse[propertyId].uiSchema['ui:options'].visible
      )
  }

  const checkIfThereAreVisiblePropertyErrors = (updatedErrors = null) => {
    const productErrors = updatedErrors ? { ...updatedErrors } : { ...errors }
    return Object.values(productErrors).some((property) => property.errors.length && property.show)
  }

  const setPricingError = (error) => {
    if (error.ErrorCode && error.ErrorCode === 'ExcelCalculation') {
      const updatedPrice = {
        IsMinimumPrice: true,
        MailingFee: 0,
        Price: { Price: -1, Tax: 0 },
        ProductPrice: 0
      }
      setPrice(updatedPrice)
      UStoreProvider.state.customState.set('currentOrderItemPriceModel', updatedPrice)
      setPriceError('invalid')
      setPostLoadingProcedures(true)
    }
  }

  const updateProperties = async (changedProperties = [], updatedProperties = null, usedQuantity = null, recalculatePrice = true, updatedErrors = null) => {
    try {
      const updatedPropertiesSchema = updatedProperties || properties

      const formDataForApi = preparingFormDataToSendToServer(updatedPropertiesSchema.formData, properties)
      if (changedProperties.includes('quantity')) {
        formDataForApi.push({
          id: 'uStoreOrderItemQuantity',
          value: usedQuantity
        })
      }

      serverCallsCount.current += 1
      const updatedPropertiesFromApi = await pushPropertiesState(
        orderItem.ID,
        formDataForApi
      )
      serverCallsCount.current -= 1
      if (serverCallsCount.current > 0 && !changedProperties.includes('quantity')) {
        return {
          updatedProperties: updatedPropertiesSchema,
          updatedPropertiesObject: convertPropertiesFromApiToPropertiesObject(
            updatedPropertiesSchema,
            getDependenciesObject(updatedPropertiesSchema, excelPricingEnabled) ? getDependenciesObject(updatedPropertiesSchema, excelPricingEnabled).dependenciesObject : null
          )
        }
      }
      const updatedPropertiesObject = convertPropertiesFromApiToPropertiesObject(
        updatedPropertiesFromApi,
        getDependenciesObject(updatedPropertiesFromApi, excelPricingEnabled) ? getDependenciesObject(updatedPropertiesFromApi, excelPricingEnabled).dependenciesObject : null
      )
      setProperties(updatedPropertiesFromApi)
      setPropertiesObject(updatedPropertiesObject)
      UStoreProvider.state.customState.set('currentProductProperties', updatedPropertiesFromApi)
      const updatedErrorsFromApi = await processErrorsOnApiResponse(
        updatedPropertiesFromApi,
        updatedErrors || errors,
        changedProperties
      )
      if (recalculatePrice && !checkIfThereAreVisiblePropertyErrors(updatedErrorsFromApi)) {
        await calculateProperties(usedQuantity || quantity, null, updatedPropertiesFromApi, updatedErrorsFromApi)
      }
      return { updatedPropertiesFromApi, updatedPropertiesObject }
    } catch (e) {
      console.error(e)
      // Excel template V15.0 error
      setPricingError(e)
      return null
    } finally {
      if (nextTabbableElement) {
        nextTabbableElement.focus()
      }
    }
  }

  const calculateProperties = throttle(750, async (usedQuantity, updatedOrderItem = null, updatedProperties = null, updatedErrors = null) => {
    if (price.Price === null) return
    setPageState(State.calculatingPrice)
    const updatedPrice = await getPriceOrderItem(
      updatedOrderItem ? updatedOrderItem.ID : orderItem.ID,
      {
        ...updatedOrderItem || orderItem,
        Properties: ((updatedProperties && Object.keys(updatedProperties).length)) || (properties && Object.keys(properties).length) ? convertProductPropertiesFormIntoArray(
          updatedProperties || properties,
          excelPricingEnabled
        ) : null,
        Quantity: usedQuantity
      })
    if (updatedPrice.Price === null) {
      setPrice(updatedPrice)
      setPageState(State.initial)
      if (nextTabbableElement) {
        nextTabbableElement.focus()
      }
      return
    }
    if (updatedErrors && updatedProperties) {
      const errorsExist = checkIfThereArePropertyErrors(updatedErrors, updatedProperties)
      if (errorsExist && (updatedPrice.Price.Price === -1 || updatedPrice.IsMinimumPrice)) {
        setPriceError('can not recalculate')
        setPostLoadingProcedures(true)
      } else if (!errorsExist && (updatedPrice.Price.Price === -1 || updatedPrice.IsMinimumPrice)) {
        setPricingError({
          ErrorCode: 'ExcelCalculation'
        })
        setPageState(State.initial)
        if (nextTabbableElement) {
          nextTabbableElement.focus()
        }
        return
      } else {
        setPriceError(null)
      }
    }
    setPrice(updatedPrice)
    UStoreProvider.state.customState.set('currentOrderItemPriceModel', updatedPrice)
    setPageState(State.initial)
    if (nextTabbableElement) {
      nextTabbableElement.focus()
    }
  })

  const loadProductDeliveries = async (productFromApi, orderItemId) => {
    if (productFromApi.Configuration.Delivery.Mailing.Enabled) {
      const deliveriesFromApi = await UStoreProvider.api.orders.getDeliveryServices(orderItemId)
      setProductDeliveries(deliveriesFromApi)
      UStoreProvider.state.customState.set('currentProductDeliveries', deliveriesFromApi)
    }
  }

  const loadProductProperties = async (updatedOrderItem, initialQuantity) => {
    try {
      const propertiesFromApi = await UStoreProvider.api.orders.getProperties(updatedOrderItem.ID)
      const updatedPropertiesObject = convertPropertiesFromApiToPropertiesObject(
        propertiesFromApi,
        getDependenciesObject(propertiesFromApi, excelPricingEnabled) ? getDependenciesObject(propertiesFromApi, excelPricingEnabled).dependenciesObject : null
      )
      setProperties(propertiesFromApi)
      setPropertiesObject(updatedPropertiesObject)
      UStoreProvider.state.customState.set('currentProductProperties', { ...propertiesFromApi })
      const updatedErrors = await processErrorsOnApiResponse(propertiesFromApi, null)
      if (!checkIfThereAreVisiblePropertyErrors(updatedErrors)) {
        await calculateProperties(initialQuantity, updatedOrderItem, propertiesFromApi, updatedErrors)
      } else {
        setPricingError({
          ErrorCode: 'ExcelCalculation'
        })
      }
    } catch (e) {
      console.error(e)
      // Excel template V15.0 error
      setPricingError(e)
    }
  }

  const loadProductProofUrl = (productFromApi, orderItemId) => {
    setProofUrl(productFromApi.Proof ? `${productFromApi.Proof.Url}&OrderItemID=${orderItemId}` : null)
  }

  const loadProductData = async () => {
    if (product.ID) {
      setPageState(State.loading)
      const productFromApi = await UStoreProvider.api.products.getProductByID(product.ID)
      setProduct(productFromApi)
      const productThumbnailsFromApi = await UStoreProvider.api.products.getProductThumbnails(productFromApi.ID)
      setProductThumbnails(productThumbnailsFromApi)
      let orderItemFromApi
      if (customState.currentOrderItemId) orderItemFromApi = await UStoreProvider.api.orders.getOrderItem(customState.currentOrderItemId)
      else orderItemFromApi = await UStoreProvider.api.orders.addOrderItem(productFromApi.ID)
      setOrderItem(orderItemFromApi)
      setQuantity(orderItemFromApi.Quantity)
      setDeliveryMethod(orderItemFromApi.DeliveryMethod)
      setDeliveryService(orderItemFromApi.DeliveryServiceID)
      const lastOrderFromApi = await UStoreProvider.api.orders.getLastOrder(productFromApi.ID) // null
      await loadProductDeliveries(productFromApi, orderItemFromApi.ID)
      loadProductProofUrl(productFromApi, orderItemFromApi.ID)

      await loadProductProperties(orderItemFromApi, orderItemFromApi.Quantity)

      UStoreProvider.state.customState.setBulk({
        currentProductThumbnails: productThumbnailsFromApi,
        currentOrderItem: orderItemFromApi,
        lastOrder: lastOrderFromApi,
        currentProduct: productFromApi
      })
      setPageState(State.initial)
    }
  }

  const reRouteToCart = (storeType, cartUrl) => {
    if (storeType === 3 && cartUrl) {
      const decoded = decodeURIComponent(cartUrl)
      window.location.href = `${decoded}${decoded.includes('?') ? '&' : '?'}OrderProductId=${orderItem.FriendlyID}`
    } else {
      Router.pushRoute(urlGenerator.get({ page: 'cart' }))
    }
  }

  const reRouteToNewOrder = (productId, productName, newOrderId) => {
    pushRoute(`${urlGenerator.get({
      page: 'products',
      id: productId,
      name: decodeURI(productName) })}?OrderItemId=${newOrderId}&reorder=true`)
  }

  const handleQuantityChange = async (newQuantity, error) => {
    try {
      if (error) {
        setQuantityError('invalid')
        if (newQuantity === '') setQuantity('')
        return
      } else {
        setQuantityError(null)
      }

      if (checkIfThereArePropertyErrors()) {
        if (!excelPricingEnabled) {
          if (checkIfThereAreVisiblePropertyErrors()) {
            return
          } else {
            setPriceError('can not recalculate')
            setPostLoadingProcedures(true)
            return
          }
        }
      }

      if (pageState !== State.loading) {
        await updateProperties(['quantity'], null, newQuantity)
        setPageState(State.initial)
        if (nextTabbableElement) {
          nextTabbableElement.focus()
        }
      }
    } catch (e) {
      setPageState(State.initial)
      if (nextTabbableElement) {
        nextTabbableElement.focus()
      }
      console.error(e)
      setPricingError(e)
    }

    setQuantity(newQuantity)
    setOrderItem({
      ...orderItem,
      Quantity: newQuantity
    })
  }

  const addToCartOrSave = async () => {
    // If product is out of stock - save it for later
    const productStockQuantity = product.Inventory && product.Inventory.Quantity
    const minQuantity = product.Configuration &&
    product.Configuration.Quantity &&
    product.Configuration.Quantity.Minimum
      ? product.Configuration.Quantity.Minimum
      : 0
    const allowOutOfStockPurchase = product.Inventory && product.Inventory.AllowOutOfStockPurchase
    if (
      product.Inventory &&
        isOutOfStock(
          productStockQuantity,
          minQuantity,
          allowOutOfStockPurchase
        )
    ) {
      await pushSavedForLater(orderItem.ID)
    } else {
      if (properties && Object.keys(properties).length) {
        await pushProperties(
          orderItem.ID,
          convertProductPropertiesFormIntoArray(
            properties,
            excelPricingEnabled
          )
        )
      }
      await pushOrderItem(orderItem.ID, {
        ...orderItem,
        Properties: properties && Object.keys(properties).length ? convertProductPropertiesFormIntoArray(
          properties,
          excelPricingEnabled
        ) : null
      })
      await pushCart(orderItem.ID)
    }
    reRouteToCart(StoreType, themeContext.get('cartUrl'))
  }

  const handleAddToCartButtonClick = async () => {
    if (pageState !== State.initial) return

    // Error checking
    if (quantityError) {
      setForceAddToCartButtonPopper(true)
      setPopperError('VALIDATION_ERROR')
      setPostLoadingProcedures(true)
      return
    } else if ((priceError === 'invalid' && !((price.Price && price.Price.Price === -1) || price.IsMinimumPrice)) || excelPricingError) {
      setForceAddToCartButtonPopper(true)
      setPopperError('SOMETHING_WENT_WRONG')
      setPostLoadingProcedures(true)
      return
    } else if (priceError === 'invalid' && ((price.Price && price.Price.Price === -1) || price.IsMinimumPrice)) {
      setForceAddToCartButtonPopper(true)
      setPopperError('GET_PRICE')
      setPostLoadingProcedures(true)
      return
    } else if (checkIfThereArePropertyErrors()) {
      setForceAddToCartButtonPopper(true)
      setPopperError('VALIDATION_ERROR')
      setPostLoadingProcedures(true)
      showAllErrors()
      return
    }

    const updatedProperties = await updateProperties(Object.keys(propertiesObject))
    if (!updatedProperties) return
    if (priceError) {
      setForceAddToCartButtonPopper(true)
      setPopperError('SOMETHING_WENT_WRONG')
      setPostLoadingProcedures(true)
      return
    }
    const updatedErrors = await processErrorsOnAddToCart(updatedProperties.updatedPropertiesFromApi)
    if (checkIfThereArePropertyErrors(updatedErrors)) {
      setForceAddToCartButtonPopper(true)
      setPopperError('VALIDATION_ERROR')
      setPostLoadingProcedures(true)
      return
    }
    setPageState(State.loading)

    // If product requires proof approval, open form approval modal and exit
    if (product.Configuration.Proof && product.Configuration.Proof.RequireProofApproval) {
      if (price && price.Price) await calculateProperties(quantity, null, updatedProperties.updatedPropertiesFromApi, updatedErrors)
      setApprovalModalOpen(true)
      setPageState(State.initial)
      return
    }

    await addToCartOrSave()
  }

  const handleDeliveryChange = async (newDeliveryMethod, newDeliveryServiceId) => {
    setDeliveryMethod(newDeliveryMethod)
    setDeliveryService(newDeliveryServiceId)
    const updatedOrderItem = {
      ...orderItem,
      DeliveryMethod: newDeliveryMethod,
      DeliveryServiceID: newDeliveryServiceId
    }
    setOrderItem(updatedOrderItem)
    UStoreProvider.state.customState.set('currentOrderItem', updatedOrderItem)
    if (Object.keys(price).length && price.Price) calculateProperties(quantity, updatedOrderItem)
  }

  const onFormChange = (...args) => {
    setPageState(State.loading)
    handleFormChange(...args)
  }

  const handleFormChange = async (
    propertyId = null,
    propertyValue = undefined,
    propertyErrors = null,
    formChanges = {}
  ) => {
    setPriceError(null)
    const updatedPropertiesSchema = properties
    if (Object.keys(formChanges).length) {
      updatedPropertiesSchema.formData = {
        ...properties.formData,
        ...formChanges
      }
    } else {
      updatedPropertiesSchema.formData = {
        ...properties.formData,
        [propertyId]: propertyValue
      }
    }

    const updatedErrors = processErrorsOnFormChange(
      Object.keys(formChanges).length ? Object.keys(formChanges) : propertyId,
      propertyErrors,
      updatedPropertiesSchema,
      propertyValue
    )

    const updatedFormData = {
      ...properties,
      formData: updatedPropertiesSchema.formData
    }

    const propsFromApiToPropsObject = convertPropertiesFromApiToPropertiesObject(
      updatedFormData,
      getDependenciesObject(updatedFormData, excelPricingEnabled) ? getDependenciesObject(updatedFormData, excelPricingEnabled).dependenciesObject : null
    )

    // Set temporal local state
    setPropertiesObject(propsFromApiToPropsObject)
    setProperties({
      ...properties,
      formData: updatedPropertiesSchema.formData
    })

    if (!quantityError) {
      // Check if there are validation errors on changed property
      if ((!Object.keys(formChanges).length && updatedErrors[propertyId] && updatedErrors[propertyId].errors.length) ||
          (Object.keys(formChanges).length && checkIfThereArePropertyErrors(updatedErrors))
      ) {
        // Errors already shown as part of errors processing - processErrorsOnFormChange
        setPageState(State.initial)
        if (nextTabbableElement) {
          nextTabbableElement.focus()
        }
      } else if (checkIfThereArePropertyErrors(updatedErrors)) {
        if (excelPricingEnabled) {
          await updateProperties(
            Object.keys(formChanges).length ? Object.keys(formChanges) : [propertyId],
            updatedPropertiesSchema,
            null,
            true,
            updatedErrors
          )
          setPageState(State.initial)
          if (nextTabbableElement) {
            nextTabbableElement.focus()
          }
        } else if (!excelPricingEnabled && checkIfThereAreVisiblePropertyErrors(updatedErrors)) {
          await updateProperties(
            Object.keys(formChanges).length ? Object.keys(formChanges) : [propertyId],
            updatedPropertiesSchema,
            null,
            false,
            updatedErrors
          )
          setPageState(State.initial)
          if (nextTabbableElement) {
            nextTabbableElement.focus()
          }
        } else if (!excelPricingEnabled && !checkIfThereAreVisiblePropertyErrors(updatedErrors)) {
          await updateProperties(
            Object.keys(formChanges).length ? Object.keys(formChanges) : [propertyId],
            updatedPropertiesSchema,
            null,
            false,
            updatedErrors
          )
          setPageState(State.initial)
          if (nextTabbableElement) {
            nextTabbableElement.focus()
          }
          setPriceError('can not recalculate')
          setPostLoadingProcedures(true)
        }
      } else {
        await updateProperties(
          Object.keys(formChanges).length ? Object.keys(formChanges) : [propertyId],
          updatedPropertiesSchema,
          null,
          true,
          updatedErrors
        )
        setPageState(State.initial)
        if (nextTabbableElement) {
          nextTabbableElement.focus()
        }
      }
    } else {
      setPageState(State.initial)
      if (nextTabbableElement) {
        nextTabbableElement.focus()
      }
    }
  }

  const handlePropertyBlur = (propertyId) => {
    const updatedErrors = processErrorsOnFormChange(propertyId, null, properties)
    processErrorsOnApiResponse(
      properties,
      updatedErrors,
      [propertyId]
    )
  }

  const handleReorder = async () => {
    const { lastOrder } = UStoreProvider.state.customState.get()
    setPageState(State.loading)
    const newOrder = await getReorder(lastOrder.OrderItemID)
    reRouteToNewOrder(product.FriendlyID, product.Name, newOrder.ID)
  }

  const getContinueButtonText = (proofModal = false) => {
    if (
      product.Configuration.Proof &&
      product.Configuration.Proof.RequireProofApproval &&
      !proofModal
    ) return t('product.review_approve')
    if (
      product &&
        product.Inventory &&
        Object.keys(product.Inventory).includes('Quantity') &&
        Object.keys(product.Inventory).includes('AllowOutOfStockPurchase') &&
        isOutOfStock(
          product.Inventory.Quantity,
          product.Configuration.Quantity.Minimum,
          product.Inventory.AllowOutOfStockPurchase
        )
    ) return t('product.save_for_later')
    return t('product.add_to_cart')
  }

  const isMobile = !isServer() &&
      document.body.clientWidth < parseInt(theme.md.replace('px', ''))

  return (
    <ProductLayout className='static-product'>
      <left is="custom">
        <ImageCarousel
          showTitle
          images={productThumbnails.Thumbnails}
        />
        { product.Proof &&
            <Proof
              currentProduct={product}
              isMobile={false}
              hasThumbnails
              hideLink={false}
              orderItemId={orderItem.ID}
              onToggle={() => setProofModalOpen(!proofModalOpen)}
            />
        }
      </left>
      <right is="custom">
        <ProductDetails
          className='static-product'
          productModel={product}
          minimumQuantity={product.Configuration ? product.Configuration.Quantity.Minimum : null}
          reorderModel={!asPath.includes('reorder') ? UStoreProvider.state.customState.get('lastOrder') : null}
          onReorder={handleReorder}
          showInStock
          langCode={themeContext.get('languageCode')}
        />
        {isMobile &&
                <div className='image-carousel-mobile'>
                  <ImageCarousel
                    zoomAllowed={false}
                    images={productThumbnails.Thumbnails}
                  />
                  <Proof
                    currentProduct={product}
                    hideLink
                    isMobile
                    hasThumbnails={productThumbnails.Thumbnails && productThumbnails.Thumbnails.length > 1}
                    orderItemId={orderItem.ID}
                    onToggle={() => setProofModalOpen(!proofModalOpen)}
                  />
                </div>
        }
        <Price
          isPriceCalculating={pageState === State.calculatingPrice}
          price={price} showMinimumPrice={!!price.IsMinimumPrice}
        />
        <div className='static-product-wizard'>
          <div ref={dynamicFormRef} className='static-product-properties product-properties'>
            <div className='quantity'>
              <span className='quantity-label'>{t('product.quantity')}</span>
              <ProductQuantity
                supportsInventory
                productModel={product}
                orderModel={orderItem}
                onQuantityChange={handleQuantityChange}
                disabled={pageState === State.loading || pageState === State.calculatingPrice}
              />
            </div>
            <DynamicForm
              properties={propertiesObject}
              excelPricingEnabled={excelPricingEnabled}
              errors={errors}
              onChange={onFormChange}
              formData={properties.formData}
              onBlur={handlePropertyBlur}
              disabled={pageState === State.loading}
            />
          </div>
        </div>
        <ProductDeliveryMethod
          className='static-delivery-method'
          productModel={product}
          onDeliveryChange={handleDeliveryChange}
          currentDeliveryMethod={deliveryMethod}
          currentDeliveryServiceID={deliveryService}
          deliveryServices={productDeliveries}
        />
        <ProductOrderSummary
          currency={currentCurrency.Code}
          deliveryMethod={deliveryMethod}
          className='static-order-summary'
          productModel={product}
          quantity={quantity}
          taxFormatType={TaxFormatType}
          priceModel={Object.keys(price).length ? price : null}
          isPriceCalculating={pageState === State.calculatingPrice}
        />
        <ProductProof
          onAddToCartClick={handleAddToCartButtonClick}
          isModalOpen={proofModalOpen}
          modalClassName="static-product-proof-modal"
          src={proofUrl}
          type={product.Proof && product.Proof.MimeType ? product.Proof.MimeType : ''}
          onCloseModal={() => setProofModalOpen(!proofModalOpen)}
          isMobile={isMobile}
        />
        <ProductApproval
          isModalOpen={approvalModalOpen}
          src={proofUrl}
          type={product.Proof && product.Proof.MimeType ? product.Proof.MimeType : ''}
          onCloseModal={() => setApprovalModalOpen(!approvalModalOpen)}
          onAddToCartClick={() => addToCartOrSave()}
          addToCartBtnText={getContinueButtonText(true)}
          checkboxText={
            product.Configuration &&
                  product.Configuration.Proof &&
                  product.Configuration.Proof.ProofApprovalText
              ? product.Configuration.Proof.ProofApprovalText
              : null
          }
          errorText={
            product.Configuration &&
                  product.Configuration.Proof &&
                  product.Configuration.Proof.ProofApprovalValidationMessage
              ? product.Configuration.Proof.ProofApprovalValidationMessage
              : null
          }
        />
        <div className='add-to-cart-button-wrapper'>
          <div
            id="add-to-cart-button"
            className='button button-primary add-to-cart-button'
            onClick={handleAddToCartButtonClick}
          >
            {
              pageState === State.loading ||
                pageState === State.calculatingPrice
                ? <LoadingDots/>
                : getContinueButtonText()}
          </div>
        </div>
        <Popper
          errorCode={popperError}
          forceAddToCartButton={forceAddToCartButtonPopper}
          resetError={() => {
            setPopperError(null)
            setForceAddToCartButtonPopper(false)
          }}
        />
      </right>
      <sticky className='hidden' is="custom">
        <ProductStickyPrice
          longPrice={TaxFormatType === 3}
          onClick={handleAddToCartButtonClick}
          addToCartBtnText={getContinueButtonText()}
          priceModel={price}
          isPriceLoading={pageState === State.loading || pageState === State.calculatingPrice}
          disabled={pageState === State.loading || pageState === State.calculatingPrice}
          showMinimumPrice={!!price.IsMinimumPrice}
        />
      </sticky>
    </ProductLayout>
  )
}

export default StaticProduct
