import { MultiColumnFormStyles } from '@things-factory/form-ui'
import '@things-factory/grist-ui'
import { i18next, localize } from '@things-factory/i18n-base'
import { openPopup } from '@things-factory/layout-base'
import { client, CustomAlert, navigate, PageView } from '@things-factory/shell'
import { CommonButtonStyles } from '@things-factory/styles'
import { isMobileDevice } from '@things-factory/utils'
import gql from 'graphql-tag'
import { css, html } from 'lit-element'
import { ORDER_STATUS, WORKSHEET_STATUS, WORKSHEET_TYPE } from '../constants'
import { fetchSettingRule } from '../../fetch-setting-value'
import './cycle-count-recheck-popup'

class CycleCountReview extends localize(i18next)(PageView) {
  static get properties() {
    return {
      _worksheetNo: String,
      _reportStatus: String,
      _cycleCountNo: String,
      _enableCartonLabel: Boolean,
      _pageSettings: Object,
      cycleCountType: String,
      customerId: String,
      inventoryAccuracy: String,
      config: Object,
      data: Object
    }
  }

  static get styles() {
    return [
      MultiColumnFormStyles,
      css`
        :host {
          display: flex;
          flex-direction: column;
          overflow-x: auto;
        }
        .form-container {
          display: flex;
        }
        .form-container > form {
          flex: 1;
        }
        .grist {
          background-color: var(--main-section-background-color);
          display: flex;
          flex-direction: column;
          flex: 1;
          overflow-y: auto;
        }
        data-grist {
          overflow-y: hidden;
          flex: 1;
        }
        h2 {
          padding: var(--subtitle-padding);
          font: var(--subtitle-font);
          color: var(--subtitle-text-color);
          border-bottom: var(--subtitle-border-bottom);
        }
        .grist h2 {
          margin: var(--grist-title-margin);
          border: var(--grist-title-border);
          font: var(--grist-title-font);
          color: var(--secondary-color);
        }
        .grist h2 mwc-icon {
          vertical-align: middle;
          margin: var(--grist-title-icon-margin);
          font-size: var(--grist-title-icon-size);
          color: var(--grist-title-icon-color);
        }
        h2 + data-grist {
          padding-top: var(--grist-title-with-grid-padding);
        }
      `
    ]
  }

  get context() {
    var actions = [
      {
        title: i18next.t('button.back'),
        action: () => navigate('cycle_count_worksheet_list'),
        ...CommonButtonStyles.back
      }
    ]

    if (this._reportStatus === ORDER_STATUS.PENDING_REVIEW.value) {
      if (this.isUserBelongsDomain) {
        actions.push({
          title: i18next.t('button.adjust_inventory'),
          action: this._adjustInventory.bind(this),
          ...CommonButtonStyles.edit
        })

        if (this.cycleCountType === WORKSHEET_TYPE.CYCLE_COUNT.value) {
          actions.push({
            title: i18next.t('button.reject'),
            action: this.openCycleCountRecheckPopup.bind(this),
            ...CommonButtonStyles.reject
          })
        }
      }
    }

    return {
      title: i18next.t('title.cycle_count_worksheet_review'),
      actions,
      printable: {
        accept: ['preview'],
        content: this
      },
      exportable: {
        name: this._cycleCountNo,
        data: this._exportableData.bind(this)
      },
      help: 'operato-wms/inventory-check/cycle-count-review'
    }
  }

  render() {
    return html`
      <div class="form-container">
        <form class="multi-column-form">
          <fieldset>
            <legend>${i18next.t('title.cycle_count')}</legend>
            <label>${i18next.t('label.company')}</label>
            <input name="bizplace" readonly />
            <label>${i18next.t('label.inventory_check_no')}</label>
            <input name="cycleCountNo" readonly />
            <label>${i18next.t('label.execute_date')}</label>
            <input name="executionDate" readonly />
            <label>${i18next.t('label.inventory_accuracy')}</label>
            <input name="inventoryAccuracy" value="${this.inventoryAccuracy ? this.inventoryAccuracy : ''}" readonly />
            <label>${i18next.t('label.status')}</label>
            <select name="status" disabled>
              ${Object.keys(ORDER_STATUS).map(
                key => html`
                  <option value="${ORDER_STATUS[key].value}">${i18next.t(`label.${ORDER_STATUS[key].name}`)}</option>
                `
              )}
            </select>
            <label>${i18next.t('label.worksheet_no')}</label>
            <input name="checkCount" readonly />
          </fieldset>
        </form>
      </div>
      <div class="grist">
        <h2><mwc-icon>list_alt</mwc-icon>${i18next.t('title.inventory')}</h2>
        <data-grist
          id="grist"
          .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
          .config=${this.config}
          .fetchHandler="${this.fetchWorksheetWithPagination.bind(this)}"
        ></data-grist>
      </div>
    `
  }

  constructor() {
    super()
    this._enableCartonLabel = false
  }

  async pageUpdated(changes) {
    if (this.active && (changes.resourceId || this._worksheetNo)) {
      if (changes.resourceId) {
        this._worksheetNo = changes.resourceId
      }
      this.grist.fetch()
      await this.fetchWorksheet()
      this.updateContext()
    }
  }

  async pageInitialized() {
    const enableCarton = fetchSettingRule('enable-carton-label')

    this._pageSettings = {
      ...this._pageSettings,
      enableCarton: (await enableCarton) || false
    }

    this.isUserBelongsDomain = await this._checkUserBelongsDomain()
    this.config = {
      rows: {
        appendable: false,
        classifier: (record, rowIndex) => {
          return {
            emphasized:
              record.status === WORKSHEET_STATUS.NOT_TALLY.value
          }
        }
      },
      list: {
        fields: [
          'batchId',
          'inspectedBatchNo',
          'palletId',
          'product',
          'packingType',
          'inspectedStatus',
          'inspectedLocation',
          'inspectedQty',
          'inspectedUomValue',
          'status'
        ]
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'string',
          name: 'cartonId',
          header: i18next.t('field.carton_id'),
          width: 130
        },
        {
          type: 'string',
          name: 'palletId',
          header: i18next.t('field.lot_id'),
          width: 100
        },
        {
          type: 'string',
          name: 'sku',
          header: i18next.t('field.sku'),
          width: 110
        },
        {
          type: 'string',
          name: 'productName',
          header: i18next.t('field.product'),
          width: 350
        },
        {
          type: 'string',
          name: 'brand',
          header: i18next.t('field.brand'),
          width: 100
        },
        {
          type: 'string',
          name: 'packingType',
          header: i18next.t('field.packing_type'),
          width: 100
        },
        {
          type: 'string',
          name: 'inspectedStatus',
          header: i18next.t('field.inspected_status'),
          width: 130
        },
        {
          type: 'string',
          name: 'batchId',
          header: i18next.t('field.system_batch_no'),
          width: 100
        },
        {
          type: 'string',
          name: 'inspectedBatchNo',
          header: i18next.t('field.inspected_batch_no'),
          width: 140
        },
        {
          type: 'string',
          name: 'originExpirationDate',
          header: i18next.t('field.system_expiration_date'),
          width: 120
        },
        {
          type: 'string',
          name: 'inspectedExpirationDate',
          header: i18next.t('field.inspected_expiration_date'),
          width: 120
        },
        {
          type: 'string',
          name: 'locationName',
          header: i18next.t('field.system_location'),
          width: 120
        },
        {
          type: 'string',
          name: 'inspectedLocation',
          header: i18next.t('field.inspected_location'),
          width: 120
        },
        {
          type: 'integer',
          name: 'qty',
          header: i18next.t('field.system_qty'),
          width: 120
        },        {
          type: 'integer',
          name: 'originalQty',
          header: i18next.t('field.original_qty'),
          width: 120
        },
        {
          type: 'integer',
          name: 'inspectedQty',
          header: i18next.t('field.inspected_qty'),
          width: 100
        },
        {
          type: 'float',
          name: 'uomValue',
          header: i18next.t('field.system_uom_value'),
          width: 100
        },
        {
          type: 'float',
          name: 'originalUomValue',
          header: i18next.t('field.original_uom_value'),
          width: 100
        },
        {
          type: 'float',
          name: 'inspectedUomValue',
          header: i18next.t('field.inspected_uom_value'),
          width: 100
        },
        {
          type: 'string',
          name: 'uom',
          header: i18next.t('field.uom'),
          width: 80
        },
        {
          type: 'string',
          name: 'status',
          header: i18next.t('field.status'),
          width: 100
        }
      ]
    }
    this._updateConfig()
  }

  _updateConfig() {
    const isCartonEnabled = this._pageSettings?.enableCarton
    this.config.columns = this.config.columns.filter(column => {
      if (isCartonEnabled == true) {
        return column.name !== 'palletId'
      } else {
        return column.name !== 'cartonId'
      }
    })
  }

  get form() {
    return this.shadowRoot.querySelector('form')
  }

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

  async fetchWorksheet() {
    if (!this._worksheetNo) return
    const response = await client.query({
      query: gql`
        query worksheetWithPagination($name: String!, $pagination: Pagination) {
          worksheetWithPagination(name: $name, pagination: $pagination) {
            worksheet {
              type
              status
              inventoryCheck {
                name
                executionDate
                status
              }
              bizplace {
                id
                name
                domain {
                  id
                  name
                }
              }
            }
            worksheetDetails {
              name
              status
              description
              targetInventoryCheckItem {
                status
                id
                inspectedBatchNo
                inspectedQty
                inspectedUomValue
                inspectedLocation {
                  id
                  name
                  description
                }
                product {
                  sku
                  name
                  brand
                  description
                }
                productDetail {
                  packingType
                  uom
                }
                inventory {
                  cartonId
                  palletId
                  batchId
                  packingType
                  qty
                  uomValue
                  uom
                  location {
                    id
                    name
                    description
                  }
                  product {
                    sku
                    name
                    description
                  }
                }
              }
            }
            total
          }
        }
      `,
      variables: { name: this._worksheetNo, pagination: {} }
    })

    if (!response.errors) {
      const worksheetDetails = response.data.worksheetWithPagination.worksheetDetails
      const tallyInv = worksheetDetails.filter(wd => wd.status === WORKSHEET_STATUS.DONE.value)
      const notTallyInv = worksheetDetails.filter(wd => wd.status === WORKSHEET_STATUS.NOT_TALLY.value)
      const adjustedInv = worksheetDetails.filter(wd => wd.status === WORKSHEET_STATUS.ADJUSTED.value)

      const accuracyInv =
        (tallyInv.length / (tallyInv.length + (notTallyInv.length ? notTallyInv.length : adjustedInv.length))) * 100 +
        `%`

      this.inventoryAccuracy = accuracyInv
    }
  }

  async fetchWorksheetWithPagination({ page, limit }) {
    if (!this._worksheetNo) return
    const response = await client.query({
      query: gql`
        query worksheetWithPagination($name: String!, $pagination: Pagination) {
          worksheetWithPagination(name: $name, pagination: $pagination) {
            worksheet {
              type
              status
              checkCount
              inventoryCheck {
                name
                executionDate
                status
              }
              bizplace {
                id
                name
                domain {
                  id
                  name
                }
              }
            }
            worksheetDetails {
              name
              status
              description
              targetInventoryCheckItem {
                status
                id
                originBatchNo
                inspectedBatchNo
                originQty
                inspectedQty
                originUomValue
                inspectedUomValue
                systemExpiryDate
                inspectedExpiryDate
                originLocation {
                  id
                  name
                  description
                }
                product {
                  sku
                  name
                  brand
                  description
                }
                productDetail {
                  id
                  packingType
                  uom
                }
                inspectedLocation {
                  id
                  name
                  description
                }
                inventory {
                  qty
                  cartonId
                  palletId
                  packingType
                  uom
                  uomValue
                  product {
                    id
                    sku
                    name
                    brand
                    description
                  }
                }
              }
            }
            total
          }
        }
      `,
      variables: { name: this._worksheetNo, pagination: { page, limit } }
    })

    if (!response.errors) {
      const worksheet = response.data.worksheetWithPagination.worksheet
      const partnerDomainId = worksheet.bizplace?.domain.id
      this.cycleCountType = worksheet.type
      this.customerId = worksheet.bizplace.id
      const worksheetDetails = response.data.worksheetWithPagination.worksheetDetails
      const total = response.data.worksheetWithPagination.total

      this._enableCartonLabel = await fetchSettingRule('enable-carton-label', partnerDomainId)
      this._reportStatus = worksheet.inventoryCheck.status
      this._cycleCountNo = (worksheet.inventoryCheck && worksheet.inventoryCheck.name) || ''

      this._fillupForm({
        ...worksheet,
        cycleCountNo: worksheet.inventoryCheck.name,
        executionDate: worksheet.inventoryCheck.executionDate,
        status: this._reportStatus
      })
      const records = worksheetDetails
        .map(worksheetDetail => {
          const targetItem = worksheetDetail.targetInventoryCheckItem
          const inventoryItem = targetItem?.inventory

          return {
            ...inventoryItem,
            name: worksheetDetail.name,
            sku: inventoryItem?.product.sku ? inventoryItem.product.sku : targetItem.product.sku,
            brand: inventoryItem?.product.brand ? inventoryItem.product.brand : targetItem.product.brand,
            description: worksheetDetail.description,
            productName:
              targetItem.product.name + `(` + targetItem.product.description + `)` ||
              inventoryItem.product.name + `(` + inventoryItem.product.description + `)`,
            locationName: targetItem.originLocation?.name,
            status: worksheetDetail.status,
            cartonId: inventoryItem?.cartonId,
            palletId: inventoryItem?.palletId,
            batchId: targetItem.originBatchNo,
            inspectedBatchNo: targetItem.inspectedBatchNo,
            qty: inventoryItem?.qty,
            originalQty: targetItem?.originQty,
            inspectedQty: targetItem?.inspectedQty,
            uomValue: inventoryItem?.uomValue,
            originalUomValue: targetItem?.originUomValue,
            inspectedUomValue: targetItem?.inspectedUomValue,
            uom: inventoryItem?.uom ? inventoryItem?.uom : targetItem.productDetail?.uom,
            originExpirationDate: targetItem?.systemExpiryDate,
            inspectedExpirationDate: targetItem?.inspectedExpiryDate,
            inspectedLocation: targetItem.inspectedLocation?.name,
            packingType: inventoryItem?.packingType ? inventoryItem.packingType : targetItem.packingType,
            inspectedStatus: targetItem.status,
            orderInventoryId: targetItem.id
          }
        })
        .sort(this._compareValues('status', 'desc'))

      if (this._enableCartonLabel) {
        const groupedRecords = {}
        let groupedTotal = 0

        records.forEach(record => {
          const groupKey = `${record.cartonId || ''}-${record.sku || ''}-${record.batchId || ''}-${
            record.locationName || ''
          }`

          if (!groupedRecords[groupKey]) {
            groupedRecords[groupKey] = { ...record, count: 1 }
            groupedTotal++
          } else {
            groupedRecords[groupKey].qty += record.qty || 0
            groupedRecords[groupKey].inspectedQty += record.inspectedQty || 0
            groupedRecords[groupKey].uomValue += record.uomValue || 0
            groupedRecords[groupKey].inspectedUomValue += record.inspectedUomValue || 0
            groupedRecords[groupKey].count += 1
          }
        })

        const groupedResults = Object.values(groupedRecords)

        this.updateContext()
        return { records: groupedResults, total: groupedTotal }
      } else {
        this.updateContext()
        return { records, total }
      }
    }
  }

  _fillupForm(data) {
    for (let key in data) {
      Array.from(this.form.querySelectorAll('input, select')).forEach(field => {
        if (field.name === key && field.type === 'checkbox') {
          field.checked = data[key]
        } else if (field.name === key) {
          if (data[key] instanceof Object) {
            const objectData = data[key]
            field.value = `${objectData.name} ${objectData.description ? `(${objectData.description})` : ''}`
          } else {
            field.value = data[key]
          }
        }
      })
    }
  }

  _compareValues(key, order = 'asc') {
    return function innerSort(a, b) {
      if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
        return 0
      }

      const varA = typeof a[key] === 'string' ? a[key].toUpperCase() : a[key]
      const varB = typeof b[key] === 'string' ? b[key].toUpperCase() : b[key]

      let comparison = 0
      if (varA > varB) {
        comparison = 1
      } else if (varA < varB) {
        comparison = -1
      }
      return order === 'desc' ? comparison * -1 : comparison
    }
  }

  async _adjustInventory() {
    try {
      const result = await CustomAlert({
        title: i18next.t('title.are_you_sure'),
        text: i18next.t('text.inventory_adjustment'),
        confirmButton: { text: i18next.t('button.confirm') },
        cancelButton: { text: i18next.t('button.cancel') }
      })

      if (!result.value) {
        return
      }

      const response = await client.mutate({
        mutation: gql`
          mutation cycleCountAdjustment($worksheetNo: String!, $cycleCountNo: String!) {
            cycleCountAdjustment(worksheetNo: $worksheetNo, cycleCountNo: $cycleCountNo)
          }
        `,
        variables: { worksheetNo: this._worksheetNo, cycleCountNo: this._cycleCountNo }
      })
      if (!response.errors) {
        this._showToast({ message: i18next.t('text.inventory_has_been_adjusted') })
        await this.grist.fetch()
        this.updateContext()
        navigate(`worksheet_cycle_count/${this._worksheetNo}`)
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  openCycleCountRecheckPopup() {
    openPopup(
      html`
        <cycle-count-recheck-popup
          .cycleCountNo="${this._cycleCountNo}"
          .customerId="${this.customerId}"
          .enableCartonLabel="${this._enableCartonLabel}"
          @completed="${() => this.grist.fetch()}"
        ></cycle-count-recheck-popup>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.cycle_count_recheck')
      }
    )
  }

  async _checkUserBelongsDomain() {
    try {
      const response = await client.query({
        query: gql`
          query {
            checkUserBelongsDomain
          }
        `
      })

      if (!response.errors) {
        return response.data.checkUserBelongsDomain
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _exportableData() {
    try {
      let data = []

      var headerSetting = [
        { header: i18next.t('field.system_batch_no'), key: 'batchId', width: 25, type: 'string' },
        { header: i18next.t('field.inspected_batch_no'), key: 'inspectedBatchNo', width: 25, type: 'string' },
        { header: i18next.t('field.lot_id'), key: 'palletId', width: 25, type: 'string' },
        { header: i18next.t('field.product'), key: 'productName', width: 60, type: 'string' },
        { header: i18next.t('field.packing_type'), key: 'packingType', width: 20, type: 'string' },
        { header: i18next.t('field.inspected_status'), key: 'inspectedStatus', width: 20, type: 'string' },
        { header: i18next.t('field.system_location'), key: 'locationName', width: 20, type: 'string' },
        { header: i18next.t('field.inspected_location'), key: 'inspectedLocation', width: 20, type: 'string' },
        { header: i18next.t('field.system_qty'), key: 'qty', width: 20, type: 'string' },
        { header: i18next.t('field.inspected_qty'), key: 'inspectedQty', width: 20, type: 'string' },
        { header: i18next.t('field.system_uom_value'), key: 'uomValue', width: 20, type: 'string' },
        {
          header: i18next.t('field.inspected_uom_value'),
          key: 'inspectedUomValue',
          width: 20,
          type: 'string'
        },
        { header: i18next.t('field.uom'), key: 'uom', width: 20, type: 'string' },
        { header: i18next.t('field.status'), key: 'status', width: 20, type: 'string' }
      ]

      data = (await this.fetchWorksheetWithPagination({ page: 1, limit: 999999 })).records

      return { header: headerSetting, data }
    } catch (e) {
      this._showToast(e)
    }
  }

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

window.customElements.define('cycle-count-review', CycleCountReview)
