import '@things-factory/form-ui'
import '@things-factory/grist-ui'

import gql from 'graphql-tag'
import { css, html, LitElement } from 'lit-element'
import isEmpty from 'lodash-es/isEmpty'
import isNaN from 'lodash-es/isNaN'
import isNumber from 'lodash-es/isNumber'

import { getCodeByName } from '@things-factory/code-base'
import { i18next, localize } from '@things-factory/i18n-base'
import { client, CustomAlert, gqlContext } from '@things-factory/shell'
import { ScrollbarStyles } from '@things-factory/styles'
import { isMobileDevice } from '@things-factory/utils'

export class ProductDetailsPopup extends localize(i18next)(LitElement) {
  static get styles() {
    return [
      ScrollbarStyles,
      css`
        :host {
          display: flex;
          flex-direction: column;
          overflow: hidden;
          background-color: white;

          --grid-record-emphasized-background-color: #ffdbdb;
          --grid-record-emphasized-color: #703f3f;
        }
        search-form {
          overflow: visible;
        }
        data-grist {
          overflow-y: auto;
          flex: 1;
        }
        .button-container {
          padding: 10px 0 12px 0;
          text-align: center;
        }
        [danger] {
          --mdc-theme-primary: var(--mdc-danger-button-primary-color);
        }
        .header {
          display: grid;
          grid-template-columns: repeat(24, 1fr);
          grid-gap: var(--form-grid-gap);
          grid-auto-rows: minmax(24px, auto);
          max-width: var(--form-multi-column-max-width);
          margin-top: var(--form-margin);
          margin-left: var(--form-margin);
          margin-right: var(--form-margin);
        }
        .header legend {
          grid-column: span 24;
          text-transform: capitalize;

          padding: var(--legend-padding);
          font: var(--legend-font);
          color: var(--legend-text-color);
          border-bottom: var(--legend-border-bottom);
        }
      `
    ]
  }

  static get properties() {
    return {
      _searchFields: Array,
      config: Object,
      _data: Object,
      productId: String,
      _gtins: Array
    }
  }

  constructor() {
    super()
    this._gtins = []
  }

  get dataGrist() {
    return this.shadowRoot.querySelector('data-grist')
  }

  get buttonSave() {
    return this.shadowRoot.querySelector('#buttonSave')
  }

  render() {
    return html`
      <data-grist
        .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
        .config=${this.config}
        .data=${this._data}
        .fetchHandler="${this.fetchHandler.bind(this)}"
        @field-change="${this._fieldChange.bind(this)}"
      ></data-grist>

      <div class="button-container">
        <mwc-button
          id="buttonSave"
          @click=${this.updateProductDetail}
          raised
          label="${i18next.t('button.save')}"
        ></mwc-button>
        <mwc-button
          raised
          danger
          @click=${() => {
            history.back()
          }}
          label="${i18next.t('button.back')}"
        ></mwc-button>
      </div>
    `
  }

  async firstUpdated() {
    const packingType = await getCodeByName('PACKING_TYPES')
    const uom = await getCodeByName('UOM')
    const weightUnit = await getCodeByName('WEIGHT_UNITS')
    const lengthUnit = await getCodeByName('LENGTH_UNITS')

    this.config = {
      rows: {
        appendable: true,
        classifier: record => {
          return {
            emphasized: record.error || false
          }
        }
      },
      pagination: { infinite: true },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'clear',
          handlers: {
            click: (_columns, _data, _column, record, _rowIndex) => {
              if (record) this._removeGtin(record, _rowIndex)
            }
          }
        },
        {
          type: 'boolean',
          name: 'isDefault',
          header: i18next.t('field.default'),
          record: { editable: true, align: 'center' },
          sortable: true,
          width: 80
        },
        {
          type: 'string',
          name: 'gtin',
          header: i18next.t('field.gtin'),
          record: { editable: true },
          sortable: true,
          width: 180
        },
        {
          type: 'string',
          name: 'refCode',
          header: i18next.t('field.ref_code'),
          record: { editable: true },
          sortable: true,
          width: 150
        },
        {
          type: 'select',
          name: 'packingType',
          header: `*${i18next.t('field.packing_type')}`,
          record: {
            editable: true,
            options: ['', ...Object.keys(packingType).map(key => packingType[key].name)]
          },
          width: 120
        },
        {
          type: 'select',
          name: 'uom',
          record: {
            editable: true,
            options: ['', ...Object.keys(uom).map(key => uom[key].name)]
          },
          header: `*${i18next.t('field.uom')}`,
          width: 80
        },
        {
          type: 'float',
          name: 'uomValue',
          record: { editable: true, options: { min: 0 } },
          header: `*${i18next.t('field.uom_value')}`,
          width: 100
        },
        {
          type: 'select',
          name: 'childProductDetail',
          header: i18next.t('field.child_gtin'),
          record: { editable: true, rowOptionField: 'rowOptionProductDetail' },
          width: 180
        },
        {
          type: 'float',
          name: 'packingSize',
          record: { editable: true, options: { min: 1 } },
          header: i18next.t('field.packing_size'),
          width: 110
        },
        {
          type: 'float',
          name: 'bufferQty',
          record: { editable: true, options: { min: 0 } },
          header: i18next.t('field.buffer_qty'),
          width: 80
        },
        {
          type: 'float',
          name: 'minQty',
          record: { editable: true, options: { min: 0 } },
          header: i18next.t('field.min_qty'),
          width: 80
        },
        {
          type: 'float',
          name: 'maxQty',
          record: { editable: true, options: { min: 0 } },
          header: i18next.t('field.max_qty'),
          width: 80
        },
        {
          type: 'select',
          name: 'weightUnit',
          record: {
            editable: true,
            options: ['', ...Object.keys(weightUnit).map(key => weightUnit[key].name)]
          },
          header: `${i18next.t('field.weight_unit')}`,
          width: 100
        },
        {
          type: 'float',
          name: 'grossWeight',
          record: { editable: true },
          header: i18next.t('field.gross_weight'),
          width: 140
        },
        {
          type: 'float',
          name: 'nettWeight',
          record: { editable: true },
          header: i18next.t('field.nett_weight'),
          width: 140
        },
        {
          type: 'select',
          name: 'lengthUnit',
          record: {
            editable: true,
            options: ['', ...Object.keys(lengthUnit).map(key => lengthUnit[key].name)]
          },
          header: `${i18next.t('field.length_unit')}`,
          width: 80
        },
        {
          type: 'float',
          name: 'width',
          record: { editable: true },
          header: i18next.t('field.width'),
          width: 70
        },
        {
          type: 'float',
          name: 'depth',
          record: { editable: true },
          header: i18next.t('field.depth'),
          width: 70
        },
        {
          type: 'float',
          name: 'height',
          record: { editable: true },
          header: i18next.t('field.height'),
          width: 70
        },
        {
          type: 'float',
          name: 'volume',
          record: { editable: true },
          header: i18next.t('field.volume'),
          width: 70
        },
        {
          type: 'string',
          name: 'auxUnit1',
          record: { editable: true },
          header: `${i18next.t('field.aux_unit')} 1`,
          width: 80
        },
        {
          type: 'string',
          name: 'auxValue1',
          record: { editable: true },
          header: `${i18next.t('field.aux_value')} 1`,
          width: 90
        },
        {
          type: 'string',
          name: 'auxUnit2',
          record: { editable: true },
          header: `${i18next.t('field.aux_unit')} 2`,
          width: 90
        },
        {
          type: 'string',
          name: 'auxValue2',
          record: { editable: true },
          header: `${i18next.t('field.aux_value')} 2`,
          width: 90
        },
        {
          type: 'string',
          name: 'auxUnit3',
          record: { editable: true },
          header: `${i18next.t('field.aux_unit')} 3`,
          width: 90
        },
        {
          type: 'string',
          name: 'auxValue3',
          record: { editable: true },
          header: `${i18next.t('field.aux_value')} 3`,
          width: 90
        },
        {
          type: 'string',
          name: 'auxUnit4',
          record: { editable: true },
          header: `${i18next.t('field.aux_unit')} 4`,
          width: 90
        },
        {
          type: 'string',
          name: 'auxValue4',
          record: { editable: true },
          header: `${i18next.t('field.aux_value')} 4`,
          width: 90
        },
        {
          type: 'string',
          name: 'auxUnit5',
          record: { editable: true },
          header: `${i18next.t('field.aux_unit')} 5`,
          width: 90
        },
        {
          type: 'string',
          name: 'auxValue5',
          record: { editable: true },
          header: `${i18next.t('field.aux_value')} 5`,
          width: 90
        }
      ]
    }
  }

  async fetchHandler({ page, limit, sorters = [] }) {
    try {
      if (!this.productId) return

      const pagination = { page, limit }
      const sortings = sorters

      const filters = [{ name: 'product', operator: 'eq', value: this.productId }]

      const response = await client.query({
        query: gql`
          query productDetails($filters: [Filter], $pagination: Pagination, $sortings: [Sorting]) {
            productDetails(filters: $filters, pagination: $pagination, sortings: $sortings) {
              items {
                id
                name
                gtin
                refCode
                product {
                  id
                  sku
                  name
                  description
                }
                isDefault
                packingType
                packingSize
                movement
                uom
                uomValue
                childProductDetail {
                  id
                  name
                  gtin
                }
                bundleQty
                weightUnit
                nettWeight
                grossWeight
                lengthUnit
                width
                depth
                height
                volume
                bufferQty
                minQty
                maxQty
                auxUnit1
                auxValue1
                auxUnit2
                auxValue2
                auxUnit3
                auxValue3
                auxUnit4
                auxValue4
                auxUnit5
                auxValue5
                isTrackedAsInventory
                discountId
                costPrice
                mrpPrice
                sellPrice
                afterTaxCostPrice
                afterTaxSalesPrice
                inventoryAccountCode
                cogsAccountCode
              }
              total
            }
          }
        `,
        variables: { filters, pagination, sortings },
        context: gqlContext()
      })

      let data = this._updateRowOption(response.data.productDetails.items)

      if (!response.errors) {
        this._data = {
          total: response.data.productDetails.total || 0,
          records: data
        }
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async updateProductDetail() {
    try {
      this.buttonSave.disabled = true
      this.dataGrist.showSpinner()

      let patches = this.dataGrist._data.records

      this._validate()

      patches = this.dataGrist._data.records.map(itm => {
        return {
          id: itm.id,
          name: itm.name,
          gtin: itm.gtin,
          refCode: itm.refCode,
          isDefault: itm.isDefault,
          packingType: itm.packingType,
          movement: itm.movement,
          uom: itm.uom,
          uomValue: itm.uomValue,
          childProductDetail: itm.childProductDetail,
          packingSize: itm.packingSize,
          weightUnit: itm.weightUnit,
          nettWeight: itm.nettWeight,
          grossWeight: itm.grossWeight,
          lengthUnit: itm.lengthUnit,
          width: itm.width,
          depth: itm.depth,
          height: itm.height,
          volume: itm.volume,
          bufferQty: itm.bufferQty,
          minQty: itm.minQty,
          maxQty: itm.maxQty,
          auxUnit1: itm.auxUnit1,
          auxValue1: itm.auxValue1,
          auxUnit2: itm.auxUnit2,
          auxValue2: itm.auxValue2,
          auxUnit3: itm.auxUnit3,
          auxValue3: itm.auxValue3,
          auxUnit4: itm.auxUnit4,
          auxValue4: itm.auxValue4,
          auxUnit5: itm.auxUnit5,
          auxValue5: itm.auxValue5,
          isTrackedAsInventory: itm.isTrackedAsInventory,
          discountId: itm.discountId,
          costPrice: itm.costPrice,
          mrpPrice: itm.mrpPrice,
          sellPrice: itm.sellPrice,
          afterTaxCostPrice: itm.afterTaxCostPrice,
          afterTaxSalesPrice: itm.afterTaxSalesPrice,
          inventoryAccountCode: itm.inventoryAccountCode,
          cogsAccountCode: itm.cogsAccountCode
        }
      })

      let productId = this.productId

      if (!productId) throw new Error('Product not found')

      if (productId && patches) {
        const response = await client.mutate({
          mutation: gql`
            mutation updateMultipleProductDetail($productId: String!, $patches: [ProductDetailPatch]!) {
              updateMultipleProductDetail(productId: $productId, patches: $patches) {
                name
              }
            }
          `,
          variables: { productId, patches },
          context: gqlContext()
        })
        if (response.errors) {
          throw response.errors
        }
      } else {
        CustomAlert({
          title: i18next.t('text.nothing_changed'),
          text: i18next.t('text.there_is_nothing_to_save')
        })
      }

      this.dataGrist.hideSpinner()
      this.buttonSave.disabled = false
      this.dispatchEvent(new CustomEvent('saved', {}))
      history.back()
    } catch (error) {
      this._showToast(error)
      this.dataGrist.hideSpinner()
      this.buttonSave.disabled = false
    }
  }

  _validate() {
    let errors = []

    if (!this.dataGrist._data.records.find(x => x.isDefault)) {
      errors.push({ type: 'isDefault', value: 'Must have 1 default' })
    }

    let data = this.dataGrist._data.records.map(itm => {
      itm.error = false

      if (isEmpty(itm.gtin) || '') {
        itm.error = true
        if (!errors.find(err => err.type == 'gtin')) errors.push({ type: 'gtin', value: 'GTIN is required' })
      }
      if (isEmpty(itm.packingType) || '') {
        itm.error = true
        if (!errors.find(err => err.type == 'packingType'))
          errors.push({ type: 'packingType', value: 'Packing type is required' })
      }
      if (isEmpty(itm.uom) || '') {
        itm.error = true
        if (!errors.find(err => err.type == 'uom')) errors.push({ type: 'uom', value: 'UOM is required' })
      }
      if (!isNumber(itm.uomValue) || isNaN(itm.uomValue) || '') {
        itm.error = true
        if (!errors.find(err => err.type == 'uomValue'))
          errors.push({ type: 'uomValue', value: 'UOM Value is required' })
      }
      return itm
    })

    if (errors.length > 0) {
      this._data = {
        ...this.dataGrist.dirtyData,
        records: data
      }

      throw new Error(errors.map(itm => itm.value).join(', '))
    }
  }

  _fieldChange(e) {
    var { after, before, column, record, row } = e.detail
    let updatedRecords = {
      ...this.dataGrist._data,
      records: this._updateRowOption(this.dataGrist._data.records)
    }

    switch (column.name) {
      case 'isDefault':
        if (this.dataGrist._data.records.filter(x => x.isDefault).length == 0) {
          this._showToast({ type: 'error', message: 'Must have 1 default' })
          break
        }

        updatedRecords = {
          ...this.dataGrist._data,
          records: this.dataGrist._data.records.map((record, idx) => {
            if (idx !== e.detail.row) {
              record.isDefault = false
            }
            return record
          })
        }
        break
      case 'gtin':
        if (this.dataGrist._data.records.filter(x => x.gtin == after).length > 1) {
          this.dataGrist._data.records[row].gtin = before

          updatedRecords = {
            ...this.dataGrist._data
          }

          this._showToast({ type: 'error', message: 'Duplicated GTIN' })
          break
        }

        updatedRecords = {
          ...this.dataGrist._data,
          records: this._updateRowOption(
            this.dataGrist._data.records.map((record, idx) => {
              if (!isEmpty(record.childProductDetail) && record.childProductDetail == before) {
                record.childProductDetail = after
              }
              return record
            })
          )
        }

        break
      case 'uomValue':
        if (isNaN(after)) {
          updatedRecords.records[row].uomValue = 1
        }
        break
      default:
        break
    }

    if (column.record?.options?.min > after) {
      this.dataGrist._data.records[row][column.name] = before
      updatedRecords = {
        ...this.dataGrist._data
      }

      this._showToast({ type: 'error', message: `Minimum value of ${column.record.options.min} is required.` })
    }

    this._data = { ...updatedRecords }
  }

  _removeGtin(record, index) {
    let data = [...this.dataGrist._data.records]
    data.splice(index, 1)

    data = data.map(itm => {
      return { ...itm, childProductDetail: itm.childProductDetail == record.gtin ? '' : itm.childProductDetail }
    })

    if (
      !data.find(itm => {
        itm.isDefault
      })
    ) {
      data[0].isDefault = true
    }

    this._data = {
      ...this._data,
      records: [...this._updateRowOption(data)]
    }
  }

  _updateRowOption(data) {
    return (
      data.map(itm => {
        return {
          ...itm,
          childProductDetail: itm?.childProductDetail?.gtin || itm?.childProductDetail || '',
          rowOptionProductDetail: {
            options: [
              {
                display: '',
                value: ''
              },
              ...data
                .filter(child => child.gtin != itm.gtin && !isEmpty(child.gtin))
                .map(x => {
                  const rowOption = {
                    display: x.gtin,
                    value: x.gtin
                  }
                  return rowOption
                })
            ]
          }
        }
      }) || []
    )
  }

  _showToast({ type, message }) {
    document.dispatchEvent(
      new CustomEvent('notify', {
        detail: {
          type,
          message
        }
      })
    )
  }
}

window.customElements.define('product-details-popup', ProductDetailsPopup)
