import '@things-factory/barcode-ui'
import '@things-factory/grist-ui'
import './show-ro-list-popup'
import '../components/worksheet-information-popup'
import './tote-popup'
import './sorting-same-gtin-popup'

import gql from 'graphql-tag'
import { css, html } from 'lit-element'
import { connect } from 'pwa-helpers/connect-mixin.js'

import { sleep } from '@operato/utils'
import { MultiColumnFormStyles } from '@things-factory/form-ui'
import { i18next, localize } from '@things-factory/i18n-base'
import { openPopup } from '@things-factory/layout-base'
import { client, CustomAlert, PageView, store } from '@things-factory/shell'
import { CommonButtonStyles } from '@things-factory/styles'
import { isMobileDevice } from '@things-factory/utils'

import { fetchSettingRule } from '../../fetch-setting-value'
import { WORKSHEET_STATUS } from '../constants'
import { decodeQR, fetchPageSettings, isValidHttpUrl } from '../../util'

const VALIDATE_QC_SEALS = gql`
  query validateQcSeals($releaseGoodNo: String!) {
    validateQcSeals(releaseGoodNo: $releaseGoodNo) {
      valid
      error
      minimumSealNumber
    }
  }
`

class SortingProduct extends connect(store)(localize(i18next)(PageView)) {
  static get properties() {
    return {
      _enableProductScanning: Boolean,
      _qrProcessing: Number,
      _selectedOrderInventory: Object,
      _selectedTaskStatus: String,
      _worksheetInfo: Object,
      _selectedReleaseGood: Object,
      _enableToteScanningValue: Number,
      _disableQC: Boolean,
      _enableSortingQty: Boolean,
      _displayConversion: Boolean,
      bizplaceName: String,
      customerCompanyDomainId: String,
      config: Object,
      data: Object,
      releaseOrders: Array,
      taskNo: String,
      toteNo: String
    }
  }

  static get styles() {
    return [
      MultiColumnFormStyles,
      css`
        :host {
          display: flex;
          flex-direction: column;
          overflow: hidden;
        }
        data-grist {
          overflow: auto;
          flex: 1;
        }
        #form-section {
          background-color: white;
        }
        h2 {
          padding: var(--subtitle-padding);
          font: var(--subtitle-font);
          color: var(--subtitle-text-color);
          border-bottom: var(--subtitle-border-bottom);
        }
        legend mwc-icon {
          vertical-align: middle;
          margin: var(--grist-title-icon-margin);
          font-size: var(--grist-title-icon-size);
          color: var(--grist-title-icon-color);
          cursor: pointer;
        }
      `
    ]
  }

  get context() {
    var actions = []

    if (this.taskNo) {
      // Tote button only appears when disable-qc-in-loading is true AND enable-tote-scanning is 1 or above
      if (this._disableQC && this._enableToteScanningValue >= 1) {
        actions.push({
          title: i18next.t('button.tote'),
          action: this._openTotePopup.bind(this),
          ...CommonButtonStyles.activate
        })
      }
      actions.push({
        title: i18next.t('button.release_orders'),
        action: this._openSelectReleaseOrderPopup.bind(this),
        ...CommonButtonStyles.inspect
      })

      if (this.completed) {
        actions.push({
          title: i18next.t('button.order_sorted'),
          action: this._completeOrderSorting.bind(this),
          ...CommonButtonStyles.submit
        })
      }

      if (this.releaseOrders?.length == 0) {
        actions.push({
          title: i18next.t('button.complete'),
          action: this._completeSortingWorksheet.bind(this),
          ...CommonButtonStyles.submit
        })
      }
    }

    return {
      title: i18next.t('title.sorting'),
      actions,
      help: 'operato-wms/outbound/sorting-product'
    }
  }

  get completed() {
    return this.data.records.every(record => record.completed)
  }

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

  get infoForm() {
    return this.shadowRoot.querySelector('form#info-form')
  }

  get inputForm() {
    return this.shadowRoot.querySelector('form#input-form')
  }

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

  get scannable() {
    return this._selectedTaskStatus && this._selectedTaskStatus === WORKSHEET_STATUS.EXECUTING.value
  }

  get sortingQtyInput() {
    return this.shadowRoot?.querySelector('input[name=sortingQty]')
  }

  get taskNoInput() {
    const element = this.shadowRoot?.querySelector('barcode-scanable-input[name=taskNo]')
    return element?.shadowRoot?.querySelector('input')
  }

  get serialInput() {
    const element = this.shadowRoot?.querySelector('barcode-scanable-input[name=serialNumber]')
    return element?.shadowRoot?.querySelector('input')
  }

  _focusOnTaskNoInput() {
    if (this.taskNoInput) {
      setTimeout(() => this.taskNoInput.focus(), 100)
    }
  }

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

  _focusOnSortingQtyInput() {
    if (this.sortingQtyInput) {
      setTimeout(() => this.sortingQtyInput.focus(), 100)
    }
  }

  _focusOnSerialInput() {
    if (this.serialInput) {
      setTimeout(() => this.serialInput.focus(), 50)
    }
  }

  render() {
    return html`
      <form class="multi-column-form">
        <fieldset>
          <label>${i18next.t('label.scan_task_no')}</label>
          <barcode-scanable-input
            name="taskNo"
            custom-input
            @keypress="${async e => {
              if (e.keyCode === 13) {
                e.preventDefault()
                if (this.taskNoInput.value) {
                  this._fetchReleaseOrderList(this.taskNoInput.value)
                }
              }
            }}"
          ></barcode-scanable-input>
        </fieldset>
      </form>
      <form id="input-form" class="multi-column-form" @keypress="${this._sorting.bind(this)}">
        <fieldset>
          ${this._enableProductScanning
            ? html`
                <label>${i18next.t('label.scan_product_barcode')}</label>
                <barcode-scanable-input
                  name="productBarcode"
                  custom-input
                  @keypress="${async e => {
                    if (this._qrProcessing > 0 && e.keyCode === 13) {
                      e.preventDefault()
                      await this._processQRAndFillForm(e)
                    }
                  }}"
                ></barcode-scanable-input>
              `
            : ''}
          ${(this._enableSortingQty && !this._isRequireSerialNumberScanningOutbound) || !this._enableProductScanning
            ? html`
                <label>${i18next.t('label.enter_picked_qty')}</label>
                <input type="number" min="1" name="sortingQty" />
              `
            : ''}
          ${this._isRequireSerialNumberScanningOutbound
            ? html`
                <label>${i18next.t('label.serial_number')}</label>
                <barcode-scanable-input
                  name="serialNumber"
                  custom-input
                  @keypress="${async e => {
                    if (this._qrProcessing > 0 && e.keyCode === 13) {
                      e.preventDefault()
                      await this._processQRAndFillForm(e)
                    }
                  }}"
                ></barcode-scanable-input>
              `
            : ``}
        </fieldset>
      </form>
      <data-grist .mode=${isMobileDevice() ? 'LIST' : 'GRID'} .config=${this.config} .data=${this.data}>
        <div slot="headroom" id="form-section">
          <form id="info-form" class="multi-column-form">
            <fieldset>
              <legend>
                ${i18next.t('title.order_no')}: ${this.taskNo}
                ${this.taskNo
                  ? html`(${this._selectedReleaseGood})
                      <mwc-icon @click="${this._openWorksheetInformation.bind(this)}">info</mwc-icon>`
                  : ''}
              </legend>
            </fieldset>
          </form>
        </div>
      </data-grist>
    `
  }

  constructor() {
    super()
    this.data = { records: [] }
    this.taskNo = ''
    this._isRequireSerialNumberScanningOutbound = false
    this._enableProductScanning = false
    this._selectedOrderInventory = null
    this._selectedTaskStatus = null
    this._enableSortingQty = false
    this._qrProcessing = 0
    this._enableToteScanningValue = 0
    this._disableQC = false
    this._displayConversion = false
  }

  updated(changedProps) {
    // Update context when relevant properties change
    const shouldUpdateContext =
      (changedProps.has('_selectedTaskStatus') && this._selectedTaskStatus) ||
      changedProps.has('_enableToteScanningValue') ||
      changedProps.has('_disableQC') ||
      changedProps.has('taskNo')

    if (shouldUpdateContext) {
      this.updateContext()
    }
  }

  pageUpdated(changes, lifecycle) {
    if (this.active) {
      this._focusOnTaskNoInput()
    }
  }

  async pageInitialized() {
    // Fetch domain-level settings
    let domainSettings = await fetchPageSettings({
      settings: {
        name: 'name',
        operator: 'in',
        value: ['display-conversion']
      }
    })
    const domainSettingsMap = new Map(domainSettings.settings.map(setting => [setting.name, setting.value]))
    this._displayConversion = domainSettingsMap.get('display-conversion') === 'true'

    this.config = {
      rows: {
        appendable: false,
        handlers: {
          click: async (columns, data, column, record, rowIndex) => {
            if (data.records.length && record) {
              this._selectedOrderInventory = record
              this._selectedTaskStatus = record.status
              this._isRequireSerialNumberScanningOutbound = record.isRequireSerialNumberScanningOutbound

              if (this.scannable) {
                if (this._enableProductScanning) this._focusOnProductBarcodeInput()
              }

              this.updateContext()
            }
          }
        }
      },
      pagination: { infinite: true },
      list: {
        fields: [
          'completed',
          'sku',
          'product',
          'batchId',
          'packingType',
          'packingSize',
          'expirationDate',
          'progress',
          'conversion',
          'binRemarks'
        ]
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'boolean',
          name: 'completed',
          header: i18next.t('field.done'),
          width: 40
        },
        {
          type: 'string',
          name: 'sku',
          header: i18next.t('field.sku'),
          label: true,
          width: 120
        },
        {
          type: 'object',
          name: 'product',
          header: i18next.t('field.product'),
          label: true,
          width: 140
        },
        {
          type: 'string',
          name: 'batchId',
          header: i18next.t('field.batch_id'),
          label: true,
          width: 140
        },
        {
          type: 'string',
          name: 'packingType',
          header: i18next.t('field.packing_type'),
          label: true,
          width: 140
        },
        {
          type: 'number',
          name: 'packingSize',
          header: i18next.t('field.packing_size'),
          label: true,
          width: 140
        },
        {
          type: 'date',
          name: 'expirationDate',
          header: i18next.t('field.exp_date'),
          label: true,
          width: 140
        },
        {
          type: 'string',
          name: 'progress',
          header: i18next.t('field.progress'),
          label: true,
          width: 120
        },
        {
          type: 'string',
          name: 'conversion',
          header: i18next.t('field.pack_conversion'),
          label: true,
          hidden: !this._displayConversion,
          width: 140
        },
        {
          type: 'string',
          name: 'binRemarks',
          header: i18next.t('field.bin_location'),
          label: true,
          width: 400
        }
      ]
    }
  }

  async _fetchReleaseOrderList(taskNo) {
    try {
      const response = await client.query({
        query: gql`
          query findSortingReleaseOrdersByTaskNo($taskNo: String!) {
            findSortingReleaseOrdersByTaskNo(taskNo: $taskNo) {
              releaseGoods {
                id
                name
                refNo
                refNo2
                status
                district
                attentionCompany
                deliveryAddress1
                deliveryAddress2
                deliveryAddress3
                deliveryAddress4
                deliveryAddress5
                sortedByUser
                itemCounts {
                  total
                  pending
                }
              }
              selectedReleaseGood
              taskNo
            }
          }
        `,
        variables: { taskNo }
      })

      if (!response.errors) {
        this.taskNo = response.data.findSortingReleaseOrdersByTaskNo.taskNo
        this.releaseOrders = response.data.findSortingReleaseOrdersByTaskNo.releaseGoods.map(ro => {
          const addressParts = [
            ro.deliveryAddress1,
            ro.deliveryAddress2,
            ro.deliveryAddress3,
            ro.deliveryAddress4,
            ro.deliveryAddress5
          ].filter(addr => addr && addr.trim() !== '')
           .map(addr => addr.trim().replace(/,+\s*$/, ''))
          
          return {
            ...ro,
            address: addressParts.join(', ')
          }
        })

        if (response.data.findSortingReleaseOrdersByTaskNo?.selectedReleaseGood) {
          await this._setSelectedRO(response.data.findSortingReleaseOrdersByTaskNo.selectedReleaseGood)
        } else {
          if (this.releaseOrders?.find(itm => itm.status == 'SORTING')) {
            await this._openSelectReleaseOrderPopup()
          } else {
            await this._completeSortingWorksheet()
          }
        }
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _openSelectReleaseOrderPopup() {
    await sleep(1010)
    openPopup(
      html`
        <show-ro-list-popup
          .releaseOrders=${this.releaseOrders}
          .sorting=${true}
          @ro-selected-sorting=${async e => {
            await this._setSelectedRO(e.detail.ro?.name)
          }}
        ></show-ro-list-popup>
      `,
      {
        backdrop: true,
        size: 'medium',
        title: i18next.t('title.release_orders')
      }
    )
  }

  async _setSelectedRO(ro) {
    this._selectedReleaseGood = ro
    await this._fetchItemList(this._selectedReleaseGood)
    // Wait for component to finish updating before accessing DOM elements
    await this.updateComplete
    // Clear input fields after component has rendered
    if (this._enableProductScanning && this.productBarcodeInput) {
      this.productBarcodeInput.value = ''
    }
    if (this.sortingQtyInput) {
      this.sortingQtyInput.value = ''
    }
    if (this.serialInput) {
      this.serialInput.value = ''
    }
    this.toteNo = ''
    this._lastScannedProductId = ''
    // Properties are updated in _fetchItemList, which triggers updated() hook
    // which will call updateContext() automatically
  }

  _openWorksheetInformation() {
    openPopup(
      html`
        <worksheet-information-popup .worksheetInformation="${this._worksheetInfo}"></worksheet-information-popup>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.order_information')
      }
    )
  }

  async _openTotePopup() {
    await sleep(1010)

    // Query for minimum seal number setting
    let minimumSealNumber = 0
    try {
      const validationResponse = await client.query({
        query: VALIDATE_QC_SEALS,
        variables: { releaseGoodNo: this._selectedReleaseGood }
      })

      if (!validationResponse.errors && validationResponse.data?.validateQcSeals) {
        minimumSealNumber = validationResponse.data.validateQcSeals.minimumSealNumber || 0
      }
    } catch (err) {
      // If query fails, default to 0 (don't block tote scanning)
      console.warn('Failed to fetch minimum seal number:', err)
    }

    openPopup(
      html`
        <tote-popup
          .orderNo="${this._selectedReleaseGood}"
          .toteNo="${this.toteNo}"
          .bizplace="${this.bizplaceName}"
          .minimumSealNumber="${minimumSealNumber}"
          @completed="${async e => {
            this.toteNo = e.detail
            // Refresh the item list to reflect any undone items
            await this._fetchItemList(this._selectedReleaseGood)
          }}"
        ></tote-popup>
      `,
      {
        closable: false,
        backdrop: false,
        escapable: false,
        size: 'large',
        title: i18next.t('title.tote_scanning')
      }
    )
  }

  async _fetchItemList(releaseOrderNo) {
    const response = await client.query({
      query: gql`
        query sortingWorksheet($releaseGoodNo: String!, $taskNo: String!) {
          sortingWorksheet(releaseGoodNo: $releaseGoodNo, taskNo: $taskNo) {
            worksheetInfo {
              bizplaceName
              partnerDomainId
              customerCompanyDomainId
              refNo
              refNo2
              refNo3
              startedAt
              releaseGood {
                id
                name
                status
              }
            }
            worksheetDetailInfos {
              batchId
              productId
              productSku
              productName
              productDescription
              productBarcodes {
                id
                gtin
              }
              isRequireSerialNumberScanningOutbound
              expirationDate
              releaseQty
              sortedQty
              status
              packingType
              packingSize
              releaseGood {
                id
                name
              }
              binRemarks
              conversion
            }
          }
        }
      `,
      variables: { releaseGoodNo: releaseOrderNo, taskNo: this.taskNo }
    })
    if (!response.errors) {
      const { worksheetInfo, worksheetDetailInfos } = response.data.sortingWorksheet
      this._worksheetInfo = worksheetInfo
      this.bizplaceName = worksheetInfo.bizplaceName
      this.customerCompanyDomainId = worksheetInfo.customerCompanyDomainId

      this.data = { records: this.formatWorksheetDetailInfos(worksheetDetailInfos) }
      if (!this._selectedOrderInventory) {
        this._selectedOrderInventory = worksheetDetailInfos[0]
        this._isRequireSerialNumberScanningOutbound = worksheetDetailInfos[0].isRequireSerialNumberScanningOutbound
      }

      //fetch partner page settings
      let pageSettings = await fetchPageSettings({
        settings: {
          name: 'name',
          operator: 'in',
          value: [
            'enable-product-scanning',
            'enable-tote-scanning',
            'enable-sorting-qty',
            'qr-code-scanning',
            'disable-QC-in-loading'
          ]
        },
        settingsDomain: worksheetInfo.partnerDomainId
      })

      const settingsMap = new Map(pageSettings.settings.map(setting => [setting.name, setting.value]))

      this._enableProductScanning = settingsMap.get('enable-product-scanning') === 'true'
      this._enableToteScanningValue = parseInt(settingsMap.get('enable-tote-scanning') || '0', 10)
      // Handle both boolean true and string 'true' (case-insensitive)
      const disableQCValue = settingsMap.get('disable-QC-in-loading')
      this._disableQC = disableQCValue === true || String(disableQCValue).toLowerCase().trim() === 'true'
      this._enableSortingQty = settingsMap.get('enable-sorting-qty') === 'true'
      this._qrProcessing = settingsMap.get('qr-code-scanning')

      // Update context after properties are set
      this.updateContext()
      this._completeHandler()

      this.focusPage(worksheetDetailInfos)
    }
  }

  async focusPage(worksheetDetailInfos) {
    this.data = { records: this.formatWorksheetDetailInfos(worksheetDetailInfos) }
    if (!this._selectedOrderInventory) {
      this._selectedOrderInventory = worksheetDetailInfos[0]
      this._isRequireSerialNumberScanningOutbound = worksheetDetailInfos[0].isRequireSerialNumberScanningOutbound
    }

    // Wait for component to finish updating before accessing DOM elements
    await this.updateComplete

    if (this._isRequireSerialNumberScanningOutbound && !this._enableProductScanning) {
      this._focusOnSerialInput()
    } else if (this._enableProductScanning) {
      if (this.productBarcodeInput?.value) {
        if (this._isRequireSerialNumberScanningOutbound) {
          this._focusOnSerialInput()
        } else if (this._enableSortingQty) {
          this._focusOnSortingQtyInput()
        } else {
          this._focusOnProductBarcodeInput()
        }
      } else {
        this._focusOnProductBarcodeInput()
      }
    } else {
      this._focusOnSortingQtyInput()
    }
  }

  async _openSortingSameGtinPopup(records) {
    await sleep(1010)
    openPopup(
      html`
        <sorting-same-gtin-popup
          .sortingInventories="${records}"
          @selections="${async e => {
            await this.scanProductSorting(e.detail[0].productId, true)
          }}"
        ></sorting-same-gtin-popup>
      `,
      {
        backdrop: true,
        size: 'medium',
        title: i18next.t('title.select_product_batch')
      }
    )
  }

  async _processQRAndFillForm(e) {
    // QR scanning
    let isBarcode = isValidHttpUrl(this.productBarcodeInput.value)
    if (
      this._enableProductScanning &&
      this._qrProcessing > 0 &&
      (isBarcode || isValidHttpUrl(this.serialInput?.value))
    ) {
      let input = isBarcode ? this.productBarcodeInput.value : this.serialInput.value
      let config = JSON.parse(
        await fetchSettingRule('qr-data-parsing-configuration', this._worksheetInfo.partnerDomainId)
      )
      // Get GTIN and serial number from QR code
      let data = await decodeQR(input, config)

      // Fill up form
      if (this.productBarcodeInput && data.productBarcode) {
        this.productBarcodeInput.value = data.productBarcode
      }
      if (this.serialInput && data.serialNumber) {
        this.serialInput.value = data.serialNumber
      }

      await sleep(1010)

      this._sorting(e)
    }
  }

  async _sorting(e) {
    if (e.keyCode !== 13) return
    e.preventDefault()
    try {
      let answer = await this._validateSorting()
      if (!answer) return

      let response
      if (this._enableProductScanning) {
        const records = this.data.records.filter(
          record =>
            record.productBarcodes.find(pb => pb.gtin == this.productBarcodeInput.value) && record.status !== 'SORTED'
        )

        if (records.length > 1 && !this?._lastScannedProductId) {
          this._openSortingSameGtinPopup(records)
          return
        } else {
          let scannedProductId = records[0].productId
          this._productId = scannedProductId
          response = await this.scanProductSorting(scannedProductId)
        }
      } else {
        response = await this.commonSorting()
      }

      if (this._isRequireSerialNumberScanningOutbound) {
        this.serialInput.value = ''
      }

      if (this._enableProductScanning && !this._isRequireSerialNumberScanningOutbound) {
        if (this._enableSortingQty) {
          this.sortingQtyInput.value = ''
        } else {
          this.productBarcodeInput.value = ''
        }
      } else if (!this._enableProductScanning && !this._isRequireSerialNumberScanningOutbound) {
        this.sortingQtyInput.value = ''
      }

      if (!response?.errors || (this._enableProductScanning && records.length > 1)) {
        this._fetchItemList(this._selectedReleaseGood)
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async commonSorting() {
    return await client.mutate({
      mutation: gql`
        mutation sortingProduct(
          $taskNo: String!
          $releaseGoodNo: String!
          $productId: String!
          $sortingQty: Float!
          $serialNumber: String
          $toteNo: String
        ) {
          sortingProduct(
            taskNo: $taskNo
            releaseGoodNo: $releaseGoodNo
            productId: $productId
            sortingQty: $sortingQty
            serialNumber: $serialNumber
            toteNo: $toteNo
          )
        }
      `,
      variables: {
        taskNo: this.taskNo,
        releaseGoodNo: this._selectedReleaseGood,
        productId: this._selectedOrderInventory?.product.id,
        sortingQty: this._isRequireSerialNumberScanningOutbound ? 1 : parseFloat(this.sortingQtyInput.value),
        serialNumber: this._isRequireSerialNumberScanningOutbound ? this.serialInput.value.trim() : null,
        toteNo: this._enableToteScanningValue >= 1 && this.toteNo !== '' ? this.toteNo : null
      }
    })
  }

  async scanProductSorting(productId) {
    const variables = {
      releaseGoodNo: this._selectedReleaseGood,
      taskNo: this.taskNo,
      productBarcode: this.productBarcodeInput.value.trim(),
      serialNumber: this._isRequireSerialNumberScanningOutbound ? this.serialInput.value.trim() : null,
      toteNo: this._enableToteScanningValue >= 1 && this.toteNo !== '' ? this.toteNo : null,
      sortingQty:
        this._enableSortingQty && !this._isRequireSerialNumberScanningOutbound
          ? parseFloat(this.sortingQtyInput.value)
          : null,
      productId
    }

    const response = await client.mutate({
      mutation: gql`
        mutation scanProductSorting(
          $releaseGoodNo: String!
          $taskNo: String!
          $productBarcode: String!
          $serialNumber: String
          $toteNo: String
          $sortingQty: Int
          $productId: String
        ) {
          scanProductSorting(
            releaseGoodNo: $releaseGoodNo
            taskNo: $taskNo
            productBarcode: $productBarcode
            serialNumber: $serialNumber
            toteNo: $toteNo
            sortingQty: $sortingQty
            productId: $productId
          )
        }
      `,
      variables
    })

    this._lastScannedProductId = productId
    this._fetchItemList(this._selectedReleaseGood)
    this._clearField()
  }

  async _completeHandler() {
    if (!this.data?.records?.length || this.data.records.some(record => !record.completed)) return

    this.updateContext()
    const result = await CustomAlert({
      title: i18next.t('title.move_to_next_order'),
      text: i18next.t('text.current_order_complete_sorting'),
      confirmButton: { text: i18next.t('button.okay') }
    })

    if (!result?.value) {
      return
    }

    this._completeOrderSorting()
  }

  async _completeOrderSorting() {
    try {
      const response = await client.mutate({
        mutation: gql`
          mutation completeOrderSorting($releaseGoodNo: String!) {
            completeOrderSorting(releaseGoodNo: $releaseGoodNo)
          }
        `,
        variables: {
          releaseGoodNo: this._selectedReleaseGood
        }
      })
      if (!response.errors) {
        this._showToast({ message: i18next.t('text.current_release_order_sorting_completed') })
        this._selectedOrderInventory = null
        await this._fetchReleaseOrderList(this.taskNo)
        this.updateContext()
      }
    } catch (e) {
      this._showToast
    }
  }

  async _completeSortingWorksheet() {
    try {
      const response = await client.mutate({
        mutation: gql`
          mutation completeWorksheetSorting($taskNo: String!) {
            completeWorksheetSorting(taskNo: $taskNo)
          }
        `,
        variables: {
          taskNo: this.taskNo
        }
      })
      if (!response.errors) {
        await CustomAlert({
          title: i18next.t('title.sorting'),
          text: i18next.t('text.sorting_is_completed'),
          confirmButton: { text: i18next.t('button.complete') }
        })
        this._clearView()
      }
    } catch (e) {
      this._showToast
    }
  }

  async _validateSorting() {
    try {
      if (this._enableProductScanning) {
        if (!this.productBarcodeInput.value) {
          this._focusOnProductBarcodeInput()
          throw new Error(i18next.t('text.product_barcode_is_empty'))
        }

        let answer = await this._checkedScannedProduct()
        if (!answer) return false

        if (this._isRequireSerialNumberScanningOutbound) {
          if (!this.serialInput?.value) {
            this._focusOnSerialInput()
            throw new Error(i18next.t('text.empty_serial_number'))
          }
        }

        if (this._enableSortingQty && !this._isRequireSerialNumberScanningOutbound) {
          if (!parseFloat(this.sortingQtyInput.value)) {
            this.sortingQtyInput.value = ''
            this._focusOnSortingQtyInput()
            throw new Error(i18next.t('text.release_qty_is_empty'))
          }

          if (!(parseFloat(this.sortingQtyInput.value) > 0)) {
            this.sortingQtyInput.value = ''
            this._focusOnSortingQtyInput()
            throw new Error(i18next.t('text.qty_should_be_positive'))
          }

          if (this.sortingQtyInput.value % 1 != 0) {
            this.sortingQtyInput.value = ''
            this._focusOnSortingQtyInput()
            throw new Error(i18next.t('text.qty_should_not_have_decimal'))
          }
        }
      } else {
        // validate for order selection
        if (!this._selectedOrderInventory) throw new Error(i18next.t('text.target_is_not_selected'))

        if (this._isRequireSerialNumberScanningOutbound) {
          if (!this.serialInput?.value) {
            this._focusOnSerialInput()
            throw new Error(i18next.t('text.empty_serial_number'))
          }
        } else {
          if (!this.sortingQtyInput.checkValidity()) throw new Error(i18next.t('text.release_qty_invalid'))

          if (!parseInt(this.sortingQtyInput.value)) {
            this._focusOnSortingQtyInput()
            throw new Error(i18next.t('text.release_qty_is_empty'))
          }

          if (parseInt(this.sortingQtyInput.value) != this._selectedOrderInventory.releaseQty) {
            this._focusOnSortingQtyInput()
            throw new Error(i18next.t('text.wrong_sorting_qty'))
          }
        }
      }

      return true
    } catch (e) {
      this._showToast(e)
      return false
    }
  }

  async _checkedScannedProduct() {
    //fetch any product details with entered gtin
    const response = await this.fetchProductDetails(this.productBarcodeInput?.value || '', '')

    if (!response.errors) {
      let worksheetDetails = this.data.records
      let checkedProductDetails = response.data.checkGtin.items

      //check from grist if any is matched
      const foundProducts = worksheetDetails.filter(wd => {
        return checkedProductDetails.some(
          itm =>
            itm.product.id == wd.product.id && itm.packingType == wd.packingType && itm.packingSize == wd.packingSize
        )
      })

      const foundParentProducts = worksheetDetails.filter(wd => {
        return checkedProductDetails.some(itm => itm.product.id == wd.product.id)
      })

      if (!foundProducts?.length) {
        if (!foundParentProducts?.length) {
          if (this._enableSortingQty && !this._isRequireSerialNumberScanningInbound) {
            if (this.sortingQtyInput?.value) this.sortingQtyInput.value = ''
          }

          if (this._isRequireSerialNumberScanningInbound) {
            if (this.serialNumberInput?.value) this.serialNumberInput.value = ''
          }

          this.productBarcodeInput.value = ''
          this._focusOnProductBarcodeInput()
          throw new Error(i18next.t('text.no_matching_product_found'))
        }
      }

      const product =
        foundProducts?.length == 1
          ? foundProducts[0].product
          : foundProducts?.length == 0 && foundParentProducts?.length > 0
          ? foundParentProducts[0].product
          : null

      if (foundProducts?.length == 1) {
        this._isRequireSerialNumberScanningOutbound = foundProducts[0].isRequireSerialNumberScanningOutbound
        this._selectedOrderInventory = foundProducts[0]
      } else if (foundProducts?.length == 0 && foundParentProducts?.length > 0) {
        this._isRequireSerialNumberScanningOutbound = foundParentProducts[0].isRequireSerialNumberScanningOutbound
        this._selectedOrderInventory = foundParentProducts[0]
      }

      return true
    } else {
      this.productBarcodeInput.value = ''
      this._focusOnProductBarcodeInput()
      return false
    }
  }

  async fetchProductDetails(gtin) {
    return await client.query({
      query: gql`
        query checkGtin($gtin: String!, $customerCompanyDomainId: String!) {
          checkGtin(gtin: $gtin, customerCompanyDomainId: $customerCompanyDomainId) {
            items {
              id
              name
              packingType
              packingSize
              gtin
              product {
                id
                name
                sku
                isRequiredCheckExpiry
                isRequireSerialNumberScanningInbound
                minInboundShelfLife
              }
            }
            total
          }
        }
      `,
      variables: {
        gtin,
        customerCompanyDomainId: this.customerCompanyDomainId
      }
    })
  }

  formatWorksheetDetailInfos(wsdInfos) {
    return wsdInfos
      .map(record => {
        record.completed = record.sortedQty == record.releaseQty
        record.sku = record?.productSku
        record.product = { id: record.productId, name: record.productName, description: record.productDescription }
        record.progress = `${record.sortedQty} / ${record.releaseQty}`

        return record
      })
      .sort((a, b) => a.completed - b.completed)
  }

  _clearView() {
    this.data = { records: [] }
    this.infoForm.reset()
    this.inputForm.reset()
    this.taskNo = ''
    this.taskNoInput.value = ''
    if (this._enableProductScanning) this.productBarcodeInput.value = ''
    if (this._enableSortingQty) this.sortingQtyInput.value = ''
    this._selectedOrderInventory = null
    this._selectedTaskStatus = null
    this.serialInput.value = ''
    this.toteNo = ''
  }

  _clearField() {
    if (this._enableProductScanning) this.productBarcodeInput.value = ''
  }

  _showToast({ type, message }) {
    document.dispatchEvent(
      new CustomEvent('notify', {
        detail: {
          type,
          message
        }
      })
    )
  }
}
window.customElements.define('sorting-product', SortingProduct)
