import React, { useCallback, useEffect, useRef, useState } from 'react'
import { throttle } from 'throttle-debounce'
import { UStoreProvider } from '@ustore/core'
import { t } from '$themelocalization'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { LoadingDots } from '$core-components'
import Popper from './Popper'
import DynamicForm from '../DynamicForm'
import PreviewErrorBalloon from './PreviewErrorBalloon'
import urlGenerator from '$ustoreinternal/services/urlGenerator'
import theme from '$styles/_theme.scss'
import themeContext from '$ustoreinternal/services/themeContext'
import ImageCarousel from '../ImageCarousel'
import ProductDetails from '../ProductDetails'
import ProductDeliveryMethod from '../ProductDeliveryMethod'
import ProductOrderSummary from '../ProductOrderSummary'
import ProductStickyPrice from '../ProductStickyPrice'
import ProductThumbnailsPreview from '../dynamic/ProductThumbnailsPreview'
import RefreshPreviewButton from './RefreshPreviewButton'
import ProductLayout from '../ProductLayout'
import Proof from './Proof'
import Price from './Price'
import {
  convertPropertiesFromApiToPropertiesObject,
  getDependenciesObject,
  getPriceOrderItem, getReorder, pushCart,
  pushOrderItem,
  pushProperties,
  pushPropertiesState,
  pushSavedForLater,
} from './utils'
import {
  fastProofService,
  convertProductPropertiesFormIntoArray,
  preparingFormDataToSendToServer,
  productTypes
} from '$themeservices'
import { isOutOfStock } from '../Inventory'
import ProductQuantity from '../ProductQuantity'
import ProductProof from '../ProductProof'
import ProductApprovalModal from '../ProductApprovalModal'
import useErrors from './useErrors'
import useUpdateProperties from './useUpdateProperties'
import Slot from '../../widgets/Slot'
import './ProductProperties.scss'
import  './StaticProduct.scss'
import {useSticky} from './useSticky'

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


const StaticProduct = ({
  customState,
  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 [thumbnailModalOpen, setThumbnailModalOpen] = useState(false)
  const [popperAffectedSections, setPopperAffectedSections] = useState([])
  const [popperError, setPopperError] = useState(null)
  const [postLoadingProcedures, setPostLoadingProcedures] = useState(false)
  const [forceAddToCartButtonPopper, setForceAddToCartButtonPopper] = useState(false)
  const [htmlDataUpdateTimeout, setHtmlDataUpdateTimeout] = useState(null)
  const [showLoaderDots, setShowLoaderDots] = useState(false)
  const [disabledRefreshPreviewButton, setDisabledRefreshPreviewButton] = useState(false)
  const [lastViewImageId, setLastViewImageId] = useState(0)
  const [isMobile, setIsMobile] = useState( document.body.clientWidth < parseInt(theme.md.replace('px', '')))
  const [sectionToOpen, setSectionToOpen] = useState(0)
  const [searchParams, setSearchParams] = useSearchParams()
  const [poofPreviewError, setPoofPreviewError] = useState(false)
  const topMarkerRef = useRef(null)
  const bottomMarkerRef = useRef(null)
  const { showStickyPrice, stickyConnect, stickyDisconnect } = useSticky(topMarkerRef, bottomMarkerRef)
  const topPriceRef = useRef(null)
  const bottomPriceRef = useRef(null)
  const { addPromise } = useUpdateProperties()
  const navigate = useNavigate()
  const {
    errors,
    processErrorsOnApiResponse,
    processErrorsOnFormChange,
    processErrorsOnAddToCart,
    showAllErrors,
    excelPricingError
  } = useErrors(properties)
  const [showRefreshPreview, setShowRefreshPreview] = useState(false)
  const stickyPriceRef = useRef(null)

  const checkIfThereArePropertyErrors = useCallback((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
      )
  }, [errors, propertiesObject])

  const checkIfThereAreSectionErrors = useCallback(() => {
    return Object.values(errors).some((property) => property.errors.length && property.show && property.section)
  }, [errors])

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

  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)
      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)
        return
      } else if (product.Type === productTypes.DYNAMIC) {
        const keepError = Object.entries(updatedErrors).reduce((r, [key, val]) => {
          return r || (val.show && updatedProperties.JSONSchema.definitions[key]?.custom.affectPrice)
        }, false )
        if (!keepError) {
          setPriceError(null)
        }
      } else {
        setPriceError(null)
      }
    }
    setPrice(updatedPrice)
    UStoreProvider.state.customState.set('currentOrderItemPriceModel', updatedPrice)
    setPageState(State.initial)
  })

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

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

    const handleResponse = async (response, e) => {
      if (e) {
        console.error(e)
        // Excel template V15.0 error
        setPricingError(e)
        return {
          updatedPropertiesFromApi: properties, updatedPropertiesObject: propertiesObject
        }
      }

      const updatedPropertiesObject = convertPropertiesFromApiToPropertiesObject(
        response,
        getDependenciesObject(response, excelPricingEnabled) ? getDependenciesObject(response, excelPricingEnabled).dependenciesObject : null
      )
      setProperties(response)
      setPropertiesObject(updatedPropertiesObject)

      UStoreProvider.state.customState.set('currentProductProperties', response)
      const updatedErrorsFromApi = await processErrorsOnApiResponse(
        response,
        updatedErrors || errors,
        changedProperties
      )
      if (product.Type === productTypes.DYNAMIC) {
        recalculatePrice = changedProperties.reduce((acc, propertyId) => {
          return acc || response.JSONSchema.definitions[propertyId]?.custom.affectPrice
        }, recalculatePrice)
      }

      if (recalculatePrice && !checkIfThereAreVisiblePropertyErrors(updatedErrorsFromApi)) {
        calculateProperties(usedQuantity || quantity, null, response, updatedErrorsFromApi)
      }
      return { updatedPropertiesFromApi: response, updatedPropertiesObject }
    }

    addPromise(pushPropertiesState(
      orderItem.ID,
      formDataForApi
    ), handleResponse)

    return { updatedPropertiesFromApi: properties, updatedPropertiesObject: propertiesObject }
  }, [addPromise, calculateProperties, checkIfThereAreVisiblePropertyErrors, errors, excelPricingEnabled, orderItem.ID,
    processErrorsOnApiResponse, properties, propertiesObject, quantity, product.Type])

  const createPreview = useCallback(async (propsFromApiToPropsObject, orderItemId = null, propertiesFromApi = null) => {
    setDisabledRefreshPreviewButton(() => true)
    const propsForProof = preparingFormDataToSendToServer(Object.values(propsFromApiToPropsObject).reduce((r, p) => ({...r, [p.id]: p.value}), {}), propertiesFromApi || properties)
    fastProofService.onError = () => {
      setDisabledRefreshPreviewButton(() => false)
      fastProofService.breakCurrentLoop()
      setPoofPreviewError(true)
      setShowLoaderDots(false)
    }

    fastProofService.onProof = async (proof) => {
      setDisabledRefreshPreviewButton(() => false)
      const fileNames = proof.Items.map((p) => ({Url: p.Url.replace(/.*?fileName=(.*)$/,'$1')}))
      let proofedDownloaded = []
      let hadError = false
      let firstProof = true
      for (const file of fileNames) {
        const fileBlob = await UStoreProvider.api.products.downloadProofPreview(orderItem.ID || orderItemId, proof.PreviewID, file.Url)
        if (fileBlob) {
          if (firstProof) {
            window['proofPreview'] = window['proofPreview'] || {}
            window['proofPreview'][orderItem.ID || orderItemId] = window['proofPreview'][orderItem.ID || orderItemId] || []
            window['proofPreview'][orderItem.ID || orderItemId].push(fileBlob)
            firstProof = false
          }
          proofedDownloaded = [...proofedDownloaded, {
            Url: URL.createObjectURL(fileBlob),
            DisplayName: t('DynamicProof.Page', { pageNumber: proofedDownloaded.length + 1 }),
            type: proof.Format === 1 ? 'image' : 'pdf'
          }]
        } else {
          hadError = true
        }
      }
      if (!hadError) {
        setProductThumbnails({ Thumbnails: proofedDownloaded })
      }
      setShowLoaderDots(false)
      setPoofPreviewError(hadError)
    }
    if (showRefreshPreview || searchParams.has('OrderItemId')) {
      setShowLoaderDots(true)
    }
    setPoofPreviewError(false)
    fastProofService.push([orderItem.ID || orderItemId, propsForProof])
    // eslint-disable-next-line react-hooks/exhaustive-deps
  },[orderItem.ID, properties])

  const handleFormChange = useCallback(async (
    propertyId = null,
    propertyValue = undefined,
    propertyErrors = null,
    formChanges = {}
  ) => {
    setPriceError(null)
    resetPopperError()
    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
    )

    const propAffectProof = formChanges && !propertyId ?
      Object.entries(formChanges).reduce((r, [key, value]) => r || updatedFormData.JSONSchema.definitions[key]?.custom?.affectProof) :
      updatedFormData.JSONSchema.definitions[propertyId]?.custom?.affectProof

    if ( propAffectProof && product.Type === productTypes.DYNAMIC && !showRefreshPreview && (formChanges || propertyErrors[propertyId]?.length === 0)) {
      await createPreview(propsFromApiToPropsObject)
    }

    // 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)
      } else if (checkIfThereArePropertyErrors(updatedErrors)) {
        if (excelPricingEnabled) {
          await updateProperties(
            Object.keys(formChanges).length ? Object.keys(formChanges) : [propertyId],
            updatedPropertiesSchema,
            null,
            true,
            updatedErrors
          )
          setPageState(State.initial)
        } else if (!excelPricingEnabled && checkIfThereAreVisiblePropertyErrors(updatedErrors)) {
          await updateProperties(
            Object.keys(formChanges).length ? Object.keys(formChanges) : [propertyId],
            updatedPropertiesSchema,
            null,
            false,
            updatedErrors
          )
          setPageState(State.initial)
        } else if (!excelPricingEnabled && !checkIfThereAreVisiblePropertyErrors(updatedErrors)) {
          await updateProperties(
            Object.keys(formChanges).length ? Object.keys(formChanges) : [propertyId],
            updatedPropertiesSchema,
            null,
            false,
            updatedErrors
          )
          if (product.Type === productTypes.DYNAMIC) {
            setPageState(State.initial)
            if (updatedPropertiesSchema.JSONSchema?.definitions[propertyId]?.custom?.affectPrice) {
              setPriceError('can not recalculate')
              setPostLoadingProcedures(true)
            }
          } else {
            setPageState(State.initial)
            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 (quantityError === 'invalid' && product.Type === productTypes.DYNAMIC) {
      setPageState(State.initial)
    }

  }, [checkIfThereArePropertyErrors, checkIfThereAreVisiblePropertyErrors, excelPricingEnabled, product.Type,
    processErrorsOnFormChange, properties, quantityError, updateProperties, createPreview, showRefreshPreview])

  const loadProductData = async () => {
    if (product.ID) {
      setPageState(State.loading)
      const productFromApi = await UStoreProvider.api.products.getProductByID(product.ID)
      setProduct(productFromApi)
      const fastPreviewEnabled = !productFromApi?.Configuration?.Proof?.FastPreviewEnabled && productFromApi.Type === productTypes.DYNAMIC
      setShowRefreshPreview(fastPreviewEnabled)
      const productThumbnailsFromApi = await UStoreProvider.api.products.getProductThumbnails(productFromApi.ID)
      setProductThumbnails(productThumbnailsFromApi)
      let orderItemFromApi
      if (searchParams.get('OrderItemId')) orderItemFromApi = await UStoreProvider.api.orders.getOrderItem(searchParams.get('OrderItemId'))
      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, productFromApi, fastPreviewEnabled)
      UStoreProvider.state.customState.setBulk({
        currentProductThumbnails: productThumbnailsFromApi,
        currentOrderItem: orderItemFromApi,
        lastOrder: lastOrderFromApi,
      })
      setPageState(State.initial)
    }
  }

  const onFormChange = useCallback((...args) => {
    setPageState(State.loading)
    handleFormChange(...args)
  }, [handleFormChange])

  const loadProductDataCallbackRef = useRef(loadProductData)
  useEffect(() => {
    loadProductDataCallbackRef.current = loadProductData
  })

  useEffect(() => {
    const loadProductDataCallback = e => loadProductDataCallbackRef.current(e)

    const onResize = (event) => {
      setIsMobile(document.body.clientWidth < parseInt(theme.md.replace('px', '')))
    }

    window.addEventListener('beforeunload', cleanCustomState, true)
    window.addEventListener('resize', onResize, true)
    stickyConnect()
    onResize()
    loadProductDataCallback()

    return () => {
      stickyDisconnect()
      cleanCustomState()
      window.removeEventListener('beforeunload', cleanCustomState, true)
      fastProofService.breakCurrentLoop()
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

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

  useEffect(() => {
    const demiState = { ...properties.formData }

    window.triggerFormPropertyChange = (propertyId, propertyValue) => {
      if (propertyValue && propertyValue.includes && propertyValue.includes('__GENERIC_HTML_ACCUMULATOR_VALUE__')) {
        const formValues = JSON.parse(propertyValue)['__GENERIC_HTML_ACCUMULATOR_VALUE__']
        onFormChange(null, null, null, formValues)
      } else {
        if (propertiesObject[propertyId]) demiState[propertyId] = propertyValue
        clearTimeout(htmlDataUpdateTimeout)
        const newHtmlUpdateTimeout = setTimeout(() => {
          onFormChange(null, null, null, demiState)
        }, 500)
        setHtmlDataUpdateTimeout(newHtmlUpdateTimeout)
      }
    }
    return () => { window.triggerFormPropertyChange = undefined }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [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 cleanCustomState = () => {
    ['currentProduct',
      'currentOrderItem',
      'currentOrderItemId',
      'currentOrderItemPriceModel',
      'lastOrder',
      'currentProductThumbnails',
      'currentDeliveryServices',
      'CurrentProductDucErrors',
      'ducData',
      'currentProductProperties',
      'isLoadingData'
    ].forEach((property) => {
      UStoreProvider.state.customState.delete(property)
    })
  }

  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 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, product, fastPreviewEnabled) => {
    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)
      if (product.Type === productTypes.DYNAMIC) {
        if (searchParams.get('OrderItemId')) {
          setTimeout(() => createPreview(updatedPropertiesObject, searchParams.get('OrderItemId'), propertiesFromApi), 0)
        } else if (!fastPreviewEnabled) {
          setTimeout(() => createPreview(updatedPropertiesObject, updatedOrderItem.ID, propertiesFromApi), 0)
        }
      }


      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 reRouteToCart = (storeType, cartUrl) => {
    if (storeType === 3 && cartUrl) {
      const decoded = decodeURIComponent(cartUrl)
      window.location.href = `${decoded}${decoded.includes('?') ? '&' : '?'}OrderProductId=${orderItem.FriendlyID}`
    } else {
      navigate(urlGenerator.get({ page: 'cart' }))
    }
  }

  const reRouteToNewOrder = (productId, productName, newOrderId) => {
    setSearchParams({ OrderItemId: newOrderId, reorder: true })
    navigate(0)
  }

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

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

      if (pageState !== State.loading) {
        await updateProperties(['quantity'], null, newQuantity)
        setPageState(State.initial)
        setPopperError(null)
      }
    } catch (e) {
      setPageState(State.initial)
      console.error(e)
      setPricingError(e)
    } finally {
      setQuantity(newQuantity)
      if (!isNaN(parseInt(newQuantity))) {
        setOrderItem({
          ...orderItem,
          Quantity: newQuantity
        })
      }
    }
  }

  const addToCartOrSave = async (skipPropertiesPush = false) => {
    // 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 (!skipPropertiesPush) {
        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)
      setPostLoadingProcedures(true)
      showAllErrors()
      if (product.Type === productTypes.DYNAMIC && checkIfThereAreSectionErrors()) {
        const distinctSections = [...new Set(Object.values(propertiesObject).reduce((acc, current) => [...acc, current.uiSchema['ui:options'].section], []))]

        if (distinctSections.length > 1) {
          const sectionsAffectedByErrors = [...new Set(Object.values(errors).reduce((acc, current) => current.show && current.section ? [...acc, current.section] : [...acc], []))].sort((a, b) => distinctSections.indexOf(a) - distinctSections.indexOf(b))
          const sectionsAffectedByErrorsWithIndexes = sectionsAffectedByErrors.map((section) =>
            distinctSections.includes(section) ?
              `${distinctSections.indexOf(section) + 1}. ${section === 'xmpie_product_properties' ? t('xmpie_product_properties') : section}` :
              ''
          )
          // numbers lower than -2 are special values to signal dynamic form to open the first section with errors
          setSectionToOpen(-2 - Date.now())
          setPopperAffectedSections(sectionsAffectedByErrorsWithIndexes)
          setPopperError('SECTION_ERROR')
          return
        }
      }
      setPopperError('VALIDATION_ERROR')
      return
    }
    const updatedProperties = await updateProperties(Object.keys(propertiesObject))
    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 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 resetPopperError = () => {
    setPopperError(null)
    setForceAddToCartButtonPopper(false)
  }

  const onProofPreviewClick = async () => await createPreview(propertiesObject)

  return (
    <ProductLayout className='product-instance'
      dynamic={product.Type === productTypes.DYNAMIC}
    >
      <left is='custom'>
        <div className="left-container">
          <PreviewErrorBalloon {...{poofPreviewError}}/>
          <ImageCarousel
              showTitle
              images={productThumbnails.Thumbnails}
              showLoaderDots={showLoaderDots && !isMobile}
              isStretched={product.Type === productTypes.DYNAMIC}
          />
          { product.Proof &&
              <Proof
                  currentProduct={product}
                  isMobile={false}
                  hasThumbnails
                  hideLink={false}
                  orderItemId={orderItem.ID}
                  onToggle={() => setProofModalOpen(!proofModalOpen)}
              />
          }
          <RefreshPreviewButton {...{showRefreshPreview, onProofPreviewClick, disabled: disabledRefreshPreviewButton }}/>
        </div>
      </left>
      <right is="custom">
        <Slot name="static_product_top" />
        <ProductDetails
          className='product-instance'
          productModel={product}
          minimumQuantity={product.Configuration ? product.Configuration.Quantity.Minimum : null}
          reorderModel={!searchParams.get('reorder') ? UStoreProvider.state.customState.get('lastOrder') : null}
          onReorder={handleReorder}
          showInStock
          langCode={themeContext.get('languageCode')}
        />
        {isMobile &&
          <>
            <div className="image-carousel-mobile">
              <PreviewErrorBalloon {...{ poofPreviewError }}/>
              <ImageCarousel
                zoomAllowed={product.Type === productTypes.DYNAMIC}
                images={productThumbnails.Thumbnails}
                isStretched={true}
                showLoaderDots={showLoaderDots}
              />
              <Proof
                currentProduct={product}
                isMobile
                hasThumbnails={productThumbnails.Thumbnails && productThumbnails.Thumbnails.length > 1}
                orderItemId={orderItem.ID}
                onToggle={() => setProofModalOpen(!proofModalOpen)}
                hideLink={product.Type === productTypes.STATIC && !product.Proof}
              />

            </div>
            <RefreshPreviewButton {...{
              showRefreshPreview,
              onProofPreviewClick,
              disabled: disabledRefreshPreviewButton
            }}/>
          </>
        }
        <Price
          ref={topPriceRef}
          isPriceCalculating={pageState === State.calculatingPrice || pageState === State.loading}
          price={price} showMinimumPrice={!!price.IsMinimumPrice}
        />
        <div ref={topMarkerRef} className="price-top-marker"></div>
        <div className='product-instance-wizard'>
          <div id="form-container" className='product-instance-properties product-properties'>
            <Slot name="static_product_above_quantity" />
            <div className='quantity'>
              <span className='quantity-label'>{t('product.quantity')}</span>
              {orderItem?.Quantity && <ProductQuantity
                supportsInventory
                productModel={product}
                orderModel={orderItem}
                onQuantityChange={handleQuantityChange}
              />}
            </div>
            <Slot name="static_product_below_quantity" />
            <DynamicForm
              errors={errors}
              excelPricingEnabled={excelPricingEnabled}
              formData={properties.formData}
              onBlur={handlePropertyBlur}
              onChange={onFormChange}
              properties={propertiesObject}
              sectionToOpen={sectionToOpen}
            />
          </div>
        </div>
        <ProductDeliveryMethod
          className='static-delivery-method'
          productModel={product}
          onDeliveryChange={handleDeliveryChange}
          currentDeliveryMethod={deliveryMethod}
          currentDeliveryServiceID={deliveryService}
          deliveryServices={productDeliveries}
        />
        <ProductOrderSummary
          ref={bottomPriceRef}
          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 || pageState === State.loading}
        />
        <div ref={bottomMarkerRef} className="price-bottom-marker"></div>
        <ProductProof
          onAddToCartClick={handleAddToCartButtonClick}
          isModalOpen={proofModalOpen}
          modalClassName='product-instance-proof-modal'
          src={proofUrl}
          type={product.Proof && product.Proof.MimeType ? product.Proof.MimeType : ''}
          onCloseModal={() => setProofModalOpen(!proofModalOpen)}
          isMobile={isMobile}
        />
        {approvalModalOpen && <ProductApprovalModal product={product} properties={properties}
                                                    onAddToCartClick={addToCartOrSave} orderItem={orderItem}
                                                    productThumbnails={productThumbnails} modalOpen={approvalModalOpen}
                                                    onCloseModal={() => setApprovalModalOpen(!approvalModalOpen)}
                                                    continueButtonText={getContinueButtonText(true)} src={proofUrl}
                                                    excelPricingEnabled={excelPricingEnabled}/>}
        <div className='add-to-cart-button-wrapper'>
          <Slot name="static_product_above_add_to_cart" />
          <div
            className='button button-primary add-to-cart-button'
            id='add-to-cart-button'
            onClick={handleAddToCartButtonClick}
            tabIndex='0'
          >
            {
              pageState === State.loading ||
                pageState === State.calculatingPrice
                ? <LoadingDots/>
                : getContinueButtonText()}
          </div>
          <Slot name="static_product_below_add_to_cart" />
        </div>
        <Popper
          bottomPriceRef={bottomPriceRef}
          topPriceRef={topPriceRef}
          stickPriceRef={stickyPriceRef}
          errorCode={popperError}
          forceAddToCartButton={forceAddToCartButtonPopper}
          resetError={resetPopperError}
          popperAffectedSections={popperAffectedSections}
        />
        <Slot name="static_product_bottom" />
        {product.Type === productTypes.DYNAMIC &&
          <ProductThumbnailsPreview
            isModalOpen={thumbnailModalOpen}
            onCloseModal={() => setThumbnailModalOpen(false)}
            productThumbnails={(product.Type === productTypes.DYNAMIC && productThumbnails) ? productThumbnails : null}
            modalClassName='thumbnails-preview'
            onImageChange={(id) => setLastViewImageId(id)}
            poofPreviewError={poofPreviewError}
            onProofPreviewClick={onProofPreviewClick}
          />
        }
        {showStickyPrice &&
          <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}
            productThumbnails={(product.Type === productTypes.DYNAMIC && productThumbnails) ? productThumbnails : null}
            onImageClick={() => setThumbnailModalOpen(true)}
            lastViewImageId={lastViewImageId}
            ref={stickyPriceRef}
          />}

      </right>

    </ProductLayout>
  )
}

export default StaticProduct
