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

import gql from 'graphql-tag'
import { css, html, LitElement } from 'lit-element'

import { MultiColumnFormStyles } from '@things-factory/form-ui'
import { i18next, localize } from '@things-factory/i18n-base'
import { client } from '@things-factory/shell'
import { isMobileDevice } from '@things-factory/utils'

import { fetchLocationSortingRule } from '../../fetch-location-sorting-rule'
import { LOCATION_SORTING_RULE, ORDER_TYPES, WORKSHEET_TYPE } from '../constants'

class InventoryAssignPopup extends localize(i18next)(LitElement) {
  static get properties() {
    return {
      worksheetId: String,
      worksheetNo: String,
      batchId: String,
      productId: String,
      productName: String,
      productSku: String,
      productBrand: String,
      productPickingStrategy: String,
      bizplaceId: String,
      packingType: String,
      uom: String,
      packingSize: Number,
      worksheetType: String,
      releaseQty: Number,
      releaseUomValue: Number,
      config: Object,
      _data: Object,
      data: Object,
      pickQty: Number,
      pickUomValue: Number,
      orderProductId: String
    }
  }

  static get styles() {
    return [
      MultiColumnFormStyles,
      css`
        :host {
          padding: 10px;
          display: flex;
          flex-direction: column;
          overflow-x: overlay;
          background-color: var(--main-section-background-color);
        }
        .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;
        }
        .button-container {
          padding: var(--button-container-padding);
          margin: var(--button-container-margin);
          text-align: var(--button-container-align);
          background-color: var(--button-container-background);
          height: var(--button-container-height);
        }
      `
    ]
  }

  render() {
    return html`
      <form id="input-form" class="multi-column-form">
        <fieldset>
          <legend>${i18next.t('title.inventory_selection')}</legend>
          <label>${i18next.t('label.release_qty')}</label>
          <input readonly name="releaseQty" value="${`${this.pickQty} / ${this.releaseQty}`}" />

          <label>${i18next.t('label.release_uom_value')}</label>
          <input
            readonly
            name="releaseUomValue"
            value="${`${Math.round(this.pickUomValue * 100) / 100} / ${this.releaseUomValue}`}"
          />
        </fieldset>
      </form>

      <div class="grist">
        <data-grist
          id="grist"
          .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
          .config=${this.config}
          .data="${this.data}"
          @field-change="${this._onInventoryFieldChanged.bind(this)}"
        ></data-grist>
      </div>

      <div class="button-container">
        <mwc-button
          raised
          @click="${this.submitPickedItems.bind(this)}"
          label="${i18next.t('button.submit')}"
        ></mwc-button>
      </div>
    `
  }

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

  updated(changedProps) {
    if (changedProps.has('_data')) {
      this._calculateData(this._data)
    }
  }

  async firstUpdated() {
    this.locationSortingRules = await fetchLocationSortingRule(LOCATION_SORTING_RULE.INVENTORY_ASSIGNMENT.value)

    this.config = {
      list: {
        fields: [
          'picked',
          'palletId',
          'productSku',
          'productBrand',
          'product',
          'location',
          'pickedQty',
          'qty',
          'pickUomValue',
          'availableUomValue',
          'expirationDate',
          'createdAt'
        ]
      },
      pagination: { infinite: true },
      rows: {
        classifier: record => {
          return {
            emphasized: record.picked
          }
        }
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'boolean',
          name: 'picked',
          header: i18next.t('field.selected'),
          width: 60
        },
        {
          type: 'string',
          name: 'palletId',
          header: i18next.t('field.lot_id'),
          width: 130
        },
        {
          type: 'string',
          name: 'productSku',
          header: i18next.t('field.sku'),
          width: 130
        },
        {
          type: 'object',
          name: 'product',
          header: i18next.t('field.product'),
          width: 250
        },
        {
          type: 'string',
          name: 'productBrand',
          header: i18next.t('field.brand'),
          width: 130
        },
        {
          type: 'object',
          name: 'location',
          header: i18next.t('field.location'),
          width: 120
        },
        {
          type: 'float',
          name: 'qty',
          header: i18next.t('field.available_qty'),
          width: 100
        },
        {
          type: 'float',
          name: 'pickQty',
          header: i18next.t('field.pick_qty'),
          record: { editable: true },
          width: 80
        },
        {
          type: 'string',
          name: 'availableUomValue',
          header: i18next.t('field.available_uom_value'),
          width: 100
        },
        {
          type: 'float',
          name: 'pickUomValue',
          header: i18next.t('field.pick_uom_value'),
          record: { editable: true },
          width: 100
        },
        {
          type: 'date',
          name: 'expirationDate',
          header: i18next.t('field.expiration'),
          sortable: true,
          width: 150
        },
        {
          type: 'datetime',
          name: 'createdAt',
          header: i18next.t('field.stored_at'),
          sortable: true,
          width: 180
        }
      ]
    }

    this.fetchInventoriesByStrategy()
  }

  async fetchInventoriesByStrategy() {
    const response = await client.query({
      query: gql`
        query inventoriesByStrategy(
          $worksheetId: String!
          $batchId: String!
          $bizplaceId: String
          $productName: String!
          $productSku: String!
          $packingType: String!
          $packingSize: Float!
          $uom: String!
          $pickingStrategy: String!
          $locationSortingRules: [Sorting!]!
        ) {
          inventoriesByStrategy(
            worksheetId: $worksheetId
            batchId: $batchId
            bizplaceId: $bizplaceId
            productName: $productName
            productSku: $productSku
            packingType: $packingType
            packingSize: $packingSize
            uom: $uom
            pickingStrategy: $pickingStrategy
            locationSortingRules: $locationSortingRules
          ) {
            items {
              id
              palletId
              expirationDate
              product {
                id
                name
                description
                sku
                brand
              }
              location {
                name
              }
              packingType
              packingSize
              qty
              uomValue
              uom
              createdAt
            }
            total
          }
        }
      `,
      variables: {
        worksheetId: this.worksheetId,
        batchId: this.batchId,
        bizplaceId: this.bizplaceId,
        productName: this.productName,
        productSku: this.productSku,
        packingType: this.packingType,
        packingSize: this.packingSize,
        uom: this.uom,
        pickingStrategy: this.productPickingStrategy,
        locationSortingRules: this.locationSortingRules
      }
    })

    if (!response.errors) {
      this._data = {
        records: response.data.inventoriesByStrategy.items.map(item => {
          return {
            ...item,
            ...item.location,
            productSku: item.product.sku,
            productBrand: item.product.brand,
            availableUomValue: item.uomValue + ' ' + item.uom
          }
        }),
        total: response.data.inventoriesByStrategy.total
      }
    }
  }

  _calculateData(_data) {
    this.pickQty = 0
    this.pickUomValue = 0

    this.data = {
      ..._data,
      records: _data.records.map(item => {
        let picked = false
        let pickQty = 0
        let pickUomValue = 0

        if (this.pickQty < this.releaseQty) {
          picked = true
          const leftQty = this.releaseQty - this.pickQty
          const leftUomValue = this.releaseUomValue - this.pickUomValue
          pickQty = leftQty > item.qty ? item.qty : leftQty
          pickUomValue = leftUomValue > item.uomValue ? item.uomValue : leftUomValue

          // rounding off the pickUomValue will update the *Pick UomValue* column on grist
          pickUomValue = Math.round(pickUomValue * 100) / 100
          this.pickQty += pickQty
          this.pickUomValue += pickUomValue
        }

        // need to round off so that it will bypass the validation upon submission
        this.pickUomValue = Math.round(this.pickUomValue * 100) / 100

        return {
          ...item,
          ...item.location,
          picked,
          pickQty,
          pickUomValue
        }
      })
    }
  }

  async _onInventoryFieldChanged(e) {
    const columnName = e.detail.column.name
    if (columnName === 'pickQty' || columnName === 'pickUomValue') {
      let totalPickQty = 0
      let totalPickUomValue = 0

      if (columnName === 'pickQty') {
        this.data = {
          records: this.grist.dirtyData.records.map(data => {
            let pickQty = data.pickQty
            let pickUomValue = (data.uomValue / data.qty) * data.pickQty
            pickUomValue = Math.round(pickUomValue * 100) / 100

            if (pickQty > data.qty || Number.isNaN(pickQty)) {
              pickQty = data.qty
              pickUomValue = data.uomValue
            } else if (pickQty < 0) {
              pickQty = 0
              pickUomValue = 0
            }

            totalPickQty += pickQty
            totalPickUomValue += pickUomValue

            return {
              ...data,
              pickQty,
              pickUomValue,
              picked: Boolean(data.pickQty)
            }
          })
        }
      } else if (columnName === 'pickUomValue') {
        this.data = {
          records: this.grist.dirtyData.records.map(data => {
            let pickQty = Math.round((data.qty * data.pickUomValue) / data.uomValue)
            let pickUomValue = data.pickUomValue

            if (pickUomValue > data.uomValue || Number.isNaN(pickUomValue)) {
              pickQty = data.qty
              pickUomValue = data.uomValue
            } else if (pickUomValue < 0) {
              pickQty = 0
              pickUomValue = 0
            }

            totalPickQty += pickQty
            totalPickUomValue += pickUomValue

            return {
              ...data,
              pickQty: Math.round((pickQty * data.uomValue) / data.uomValue),
              pickUomValue,
              picked: Boolean(data.pickUomValue)
            }
          })
        }
      }

      await this.grist.updateComplete
      this.pickQty = totalPickQty
      this.pickUomValue = Math.round(totalPickUomValue * 100) / 100
    }
  }

  async submitPickedItems() {
    try {
      this._checkColumnValidity()

      let response
      switch (this.worksheetType) {
        case WORKSHEET_TYPE.BATCH_PICKING.value:
          response = await client.mutate({
            mutation: gql`
              mutation generateBatchPickingWorksheetDetails(
                $worksheetNo: String!
                $batchId: String!
                $productId: String!
                $packingType: String!
                $worksheetDetails: [NewWorksheetDetail]
              ) {
                generateBatchPickingWorksheetDetails(
                  worksheetNo: $worksheetNo
                  batchId: $batchId
                  productId: $productId
                  packingType: $packingType
                  worksheetDetails: $worksheetDetails
                )
              }
            `,
            variables: {
              worksheetNo: this.worksheetNo,
              batchId: this.batchId,
              productId: this.productId,
              packingType: this.packingType,
              worksheetDetails: this._composeWorksheetDetails()
            }
          })
          break

        case WORKSHEET_TYPE.PICKING.value:
          response = await client.mutate({
            mutation: gql`
              mutation generateReleaseGoodWorksheetDetails(
                $worksheetNo: String!
                $batchId: String!
                $productId: String!
                $packingType: String!
                $packingSize: Float!
                $worksheetDetails: [NewWorksheetDetail]
              ) {
                generateReleaseGoodWorksheetDetails(
                  worksheetNo: $worksheetNo
                  batchId: $batchId
                  productId: $productId
                  packingType: $packingType
                  packingSize: $packingSize
                  worksheetDetails: $worksheetDetails
                )
              }
            `,
            variables: {
              worksheetNo: this.worksheetNo,
              batchId: this.batchId,
              productId: this.productId,
              packingType: this.packingType,
              packingSize: this.packingSize,
              worksheetDetails: this._composeWorksheetDetails()
            }
          })
          break
      }

      if (!response.errors) {
        this.dispatchEvent(new CustomEvent('completed'))
        history.back()
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  _checkColumnValidity() {
    if (this.pickQty > this.releaseQty)
      throw new Error(i18next.t('text.selected_item_qty_is_more_than_order_release_qty'))
    if (this.pickQty < this.releaseQty)
      throw new Error(i18next.t('text.selected_item_qty_is_less_than_order_release_qty'))
    if (this.pickUomValue > this.releaseUomValue) throw new Error(i18next.t('text.picked_uom_value_over_than_release'))
    if (this.pickUomValue < this.releaseUomValue) throw new Error(i18next.t('text.picked_uom_value_less_than_release'))
  }

  _composeWorksheetDetails() {
    return this.grist.dirtyData.records
      .filter(record => record.pickQty && record.pickUomValue)
      .map(record => {
        return {
          targetInventory: {
            inventory: { id: record.id },
            product: { id: record.product.id },
            releaseQty: record.pickQty,
            releaseUomValue: record.pickUomValue,
            type: ORDER_TYPES.RELEASE_OF_GOODS.value,
            uom: record.uom
          },
          targetProduct: {
            id: this.orderProductId
          }
        }
      })
  }

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

window.customElements.define('inventory-assign-popup', InventoryAssignPopup)
