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

import './worksheet-info'
import './scan-bin-no-popup'
import './report-issue-popup'

import { gql } from 'graphql-tag'
import { css, html, LitElement } from 'lit-element'
import { i18next, localize } from '@things-factory/i18n-base'
import { openPopup } from '@things-factory/layout-base'
import { client, CustomAlert, gqlContext, PageView, store } from '@things-factory/shell'
import { CommonButtonStyles } from '@things-factory/styles'

import { fetchPageSettings, showToast } from '../../../util'
import { BATCH_PICKING, PICKING, ZONE_PICKING, ORDER_INVENTORY_STATUS } from '../../constants'

const PICKING_TYPES = { PICKING, BATCH_PICKING, ZONE_PICKING }

export class PickingExecutionBase extends LitElement {
  static get properties() {
    return {
      parent: Object,
      state: Object,
      data: Object,
      pickingData: Object,
      pickedData: Object,
      _executionData: Object,
      _displayConversion: Boolean
    }
  }

  async connectedCallback() {
    super.connectedCallback()

    const pageSettings = await fetchPageSettings({
      settings: {
        name: 'name',
        operator: 'in',
        value: ['display-conversion']
      }
    })

    const settingsMap = new Map(pageSettings?.settings?.map(setting => [setting.name, setting.value]) || [])
    this._displayConversion = settingsMap.get('display-conversion') === 'true'
  }

  get backButton() {
    return {
      title: i18next.t('button.back'),
      action: () => {
        resetContext(this.parent)
      },
      ...CommonButtonStyles.back
    }
  }

  get completeButton() {
    return {
      title: i18next.t('button.complete'),
      action: async () => {
        await this.completePicking()
        resetContext(this.parent)
      },
      ...CommonButtonStyles.submit
    }
  }

  get partialButton() {
    return {
      title: i18next.t('button.partial_pick'),
      action: async () => {
        await this.partialPicking()
      },
      ...CommonButtonStyles.submit
    }
  }

  get binNoInput() {
    return this.shadowRoot?.querySelector('barcode-scanable-input[name=binNo]')?.shadowRoot.querySelector('input')
  }

  get cartonIdInput() {
    return this.shadowRoot?.querySelector('barcode-scanable-input[name=cartonId]')?.shadowRoot.querySelector('input')
  }

  get productBarcodeInput() {
    return this.shadowRoot
      ?.querySelector('barcode-scanable-input[name=productBarcode]')
      ?.shadowRoot.querySelector('input')
  }

  get qtyInput() {
    return this.shadowRoot?.querySelector('barcode-scanable-input[name=quantity]')?.shadowRoot.querySelector('input')
  }

  _focusOnCartonNoInput() {
    if (this.cartonIdInput) {
      setTimeout(() => this.cartonIdInput.focus(), 50)
    }
  }

  _focusOnProductBarcodeInput() {
    if (this.productBarcodeInput) {
      setTimeout(() => this.productBarcodeInput.focus(), 50)
    }
  }

  _focusOnQtyInput() {
    if (this.qtyInput) {
      setTimeout(() => this.qtyInput.focus(), 50)
    }
  }

  async openTab(e, tabName) {
    this.state = { ...this.state, activeTab: tabName }

    let newData = await this.fetchWorksheet(
      this._executionData.worksheetNo,
      this.parent._context.locationSortingRules,
      tabName
    )
    if (tabName == 'PICKING') {
      this.pickingData = newData.worksheetDetailInfos
    } else if (tabName == 'PICKED') {
      this.pickedData = newData.worksheetDetailInfos
    }
  }

  resetView() {
    this._executionData = {
      ...this._executionData,
      worksheetNo: this.data.worksheetInfo.name,
      worksheetId: this.data.worksheetInfo.id
    }

    this.pickingData = this.data?.worksheetDetailInfos.filter(item => item.pickedQty != item.releaseQty)
    this.pickedData = this.data?.worksheetDetailInfos.filter(item => item.pickedQty > 0)

    this.state = { ...this.state, activeTab: 'PICKING' }
    this.getButtons(this.data)
  }

  getButtons(data) {
    let pickingButton = null

    if (this.state.activeTab == 'PICKED') {
      if (data?.completable == 'true') {
        pickingButton = this.completeButton
      } else if (data?.completable == 'partial') {
        pickingButton = this.partialButton
      }
    }

    this.parent.configContext([pickingButton, this.backButton])
  }

  _getTotalSku() {
    let activeTab = this.state?.activeTab
    let total = 0
    if (activeTab == 'PICKING') {
      let uniqueSku = new Set(this.pickingData?.map(item => item.inventoryId))
      total = uniqueSku.size
    } else {
      //need to add filter incomplete sku only
      let uniqueSku = new Set(this.pickedData?.map(item => item.inventoryId))
      total = uniqueSku.size
    }

    return total
  }

  async showWorksheetInfo(data) {
    openPopup(html` <worksheet-info .data="${data.issues}" .worksheet="${data.worksheetInfo}"></worksheet-info> `, {
      backdrop: true,
      size: 'large',
      title: i18next.t('title.worksheet_info')
    })
  }

  async binNoScanHandler(e) {
    if (e.keyCode === 13) {
      e.preventDefault()
      let binNo = await this.validateBinNo(this.binNoInput.value)
      if (binNo) this._executionData = { ...this._executionData, binNo: binNo }
    }
  }

  async changeBinNoPopup(e) {
    let popup = openPopup(
      html`<scan-bin-no
        @updateBinNo="${e => {
          this._executionData = { ...this._executionData, binNo: e.detail }
          popup.close()
        }}"
      ></scan-bin-no> `,
      {
        closable: false,
        backdrop: false,
        size: 'small'
      }
    )
  }

  async validateBinNo(binNo) {
    if (!binNo) return

    const response = await client.query({
      query: gql`
        query binLocation($name: String!) {
          binLocation(name: $name) {
            id
            name
          }
        }
      `,
      variables: { name: binNo }
    })

    if (!response.errors) {
      return response.data.binLocation.name
    } else {
      this.binNoInput.value = ''
    }
  }

  async openReportIssuePopup(palletId) {
    let popup = openPopup(
      html`
        <report-issue-popup
          ._palletId="${palletId}"
          @submit="${async e => {
            popup.close()
            let newData = await this.fetchWorksheet(
              this._executionData.worksheetNo,
              this.parent._context.locationSortingRules,
              this.state.activeTab
            )
            if (this.data) this.data.issues = newData.issues
            this.pickingData = newData.worksheetDetailInfos
          }}"
        ></report-issue-popup>
      `,
      {
        backdrop: true,
        size: 'medium',
        title: i18next.t('title.report_issue')
      }
    )
  }

  async picking(e) {
    if (e.keyCode !== 13) return
    e.preventDefault()
    try {
      await this.validatePicking()
      const response = await client.mutate({
        mutation: gql`
          mutation scanProductZonePicking(
            $worksheetId: String!
            $cartonId: String!
            $productBarcode: String!
            $binNo: String!
            $pickingQty: Float
          ) {
            scanProductZonePicking(
              worksheetId: $worksheetId
              cartonId: $cartonId
              productBarcode: $productBarcode
              binNo: $binNo
              pickingQty: $pickingQty
            )
          }
        `,
        variables: {
          worksheetId: this._executionData.worksheetId,
          productBarcode: this.productBarcodeInput.value.trim(),
          cartonId: this.cartonIdInput.value.trim(),
          binNo: this._executionData.binNo,
          pickingQty: this.qtyInput?.value ? parseFloat(this.qtyInput.value) : 1
        }
      })

      if (!response.error) {
        //reset input value
        this.productBarcodeInput.value = ''
        this.cartonIdInput.value = ''
        this.qtyInput.value = ''
        this._focusOnCartonNoInput()

        //fetch data for both tabs to ensure banner condition is accurate
        const [pickingData, pickedData] = await Promise.all([
          this.fetchWorksheet(this._executionData.worksheetNo, this.parent._context.locationSortingRules, 'PICKING'),
          this.fetchWorksheet(this._executionData.worksheetNo, this.parent._context.locationSortingRules, 'PICKED')
        ])

        this.pickingData = pickingData.worksheetDetailInfos
        this.pickedData = pickedData.worksheetDetailInfos
      }
    } catch (e) {
      showToast(e)
    }
  }

  async validatePicking() {
    if (!this.cartonIdInput.value) {
      this._focusOnCartonNoInput()
      throw new Error(i18next.t('text.carton_id_is_empty'))
    }
    if (!this.productBarcodeInput.value) {
      this._focusOnProductBarcodeInput()
      throw new Error(i18next.t('text.empty_product_barcode'))
    }
    if (!this.qtyInput.value || this.qtyInput.value < 0) {
      this._focusOnQtyInput()
      throw new Error(i18next.t('text.invalid_qty'))
    }
  }

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

      if (!result.value) return

      const response = await client.mutate({
        mutation: gql`
          mutation completeZonePicking($worksheetId: String!) {
            completeZonePicking(worksheetId: $worksheetId)
          }
        `,
        variables: {
          worksheetId: this._executionData.worksheetId
        }
      })

      if (!response.errors) {
        showToast({ type: 'success', message: i18next.t('text.picking_is_completed') })
      }
    } catch (e) {
      showToast(e)
    }
  }

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

      if (!result.value) return

      const response = await client.mutate({
        mutation: gql`
          mutation completeZonePickingPartially($worksheetId: String!) {
            completeZonePickingPartially(worksheetId: $worksheetId)
          }
        `,
        variables: {
          worksheetId: this._executionData.worksheetId
        }
      })

      if (!response.errors) {
        showToast({ type: 'success', message: i18next.t('text.picking_completed_partially') })
      }
    } catch (e) {
      showToast(e)
    }
  }

  async fetchWorksheet(worksheetNo, locationSortingRules, tab) {
    if (!worksheetNo) return

    try {
      let response = await client.query({
        query: gql`
          query zonePickingWorksheet($name: String!, $locationSortingRules: [Sorting!], $filter: String) {
            zonePickingWorksheet(name: $name, locationSortingRules: $locationSortingRules, filter: $filter) {
              groupedOrderInventories {
                inventory {
                  id
                  palletId
                  cartonId
                  batchId
                  expirationDate
                  location {
                    name
                    description
                  }
                }
                product {
                  sku
                  name
                  description
                  isInventoryDecimal
                }
                releaseQty
                pickedQty
                status
                packingType
                packingSize
                conversion
              }
              inventoryChanges {
                id
                inventory {
                  id
                  product {
                    name
                    sku
                    isInventoryDecimal
                  }
                }
                palletId
                status
                transactionType
                details
              }
              releaseGood {
                id
                refNo2
              }
              completable
            }
          }
        `,
        variables: { name: worksheetNo, locationSortingRules: locationSortingRules, filter: tab }
      })

      if (!response.errors) {
        let newData = await mapWorksheetData(response.data.zonePickingWorksheet)
        this.getButtons(newData)
        return newData
      }
    } catch (e) {
      throw new Error(e.message)
    }
  }
}

async function mapWorksheetData(data) {
  const worksheetDetailInfos = data.groupedOrderInventories.map(orderInv => ({
    inventoryId: orderInv.inventory.id,
    palletId: orderInv.inventory.palletId,
    cartonId: orderInv.inventory.cartonId,
    batchId: orderInv.inventory.batchId,
    product: {
      sku: orderInv.product.sku,
      name: orderInv.product.name,
      description: orderInv.product.description,
      isInventoryDecimal: orderInv.product.isInventoryDecimal
    },
    qty: orderInv.pickedQty,
    releaseQty: orderInv.releaseQty,
    pickedQty: orderInv.pickedQty,
    expirationDate: orderInv.inventory.expirationDate
      ? new Date(orderInv.inventory.expirationDate).toISOString().slice(0, 10).replace('-', '/').replace('-', '/')
      : null,
    status: orderInv.status,
    packingType: orderInv.packingType,
    packingSize: orderInv.packingSize,
    conversion: orderInv.conversion,
    location: {
      name: orderInv.inventory.location.name,
      description: orderInv.inventory.location.description
    },
    relatedOrderInv: {
      id: orderInv.id,
      batchId: orderInv.inventory.batchId,
      packingType: orderInv.packingType,
      product: {
        sku: orderInv.product.sku,
        name: orderInv.product.name,
        description: orderInv.product.description,
        isInventoryDecimal: orderInv.product.isInventoryDecimal
      }
    }
  }))

  const issues = data.inventoryChanges.map(ic => {
    return {
      ...ic,
      inventoryId: ic.inventory.id
    }
  })

  const completable = data.completable

  // Group unique refNo2 values
  const uniqueRefNo2s = [...new Set(data.releaseGood.map(rg => rg.refNo2).filter(refNo2 => refNo2))] // Filter out null/undefined values
  const groupedRefNo2 = uniqueRefNo2s.join(', ')

  // Add grouped refNo2 to worksheet info
  const worksheetInfo = {
    ...data.worksheet,
    groupedRefNo2
  }

  return {
    worksheetDetailInfos,
    issues,
    completable,
    worksheetInfo
  }
}

function resetContext(target) {
  target._data = null
  target.configContext()
}
