import React, { useState, useEffect } from 'react'
import Hashids from 'hashids'

import ProductQuantity from './ProductQuantity'
import { t } from '$themelocalization'
import { isServer } from '$ustoreinternal/services/utils'
import { UStoreProvider } from '@ustore/core'
import { preparingFormDataToSendToServer, cleanServerErrors } from '../../services/utils'
import './ProductProperties.scss'
import { debounce } from 'throttle-debounce'
import deepcopy from 'deepcopy'
import DynamicForm from '$core-components/DynamicForm'

const withCustomProps = (WrappedComponent = {}) => {
  class ConnectedComponent extends React.Component {
    constructor(props) {
      super(props);
      const regex = new RegExp(/[!$%^&*()+|~=`{}\[\]:";'<>?,.\/]/, 'g');
      this.id = this.props.id.replace('root_', '').replace(regex, '')
    }

    onDucChange = (value, errors = []) => {
      const newDucValue = value === 'default' ? undefined : value
      const schemaErrors = !value && this.props.uiSchema['ui:errorMessages']
        ? Object.values(this.props.uiSchema['ui:errorMessages']).map((err) => err)
        : []
      const errSchema = { [this.props.id]: { __errors: [...schemaErrors, ...errors] }}
      this.props.onChange(this.props.id, newDucValue, errSchema)
    }

    render() {
      const hashids = new Hashids(this.id)
      const hashedID = hashids.encode(1, 2, 3)

      return (
        <div className={`a${hashedID}`}>
          <WrappedComponent
            {...this.props}
            onChange={this.onDucChange}
            id={this.id}
            disabled={this.props.schema.disabled}
          />
        </div>
      )
    }
  }
  return ConnectedComponent
}

const widgets = !isServer() ? uStoreDucs.reduce((r, duc) => ({...r, [duc.name]: withCustomProps(duc.component)}), {})  : {}

const ProductPropertiesNG = (props) => {
  const {
    className,
    productModel,
    orderModel,
    onQuantityChange,
    propertiesModel,
    onFirstValidate,
    onFirstAddToCart,
    translations,
    quantity,
    onFormChange,
    isAddClicked,
    excelPricingEnabled
  } = props

  const { ID } = orderModel
  const { currentProductProperties } = UStoreProvider.state.customState.get()

  const [formDataState, setFormDataState] = useState(propertiesModel.formData)
  const [jsonSchemaState, setJsonSchemaState] = useState(propertiesModel.JSONSchema)
  const [uiSchemaState, setUiSchemaState] = useState(propertiesModel.UISchema)
  const [errorsObj, setErrorsObj] = useState(propertiesModel.errors)
  const [ducErrors, setDucErrors] = useState([])

  useEffect(() => {
    setFormDataState(propertiesModel.formData)
    setJsonSchemaState(propertiesModel.JSONSchema)

    UStoreProvider.state.customState.set('currentProductProperties', {
      ...propertiesModel,
      formData: propertiesModel.formData
    })
  }, [quantity])

  const updatePropertiesState = async (dataToUpdate) => {
    const res = await UStoreProvider.api.orders.updatePropertiesState(ID, dataToUpdate)
    return Object.keys(res).includes('formData') ? res : { errorFromServer: res }
  }

  const onPropertyChange = debounce(500,false, async (
    newData,
    hiddenErrors = [],
    visibleErrors = [],
    newSchema) => {
    const prevData = { ...formDataState }
    let errors = [].concat(hiddenErrors || [])

    const changedProperty = Object.keys(newData)
      // do not change for !==, need check numbers with different types
      .filter((propertyName) => newData[propertyName] !== undefined && newData[propertyName] != prevData[propertyName])

    if (changedProperty.length > 0) {
      const changedData = changedProperty.reduce((acc, value) => ({...acc, [value]: newData[value]}), {})
      const propertiesToUpdate = preparingFormDataToSendToServer(
        {...prevData, ...changedData}, currentProductProperties)

      const res = await updatePropertiesState(propertiesToUpdate)

      if(!res.errorFromServer) {
        const errorsCopy = deepcopy(errors)

        const errorsFromRes = res && res.errors ? Object.keys(res.errors).reduce((acc, propertyName) => {
          errorsCopy.some((error) => {
            if(error.dataPath === `['${propertyName}']`) {
              if(error.keyword) {
                acc = acc.concat({
                  message: res.UISchema[propertyName]['ui:errorMessages'][error.keyword],
                  property: propertyName,
                  dataPath: `[${propertyName}]`,
                  affectPrice: currentProductProperties.JSONSchema.definitions[propertyName].custom.affectPrice
                })
              }
            }
          })
          return acc
        }, []) : []
        errors = errors.concat(errorsFromRes)

        UStoreProvider.state.customState.set('currentProductProperties', {
          ...res,
          formData: res.formData,
          errors: errors
        })

        let errorsObj = res.errors ? res.errors : {}

        setFormDataState(res.formData)
        setJsonSchemaState(res.JSONSchema)
        setUiSchemaState(res.UISchema)
        setDucErrors(errors)
        setErrorsObj(errorsObj)

        onFormChange && onFormChange(newData, prevData, errors, visibleErrors, newSchema, errorsObj)
      } else {
        // todo check if it needs
        // const propertiesToUpdateObject = propertiesToUpdate.reduce((acc, next) => ({...acc, [next.id]: next.value}), {})
        //
        // const updatedStateFormData = Object.keys(formDataState).reduce((acc, next) => {
        //   const key = next.split('_')[1]
        //   return {
        //     ...acc, [next]: propertiesToUpdateObject[key]
        //   }
        // }, {})
        // setFormDataState(updatedStateFormData)
        setFormDataState(formDataState)
        onFormChange && onFormChange(res)
      }
    }
  })

  const triggerFormPropertyChangeFunc = debounce(500, false,(newFormState) => {
    setFormDataState(newFormState)
    onPropertyChange(newFormState)
  })

  const demiState = {...formDataState}

  useEffect(() => {
    window.triggerFormPropertyChange = (propId, propValue) => {
      demiState[propId] = propValue
      triggerFormPropertyChangeFunc(demiState)
    }
    return () => { window.triggerFormPropertyChange = undefined }
  }, [])

  const transformErrors = (errors) => {
    return errors.map((error) => {
      const ID = error.property

      if (uiSchemaState[ID] && uiSchemaState[ID]['ui:errorMessages'] && uiSchemaState[ID]['ui:errorMessages'][error.keyword]) {
        error.message = uiSchemaState[ID]['ui:errorMessages'][error.keyword]
      }
      return error
    })
  }

  if (!productModel || !propertiesModel || !propertiesModel.JSONSchema || !propertiesModel.UISchema) {
    return null
  }

  let errorsObject = errorsObj || {}
  if (Object.keys(errorsObject).length > 0)
    errorsObject = cleanServerErrors(errorsObj, propertiesModel.UISchema).errorsObject

  return (
    <div className={` ${className} product-properties`}>
      <div className='quantity'>
        <span className='quantity-label'>{t('product.quantity')}</span>
        <ProductQuantity
          supportsInventory={true}
          productModel={productModel}
          orderModel={orderModel}
          onQuantityChange={onQuantityChange}
        />
      </div>
      <DynamicForm
        serverErrors={errorsObject}
        schema={jsonSchemaState}
        uiSchema={uiSchemaState}
        formData={formDataState}
        widgets={widgets}
        formContext={{ UStoreProvider, excelPricingEnabled }}
        onChange={onPropertyChange}
        errorSchema={ducErrors}
        transformErrors={transformErrors}
        isAddClicked={isAddClicked}
        onFirstValidate={onFirstValidate}
        onFirstAddToCart={onFirstAddToCart}
        translations={translations}
      />
    </div>
  )
}

export default ProductPropertiesNG
