import '@things-factory/barcode-ui'
import '@things-factory/grist-ui'
import '../components/popup-note'
import '../components/popup-reusable-pallet'
import './palletizing-pallets-popup'
import '../components/worksheet-information-popup'
import '../components/unloading-worksheet-information-popup'
import './unloaded-inventories-popup'
import './undo-unload-popup'
import './condition-of-goods-popup'
import './select-product-batch'
import './serial-number-list-popup'
import '../components/list-view/product-list-view'

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

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

import { fetchSettingRule } from '../../fetch-setting-value'
import { ARRIVAL_NOTICE, RETURN_ORDER, VAS_TYPE, CONDITION_OF_GOODS } from '../constants'

const AVAIL_ORDER_TYPES = { ARRIVAL_NOTICE, RETURN_ORDER }
class UnloadProduct extends connect(store)(localize(i18next)(PageView)) {
  static get properties() {
    return {
      _worksheetLoaded: Boolean,
      _usingReusablePallet: Boolean,
      _bizplaceName: String,
      _cartonId: String,
      _enableProductScanning: Boolean,
      __minInboundShelfLife: Number,
      _enableCartonLabel: Boolean,
      _enableLotIdInput: Boolean,
      _enableInputQty: Number,
      _isLooseItem: Boolean,
      _isQtyChecked: Boolean,
      _isRequiredCheckExpiry: Boolean,
      _isRequiredCheckManufactureDate: Boolean,
      _isRequireSerialNumberScanningInbound: Boolean,
      _orderNo: String,
      _orderId: String,
      _palletId: String,
      _reusablePallet: Object,
      _scanning: Boolean,
      _selectedOrderProduct: Object,
      _previousSelectedOrderProduct: Object,
      _selectedInventory: Object,
      _unloadedInventories: Array,
      _worksheetInfo: Object,
      customerCompanyDomainId: String,
      orderProductConfig: Object,
      orderProductData: Object,
      orderVasData: Object,
      refOrderType: String,
      _pageSettings: Object,
      _productBarcode: String,
      _productBarcodeScanned: String,
      _packingTypeScanned: String,
      _conditionOfGoods: String
    }
  }

  static get styles() {
    return [
      MultiColumnFormStyles,
      SingleColumnFormStyles,
      css`
        data-grist {
          overflow: auto;
          flex: 1;
        }
        #form-section {
          background-color: white;
        }
        :host {
          display: flex;
          flex-direction: column;
          overflow: hidden;
        }
        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;
        }

        /* Mobile form styles - matching landing page design */
        .mobile-form {
          display: flex;
          flex-direction: column;
          gap: 10px;
          padding: 8px 0;
        }
        .mobile-form fieldset {
          display: flex;
          flex-direction: column;
          align-items: flex-start !important;
          border: none;
          margin: 0;
          padding: 0;
          width: 100%;
          box-sizing: border-box;
        }
        .mobile-form label {
          display: block;
          grid-column: unset !important;
          text-align: left !important;
          align-self: flex-start !important;
          margin-bottom: 4px;
          color: #333;
          font-weight: bold;
          font-size: 14px;
          width: 100%;
          text-transform: capitalize;
        }
        .mobile-form select,
        .mobile-form input[type="text"],
        .mobile-form input[type="date"] {
          grid-column: unset !important;
          width: 100%;
          max-width: 100%;
          height: 24px;
          min-height: 24px;
          border: 1px solid rgba(0, 0, 0, 0.2);
          border-radius: 4px;
          padding: 4px 8px;
          font-size: 12px;
          background-color: white;
          box-sizing: border-box;
        }
        .mobile-form input[type="number"] {
          grid-column: unset !important;
          width: 100%;
          max-width: 100%;
          height: 24px;
          min-height: 24px;
          border: 1px solid rgba(0, 0, 0, 0.2);
          border-radius: 4px;
          padding: 4px 8px;
          padding-right: 24px;
          font-size: 12px;
          background-color: white;
          box-sizing: border-box;
          -moz-appearance: textfield;
        }
        .mobile-form input[type="number"]::-webkit-inner-spin-button,
        .mobile-form input[type="number"]::-webkit-outer-spin-button {
          opacity: 1;
          height: 16px;
        }
        .mobile-form select:focus,
        .mobile-form input:focus {
          outline: none;
          border-color: #017E7F;
        }
        .mobile-form [custom-input] {
          grid-column: unset !important;
          width: 100%;
          box-sizing: border-box;
          border: var(--input-field-border, 1px solid rgba(0, 0, 0, 0.2));
          border-radius: var(--input-field-border-radius, 4px);
        }
        .mobile-form barcode-scanable-input {
          width: 100%;
          max-width: 100%;
          box-sizing: border-box;
          min-width: 0;
          height: 24px;
          min-height: 24px;
        }
        .mobile-form input[disabled] {
          background-color: #f5f5f5;
          cursor: not-allowed;
        }
        .mobile-form .side-by-side-row {
          display: flex;
          flex-direction: row;
          gap: 10px;
        }
        .mobile-form .side-by-side-row fieldset {
          flex: 1;
          min-width: 0;
        }
        /* End of mobile form styles - matching landing page design */
        
        .order-no {
          display: -webkit-box;
          -webkit-line-clamp: 1;
          -webkit-box-orient: vertical;
          overflow: hidden;
        }
        .order-info {
          border: none;
          background: none;
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 8px;
          color: #017E7F;
          font-weight: bold;
        }
        .hr-line {
          border: none;
          border-top: 1px solid #017E7F;
          margin: 0px;
        }
        product-list-view {
          display: flex;
          flex-direction: column;
          flex: 1;
          min-height: 0;
          overflow-y: auto;
          --product-item-active-bg: #F0FFFA;
        }
      `
    ]
  }

  get actualQtyInput() {
    return this.shadowRoot.querySelector('input[name=qty]')
  }

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

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

  get completable() {
    let completable = false
    const orderProductWithIssues = this.orderProductData.records.filter(orderProduct => !orderProduct.validity)
    if (!orderProductWithIssues?.length) {
      completable = this.orderProductData.records.every(record => record.validity)
    } else {
      completable = orderProductWithIssues.every(record => record.issue)
    }
    return completable
  }

  get context() {
    var actions = []
    if (this._worksheetLoaded && !this._enableCartonLabel) {
      actions.push({
        title: i18next.t('button.plastic_pallet'),
        action: this._reusablePalletMode.bind(this),
        ...CommonButtonStyles.activate
      })
    }

    if (this._isLooseItem) {
      actions.push({
        title: i18next.t('button.vas'),
        action: this._openVasPopup.bind(this),
        ...CommonButtonStyles.vas
      })
    }

    if (this._selectedOrderProduct && this._isRequireSerialNumberScanningInbound) {
      if (this._selectedOrderProduct.sku != this._previousSelectedOrderProduct?.sku) {
        this._previousSelectedOrderProduct = this._selectedOrderProduct
        this._productBarcodeScanned = null
      }
      actions.push({
        title: i18next.t('button.serial_number'),
        action: this._openSerialNumberList.bind(this),
        ...CommonButtonStyles.activate
      })
    }

    if (this._selectedOrderProduct && !this._selectedOrderProduct.validity) {
      actions.push({
        title: i18next.t('button.issue'),
        action: this._openIssueNote.bind(this),
        ...CommonButtonStyles.issue
      })
    }

    if (this._unloadedInventories && this._unloadedInventories.length > 0) {
      actions.push(
        {
          title: i18next.t('button.undo'),
          action: this._openUndoUnloadPopup.bind(this),
          ...CommonButtonStyles.undo
        }
      )
    }

    if (this._worksheetInfo) {
      actions.push({
        title: i18next.t('button.condition'),
        action: this._openConditionPopup.bind(this),
        ...CommonButtonStyles.undo,
        icon: 'search'
      })
    }

    if (
      this._unloadedInventories &&
      this._unloadedInventories.length > 0 &&
      this.orderProductData.records.some(task => !task.validity) &&
      this.orderProductData.records.some(task => task.actualPackQty < task.packQty)
    ) {
      actions.push({
        title: i18next.t('button.partial_unload'),
        action: this._completePartially.bind(this),
        ...CommonButtonStyles.submit
      })
    }

    if (this.orderProductData && this.orderProductData.records.length && this.completable) {
      actions.push({
        title: i18next.t('button.complete'),
        action: this._complete.bind(this),
        ...CommonButtonStyles.submit
      })
    }

    return {
      title: i18next.t('title.unloading'),
      help: 'operato-wms/inbound/unload-product',
      actions,
      exportable: {
        name: i18next.t('title.unloading'),
        data: this._exportableData.bind(this)
      }
    }
  }

  get expDateInput() {
    return this.shadowRoot.querySelector('input[name=expDate]')
  }

  get manufactureDateInput() {
    return this.shadowRoot.querySelector('input[name=manufactureDate]')
  }

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

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

  get orderProductGrist() {
    return this.shadowRoot.querySelector('data-grist#order-product-grist')
  }

  get orderNoInput() {
    return this.shadowRoot.querySelector('barcode-scanable-input[name=orderNo]').shadowRoot.querySelector('input')
  }

  get palletProductGrist() {
    return this.shadowRoot.querySelector('data-grist#pallet-product-grist')
  }

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

  get unloadingMobileListConfig() {
    return {
      selectable: false,
      onRecordClick: (record, e, index) => {
        if (record && record.batchId) {
          const unflattenedRecord = this._unflattenData(record)
          this._selectedOrderProduct = unflattenedRecord
          this._isRequiredCheckExpiry = unflattenedRecord.product?.isRequiredCheckExpiry
          this._isRequiredCheckManufactureDate = unflattenedRecord.product?.isRequiredCheckManufactureDate
          this._isRequireSerialNumberScanningInbound = unflattenedRecord.product?.isRequireSerialNumberScanningInbound
          this._isQtyChecked = false

          this.inputForm.reset()
          
          if (this._isRequiredCheckManufactureDate && unflattenedRecord?.manufactureDate && this.refOrderType === AVAIL_ORDER_TYPES.ARRIVAL_NOTICE.value) {
            setTimeout(() => {
              if (this.manufactureDateInput) {
                this.manufactureDateInput.value = this._formatDateForInput(unflattenedRecord.manufactureDate)
              }
            }, 0)
          }

          if (this._isRequiredCheckManufactureDate && unflattenedRecord?.inventory?.manufactureDate && this.refOrderType === AVAIL_ORDER_TYPES.RETURN_ORDER.value) {
            setTimeout(() => {
              if (this.manufactureDateInput) {
                this.manufactureDateInput.value = this._formatDateForInput(unflattenedRecord.inventory.manufactureDate)
              }
            }, 0)
          }
          
          if (this._enableCartonLabel) {
            if (this._enableProductScanning && this.productBarcodeInput) {
              this.productBarcodeInput.readOnly = false
              this.productBarcodeInput.value = ''
            }
            if (unflattenedRecord.product?.isRequireSerialNumberScanningInbound) {
              if (this.serialNumberInput) this.serialNumberInput.value = ''
            }
            if (this.cartonInput) this.cartonInput.value = ''
            this._focusOnCartonInput()
          } else {
            if (this.lotInput) this.lotInput.value = ''
            this._focusOnLotInput()
          }
        }
      },
      columnLeft: {
        size: 9,
        fields: [
          {
            type: 'row',
            content: [{ header: 'SKU No.', field: 'sku', type: 'string' }]
          },
          {
            type: 'row',
            content: [
              {
                header: 'Product Name',
                field: 'product.nameDisplay',
                type: 'string',
                style: '-webkit-line-clamp: 2;'
              }
            ]
          },
          {
            type: 'row',
            content: [
              { header: 'Packing Type', field: 'packingType', type: 'string' },
              { header: 'Packing Size', field: 'packingSize', type: 'string' }
            ]
          },
          {
            type: 'row',
            content: [
              { header: 'Mfg Date', field: 'displayManufactureDate', type: 'string' },
              { header: 'Batch No', field: 'batchId', type: 'string' }
            ]
          },
          {
            type: 'row',
            content: [{ header: 'Issue Note', field: 'issue', type: 'string' }]
          }
        ]
      },
      columnRight: {
        size: 3,
        fields: [
          {
            type: 'valueBox',
            content: { header: 'Lot  Qty.', field: 'actualPalletQty', field2: 'palletQty', type: 'string' }
          },
          {
            type: 'valueBox',
            content: { header: 'Pack Qty.', field: 'actualPackQty', field2: 'packQty', type: 'string' }
          },
          {
            type: 'actionBox',
            content: [
              {
                header: 'View',
                callback: (item, e) => {
                  if (!this._unloadedInventories?.length) return

                  const { sku, batchId, packingType, packingSize } = item
                  openPopup(
                    html`
                      <unloaded-inventories-popup
                        .unloadedInventories="${this._unloadedInventories.filter(
                          inventory =>
                            inventory.batchId == batchId &&
                            inventory?.product.sku == sku &&
                            inventory.packingType == packingType &&
                            inventory.packingSize == packingSize
                        )}"
                      ></unloaded-inventories-popup>
                    `,
                    {
                      backdrop: true,
                      size: 'large',
                      title: i18next.t('title.unloaded_inventories')
                    }
                  )
                },
                btnClass: 'btn-success',
                condition: (item) => {
                  return this._unloadedInventories?.length > 0
                }
              }
            ]
          }
        ]
      }
    }
  }

  render() {
    const isMobileDeviceCheck = isMobileDevice()

    return html`
      ${!isMobileDeviceCheck
        ? html`
            <form id="info-form" class="multi-column-form">
              <fieldset>
                <label>${i18next.t('label.order_type')}</label>
                <select name="orderType" @change="${this.orderTypeChangeHandler.bind(this)}">
                  ${Object.keys(AVAIL_ORDER_TYPES).map(key =>
                    this.refOrderType === AVAIL_ORDER_TYPES[key].value
                      ? html`
                          <option value="${AVAIL_ORDER_TYPES[key].value}" selected>
                            ${i18next.t(`label.${AVAIL_ORDER_TYPES[key].name}`)}
                          </option>
                        `
                      : html`
                          <option value="${AVAIL_ORDER_TYPES[key].value}">
                            ${i18next.t(`label.${AVAIL_ORDER_TYPES[key].name}`)}
                          </option>
                        `
                  )}
                </select>

                <label>${i18next.t('label.order_no')}</label>
                <barcode-scanable-input
                  name="orderNo"
                  custom-input
                  @keypress="${e => {
                    if (e.keyCode === 13) {
                      e.preventDefault()
                      if (this.orderNoInput.value) {
                        this._clearView()
                        this._scanning = false
                        this._fetchProducts(this.orderNoInput.value)
                        this.orderNoInput.value = ''
                      }
                    }
                  }}"
                ></barcode-scanable-input>
              </fieldset>
            </form>

            <form id="input-form" class="multi-column-form">
              ${this._renderFormFields(false)}
            </form>

            <data-grist
              .mode=${'GRID'}
              .config=${this.orderProductConfig}
              .data=${this.orderProductData}
            >
              <div slot="headroom" id="form-section">
                <form id="info-form" class="multi-column-form">
                    <fieldset>
                    <legend>
                      ${this._orderNo}
                      ${this._orderNo
                        ? html`(${this._bizplaceName})
                            <mwc-icon @click="${this._openWorksheetInformation.bind(this)}">info</mwc-icon>`
                        : ''}
                    </legend>
                    </fieldset>
                </form>
                </div>
            </data-grist>
          `
        : html`
            <form id="input-form" class="mobile-form">
              ${this._renderFormFields(true)}
          </form>

          ${this._orderNo ? 
            html`
              <button
                class="order-info text-center"
                ?disabled=${!Boolean(this._orderNo)}
                    @click=${this._openWorksheetInformation.bind(this)}
              >
                <label class="text-14 font-bold order-no"
                  >GAN No.: ${this._orderNo || ''}</label
                >
                <mwc-icon style="color:#007E7F;">info</mwc-icon>
              </button>
            ` : html``}

            <hr class="hr-line">

            <product-list-view
              id="unloading"
              style="--product-item-selected-bg: #F0FFFA;"
              .config=${this.unloadingMobileListConfig}
              .data=${this.orderProductData?.records || []}
              .selectedRecordId=${this._selectedOrderProduct?.id || null}
            ></product-list-view>
          `
      }
    `
  }

  constructor() {
    super()
    this._worksheetLoaded = false
    this._usingReusablePallet = false
    this._enableCartonLabel = false
    this._enableInputQty = 0
    this._enableProductScanning = false
    this._enableLotIdInput = false
    this.__minInboundShelfLife = 0
    this._isLooseItem = false
    this._isQtyChecked = false
    this._isRequiredCheckExpiry = false
    this._isRequiredCheckManufactureDate = false
    this._isRequireSerialNumberScanningInbound = false
    this._orderNo = ''
    this._scanning = false
    this.refOrderType = AVAIL_ORDER_TYPES.ARRIVAL_NOTICE.value
    this.orderProductData = { records: [] }
    this.orderVasData = { records: [] }
    this.reusablePalletIdData = { records: [] }
    this._conditionOfGoods = CONDITION_OF_GOODS.GOOD.value
  }

  updated(changedProps) {
    if (
      changedProps.has('_orderNo') ||
      changedProps.has('_selectedOrderProduct') ||
      changedProps.has('_unloadedInventories') ||
      changedProps.has('_usingReusablePallet')
    ) {
      this.updateContext()
    }
  }

  pageUpdated(changes, lifecycle, before) {
    if (this.active) {
      // Mobile: Check if we have params from landing page and auto-load
      if (isMobileDevice() && lifecycle?.params) {
        const orderType = lifecycle.params.orderType
        const orderNo = lifecycle.params.orderNo
        
        if (orderType && orderNo) {
          this.refOrderType = orderType
          this._clearView()
          this._scanning = false
          this._fetchProducts(orderNo)
          
          requestAnimationFrame(() => {
            const currentState = history.state || {}
            // Only create overlay if it doesn't exist yet
            if (!currentState.overlay) {
              const beforeOverlay = currentState.overlay
              const beforeSequence = beforeOverlay?.sequence || 0
              const afterSequence = beforeSequence + 1

              const newState = {
                ...currentState,
                overlay: {
                  name: 'unloading-page',
                  sequence: afterSequence,
                  escapable: true
                }
              }

              // Replace current state with overlay (preserves URL)
              history.replaceState(newState, '', window.location.href)
              
              // Trigger popstate to update toolbar
              window.dispatchEvent(
                new PopStateEvent('popstate', {
                  state: newState
                })
              )
            }
          })
        }
      } else if (isMobileDevice() && !this._worksheetInfo && !lifecycle?.params) {
        // Mobile: If no order loaded and no params, redirect to landing page
        navigate('unloading_landing')
      } else {
        this._focusOnOrderNoField()
      }
    }
  }

  async pageInitialized() {
    this._pageSettings = {
      ...this._pageSettings,
      cameraScanOnly: await fetchSettingRule('camera-scan-only'),
      enableLotIdInput: await fetchSettingRule('enable-lot-id-input')
    }

    this.orderProductConfig = {
      rows: {
        appendable: false,
        handlers: {
          click: (columns, data, column, record, rowIndex) => {
            if (record && record.batchId) {
              this._selectedOrderProduct = record
              this._isRequiredCheckExpiry = record.product?.isRequiredCheckExpiry
              this._isRequiredCheckManufactureDate = record.product?.isRequiredCheckManufactureDate
              this._isRequireSerialNumberScanningInbound = record.product?.isRequireSerialNumberScanningInbound
              this._isQtyChecked = false

              this.inputForm.reset()
              
              if (this._isRequiredCheckManufactureDate && record?.manufactureDate && this.refOrderType === AVAIL_ORDER_TYPES.ARRIVAL_NOTICE.value) {
                setTimeout(() => {
                  if (this.manufactureDateInput) {
                    this.manufactureDateInput.value = this._formatDateForInput(record.manufactureDate)
                  }
                }, 0)
              }

              if (this._isRequiredCheckManufactureDate && record?.inventory?.manufactureDate && this.refOrderType === AVAIL_ORDER_TYPES.RETURN_ORDER.value) {
                setTimeout(() => {
                  if (this.manufactureDateInput) {
                    this.manufactureDateInput.value = this._formatDateForInput(record.inventory.manufactureDate)
                  }
                }, 0)
              }
              
              if (this._enableCartonLabel) {
                if (this._enableProductScanning && this.productBarcodeInput) {
                  this.productBarcodeInput.readOnly = false
                  this.productBarcodeInput.value = ''
                }
                if (record.product?.isRequireSerialNumberScanningInbound) {
                  if (this.serialNumberInput) this.serialNumberInput.value = ''
                }
                if (this.cartonInput) this.cartonInput.value = ''
                this._focusOnCartonInput()
              } else {
                if (this.lotInput) this.lotInput.value = ''
                this._focusOnLotInput()
              }
            }
          }
        }
      },
      pagination: { infinite: true },
      list: {
        fields: [
          'validity',
          'sku',
          'product',
          'packingType',
          'packingSize',
          'batchId',
          'batchIdRef',
          'palletId',
          'palletQty',
          'actualPalletQty',
          'packQty',
          'actualPackQty',
          'displayManufactureDate',
          'issue'
        ]
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'reorder',
          handlers: {
            click: (columns, data, column, record, rowIndex) => {
              if (!this._unloadedInventories?.length) return

              const { sku, batchId, packingType, packingSize } = record
              openPopup(
                html`
                  <unloaded-inventories-popup
                    .unloadedInventories="${this._unloadedInventories.filter(
                      inventory =>
                        inventory.batchId == batchId &&
                        inventory?.product.sku == sku &&
                        inventory.packingType == packingType &&
                        inventory.packingSize == packingSize
                    )}"
                  ></unloaded-inventories-popup>
                `,
                {
                  backdrop: true,
                  size: 'large',
                  title: i18next.t('title.unloaded_inventories')
                }
              )
            }
          }
        },
        {
          type: 'boolean',
          name: 'validity',
          header: i18next.t('field.validity'),
          width: 40,
          imex: {
            type: 'boolean',
            key: 'validity',
            header: i18next.t('field.validity'),
            width: 10
          }
        },
        {
          type: 'string',
          name: 'batchId',
          label: true,
          header: i18next.t('field.batch_id'),
          width: 150,
          imex: {
            type: 'string',
            key: 'batchId',
            header: i18next.t('field.batch_id'),
            width: 35
          }
        },
        {
          type: 'string',
          name: 'batchIdRef',
          label: true,
          header: i18next.t('field.batch_no_ref'),
          width: 150,
          imex: {
            type: 'string',
            key: 'batchIdRef',
            header: i18next.t('field.batch_no_ref'),
            width: 35
          }
        },
        {
          type: 'string',
          name: 'sku',
          label: true,
          header: i18next.t('field.sku'),
          width: 120,
          imex: {
            type: 'string',
            key: 'sku',
            header: i18next.t('field.sku'),
            width: 30
          }
        },
        {
          type: 'object',
          name: 'product',
          label: true,
          header: i18next.t('field.product'),
          width: 200,
          imex: {
            type: 'string',
            key: 'product.name',
            header: i18next.t('field.product'),
            width: 60
          }
        },
        {
          type: 'string',
          name: 'packingType',
          label: true,
          header: i18next.t('field.packing_type'),
          width: 120,
          imex: {
            type: 'string',
            key: 'packingType',
            header: i18next.t('field.packing_type'),
            width: 30
          }
        },
        {
          type: 'number',
          name: 'packingSize',
          label: true,
          header: i18next.t('field.packing_size'),
          width: 120,
          imex: {
            type: 'number',
            key: 'packingSize',
            header: i18next.t('field.packing_size'),
            width: 30
          }
        },
        ...(this._pageSettings.enableLotIdInput
          ? [
              {
                type: 'string',
                name: 'palletId',
                label: true,
                header: i18next.t('field.lot_id'),
                width: 150,
                imex: {
                  type: 'string',
                  key: 'palletId',
                  width: 25
                }
              }
            ]
          : []),
        {
          type: 'integer',
          name: 'palletQty',
          label: true,
          header: i18next.t('field.lot_qty'),
          width: 90,
          imex: {
            type: 'integer',
            key: 'palletQty',
            header: i18next.t('field.lot_qty'),
            width: 20
          }
        },
        {
          type: 'integer',
          name: 'actualPalletQty',
          label: true,
          header: i18next.t('field.actual_lot_qty'),
          width: 100,
          imex: {
            type: 'integer',
            key: 'actualPalletQty',
            header: i18next.t('field.actual_lot_qty'),
            width: 25
          }
        },
        {
          type: 'float',
          name: 'packQty',
          label: true,
          header: i18next.t('field.total_pack_qty'),
          width: 90,
          imex: {
            type: 'float',
            key: 'packQty',
            header: i18next.t('field.total_pack_qty'),
            width: 20
          }
        },
        {
          type: 'float',
          name: 'actualPackQty',
          label: true,
          header: i18next.t('field.actual_pack_qty'),
          width: 100,
          imex: {
            type: 'float',
            key: 'actualPackQty',
            header: i18next.t('field.actual_pack_qty'),
            width: 25
          }
        },
        {
          type: 'string',
          name: 'displayManufactureDate',
          label: true,
          header: i18next.t('field.manufacture_date'),
          width: 100,
          imex: {
            type: 'string',
            key: 'displayManufactureDate',
            header: i18next.t('field.manufacture_date'),
            width: 25
          }
        },
        {
          type: 'string',
          name: 'issue',
          label: true,
          header: i18next.t('field.issue'),
          width: 100,
          imex: {
            type: 'string',
            key: 'issue',
            header: i18next.t('field.issue'),
            width: 25
          }
        }
      ]
    }
  }

  //===== FORMAT TIMESTAMP VALUE TO DATE STRING (YYYY-MM-DD) =====
  _formatDateForInput(date) {
    if (!date) return ''
    
    if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
      return date
    }
    
    if (!isNaN(Number(date))) {
      date = Number(date)
    }
    
    const dateObj = new Date(date)
    if (isNaN(dateObj.getTime())) return ''
    
    const year = dateObj.getFullYear()
    const month = String(dateObj.getMonth() + 1).padStart(2, '0')
    const day = String(dateObj.getDate()).padStart(2, '0')
    
    return `${year}-${month}-${day}`
  }

  //==== GET THE VALID MANUFACTURING DATE FROM THE MANUFACTURING DATE INPUT =====
  _getValidManufactureDate() {
    const value = this.manufactureDateInput?.value
    if (!value || value.trim() === '') return null
    
    const dateRegex = /^\d{4}-\d{2}-\d{2}$/
    if (!dateRegex.test(value)) return null
    
    const date = new Date(value)
    if (isNaN(date.getTime())) return null
    
    return value
  }

  _getManufactureDateValue() {
    //===== IF MANUFACTURING DATE CHECK SETTING IS SET =====
    if (this._isRequiredCheckManufactureDate) {
      return this._getValidManufactureDate()
    }
    
    //===== ELSE, GET THE MANUFACTURING DATE FROM THE SELECTED ORDER PRODUCT =====
    let manufactureDate = null
    
    if (this.refOrderType === AVAIL_ORDER_TYPES.ARRIVAL_NOTICE.value) {
      manufactureDate = this._selectedOrderProduct?.manufactureDate || null
    } else if (this.refOrderType === AVAIL_ORDER_TYPES.RETURN_ORDER.value) {
      manufactureDate = this._selectedOrderProduct?.inventory?.manufactureDate || null
    }
    
    if (!manufactureDate) return null
    
    const formattedDate = this._formatDateForInput(manufactureDate)
    return formattedDate || null
  }

  async _fetchProducts(orderNo, response) {
    if (this.refOrderType === AVAIL_ORDER_TYPES.ARRIVAL_NOTICE.value) {
      const response = await client.query({
        query: gql`
          query unloadingWorksheet($arrivalNoticeNo: String!) {
            unloadingWorksheet(arrivalNoticeNo: $arrivalNoticeNo) {
              worksheetInfo {
                arrivalNotice {
                  id
                  name
                }
                bizplaceName
                partnerDomainId
                customerCompanyDomainId
                containerNo
                refNo
                refNo2
                refNo3
                looseItem
                bufferLocation
                startedAt
                orderVas {
                  targetVas {
                    id
                    name
                    vas {
                      id
                      name
                      description
                      type
                    }
                    qty
                    remark
                  }
                }
              }
              worksheetDetailInfos {
                id
                batchId
                batchIdRef
                product {
                  id
                  sku
                  name
                  description
                  isRequiredCheckExpiry
                  isRequiredCheckManufactureDate
                  isRequireSerialNumberScanningInbound
                  isInventoryDecimal
                  minInboundShelfLife
                }
                name
                targetName
                description
                packingSize
                packingType
                palletQty
                palletId
                actualPalletQty
                packQty
                actualPackQty
                issue
                manufactureDate
                remark
              }
            }
          }
        `,
        variables: { arrivalNoticeNo: orderNo }
      })

      if (!response.errors) {
        const responseType = response.data?.unloadingWorksheet?.worksheetInfo
          ? response.data.unloadingWorksheet
          : response.data.unload
        this._worksheetLoaded = true
        const worksheetInfo = responseType.worksheetInfo
        const orderVasInfo = worksheetInfo.orderVas

        this._productBarcodeScanned = null
        this._orderNo = orderNo
        this._orderId = worksheetInfo.arrivalNotice.id
        this._bizplaceName = worksheetInfo.bizplaceName
        this._isLooseItem = worksheetInfo.looseItem
        this._worksheetInfo = worksheetInfo

        //vas --inbound
        this.orderVasData = orderVasInfo
          ? {
              records: orderVasInfo
                .filter(x => x.targetVas.vas.type == VAS_TYPE.MATERIALS.value)
                .map((orderVas, idx) => {
                  let newTargetVas = worksheetInfo.orderVas[idx].targetVas
                  return {
                    ...newTargetVas,
                    id: newTargetVas.id,
                    name: newTargetVas.name,
                    description: newTargetVas.description,
                    qty: newTargetVas.qty,
                    remark: newTargetVas.remark,
                    vas: newTargetVas.vas
                  }
                })
            }
          : { records: [] }

        const worksheetDetailInfo = orderBy(
          responseType.worksheetDetailInfos.map(worksheetDetailInfo => {
            return {
              ...worksheetDetailInfo,
              validity:
                worksheetDetailInfo.packQty > 0 //check new packQty after edit approved
                  ? worksheetDetailInfo.actualPackQty === worksheetDetailInfo.packQty &&
                    (worksheetDetailInfo.palletQty
                      ? worksheetDetailInfo.actualPalletQty === worksheetDetailInfo.palletQty
                      : true)
                  : worksheetDetailInfo.packQty === 0
                  ? true // if 0
                  : false, // if undefined or null
              sku: worksheetDetailInfo.product.sku,
              product: {
                ...worksheetDetailInfo.product,
                nameDisplay: worksheetDetailInfo.product.description
                  ? `${worksheetDetailInfo.product.name} (${worksheetDetailInfo.product.description})`
                  : worksheetDetailInfo.product.name
              }
            }
          }),
          ['validity'],
          ['asc']
        )

        this.orderProductData = {
          records: worksheetDetailInfo
        }

        if (this._selectedOrderProduct) {
          let quantityTally = worksheetDetailInfo.filter(
            itm => itm.id == this._selectedOrderProduct.id && itm.actualPackQty == itm.packQty
          )
          if (quantityTally?.length == 1) {
            // Only clear selected product if serial number scanning is NOT required
            // This ensures the Serial Number button remains visible when qty is matched
            if (!this._isRequireSerialNumberScanningInbound) {
              this._selectedOrderProduct = null
            }

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

              if (this.expDateInput?.value) {
                this.expDateInput.value = null
              }

              if (this._enableCartonLabel) {
                if (this.cartonInput) this.cartonInput.value = ''
                if (this.productBarcodeInput) this.productBarcodeInput.value = ''
                this._packingTypeScanned = ''
                this._focusOnCartonInput()
              } else {
                if (this.lotInput) this.lotInput.value = ''
                this._focusOnLotInput()
              }
            }
          }
        }

        const partnerDomainId = worksheetInfo.partnerDomainId
        this.customerCompanyDomainId = worksheetInfo.customerCompanyDomainId
        this._enableProductScanning = await fetchSettingRule('enable-product-scanning', partnerDomainId)
        this._enableCartonLabel = await fetchSettingRule('enable-carton-label', partnerDomainId)
        if (this._enableProductScanning)
          this._enableInputQty = parseInt(await fetchSettingRule('enable-input-qty', partnerDomainId))

        this._fetchInventories()
      }
    } else {
      const response = await client.query({
        query: gql`
          query unloadingReturnWorksheet($returnOrderNo: String!) {
            unloadingReturnWorksheet(returnOrderNo: $returnOrderNo) {
              worksheetInfo {
                bizplaceName
                partnerDomainId
                customerCompanyDomainId
                containerNo
                refNo
                bufferLocation
                startedAt
              }
              worksheetDetailInfos {
                id
                batchId
                palletId
                product {
                  id
                  name
                  sku
                  description
                  sku
                  isRequiredCheckExpiry
                  isRequiredCheckManufactureDate
                  isRequireSerialNumberScanningInbound
                  isInventoryDecimal
                  minInboundShelfLife
                }
                inventory {
                  id
                  manufactureDate
                }
                name
                targetName
                description
                packingType
                packingSize
                palletQty
                actualPalletQty
                packQty
                actualPackQty
                issue
                remark
              }
            }
          }
        `,
        variables: { returnOrderNo: orderNo }
      })

      if (!response.errors) {
        this._orderNo = orderNo
        const worksheetInfo = response.data.unloadingReturnWorksheet.worksheetInfo
        this._worksheetInfo = worksheetInfo
        this._bizplaceName = worksheetInfo?.bizplaceName
        this._productBarcodeScanned = null

        this.orderProductData = {
          records: orderBy(
            response.data.unloadingReturnWorksheet.worksheetDetailInfos.map(worksheetDetailInfo => {
              return {
                ...worksheetDetailInfo,
                validity: worksheetDetailInfo.actualPackQty === worksheetDetailInfo.packQty,
                sku: worksheetDetailInfo.product.sku,
                product: {
                  ...worksheetDetailInfo.product,
                  nameDisplay: worksheetDetailInfo.product.description
                    ? `${worksheetDetailInfo.product.name} (${worksheetDetailInfo.product.description})`
                    : worksheetDetailInfo.product.name
                }
              }
            }),
            ['validity'],
            ['asc']
          )
        }

        const partnerDomainId = worksheetInfo.partnerDomainId
        this.customerCompanyDomainId = worksheetInfo.customerCompanyDomainId
        this._enableProductScanning = await fetchSettingRule('enable-product-scanning', partnerDomainId)
        this._enableCartonLabel = await fetchSettingRule('enable-carton-label', partnerDomainId)
        if (this._enableProductScanning)
          this._enableInputQty = await fetchSettingRule('enable-input-qty', partnerDomainId)

        this._fetchInventories()
      }
    }
  }

  async _fetchInventories() {
    const response = await client.query({
      query: gql`
        query unloadedInventories($worksheetDetailNames: [String]!) {
          unloadedInventories(worksheetDetailNames: $worksheetDetailNames) {
            id
            batchId
            packingType
            bizplace {
              id
              name
              description
            }
            packingSize
            batchIdRef
            palletId
            cartonId
            qty
            expirationDate
            manufactureDate
            status
            product {
              id
              name
              sku
              isRequiredCheckExpiry
              isRequiredCheckManufactureDate
              isRequireSerialNumberScanningInbound
            }
            reusablePallet {
              id
              name
            }
            inventoryItems {
              serialNumber
            }
            conditionOfGoods
          }
        }
      `,
      variables: { worksheetDetailNames: this.orderProductData.records.map(record => record.name) }
    })

    if (!response.errors) {
      this._unloadedInventories = response.data.unloadedInventories

      this.orderProductData = {
        records: this.orderProductData.records.map(orderProduct => {
          if (orderProduct.batchId === this._unloadedInventories.batchId) {
            orderProduct.actualPalletQty = this._unloadedInventories.length
            orderProduct.actualTotalPackQty = this._unloadedInventories
              .map(inventory => inventory.qty)
              .reduce((a, b) => a + b, 0)
          }

          const matchingInventories = this._unloadedInventories.filter(
            inventory =>
              inventory.batchId == orderProduct.batchId &&
              inventory?.product.sku == orderProduct.sku &&
              inventory.packingType == orderProduct.packingType &&
              inventory.packingSize == orderProduct.packingSize
          )

          if (matchingInventories.length > 0) { // FOR ARRIVAL NOTICE AND RETURN ORDER WHERE ORDER PRODUCT IS UNLOADED
            const manufactureDates = [...new Set(
              matchingInventories
                .map(inv => this._formatDateForInput(inv.manufactureDate))
                .filter(date => date)
            )].sort()

            orderProduct.displayManufactureDate = manufactureDates.length > 0 ? manufactureDates.join(', ') : (orderProduct.manufactureDate ? this._formatDateForInput(orderProduct.manufactureDate) : null)
          } else if (orderProduct.manufactureDate) { // FOR ARRIVAL NOTICE
            orderProduct.displayManufactureDate = this._formatDateForInput(orderProduct.manufactureDate)
          } else if (orderProduct?.inventory?.manufactureDate) { // FOR RETURN ORDER
            orderProduct.displayManufactureDate = this._formatDateForInput(orderProduct.inventory.manufactureDate)
          }

          return orderProduct
        })
      }

      this._resetInput()
      this.updateContext()
    }
  }

  conditionOfGoodsChangeHandler(e) {
    this._conditionOfGoods = e.target.value
  }

  _resetInput() {
    if (this._selectedOrderProduct?.isRequiredCheckExpiry && !this._isRequireSerialNumberScanningInbound)
      if (this.expDateInput) this.expDateInput.value = null

    if (this._isRequiredCheckExpiry && !this._isRequireSerialNumberScanningInbound) {
      if (this.expDateInput) this.expDateInput.value = null
      this._focusOnExpiryDateInput()
    }

    if (this._selectedOrderProduct?.isRequiredCheckManufactureDate && !this._isRequireSerialNumberScanningInbound)
      if (this.manufactureDateInput) this.manufactureDateInput.value = null

    if (this._isRequiredCheckManufactureDate && !this._isRequireSerialNumberScanningInbound) {
      if (this.manufactureDateInput) this.manufactureDateInput.value = null
      this._focusOnManufactureDateInput()
    }

    if (this._enableCartonLabel) {
      if (this._isRequireSerialNumberScanningInbound) {
        if (!this.cartonInput?.value) {
          this._focusOnCartonInput()
        } else {
          if (this.serialNumberInput) this.serialNumberInput.value = ''
          this._focusOnSerialNumberInput()
        }
      } else {
        if (this._enableInputQty >= 0 && this._enableInputQty < 4) {
          if (this.actualQtyInput) this.actualQtyInput.value = ''
        }
        if (this.cartonInput) this.cartonInput.value = ''
        this._focusOnCartonInput()
      }
    } else {
      if (this.lotInput?.value !== '') {
        if (this._isRequireSerialNumberScanningInbound) {
          if (this.serialNumberInput) this.serialNumberInput.value = ''
          this._focusOnSerialNumberInput()
        } else {
          if (this.lotInput) this.lotInput.value = ''
          if (this._enableInputQty >= 0 && this._enableInputQty < 4 && this.actualQtyInput) this.actualQtyInput.value = ''
          this._focusOnLotInput()
        }
      }
    }

    if (this._enableProductScanning) {
      if (!this._isRequireSerialNumberScanningInbound) {
        if (this.productBarcodeInput) this.productBarcodeInput.value = ''
        if (this.cartonInput?.value) {
          this._focusOnProductBarcodeInput()
        }
      }
    }
  }

  _clearView() {
    this.orderProductData = { records: [] }
    this.inputForm.reset()
    this._selectedOrderProduct = null
    this._selectedInventory = null
    this._unloadedInventories = null
    this.updateContext()
  }

  async _transactionHandler(e) {
    if (e.keyCode !== 13) return
    e.preventDefault()
    try {
      if (this._enableProductScanning) {
        this._productBarcode = this.productBarcodeInput?.value
      }
      if (!this._enableCartonLabel) return this._unloadSelected()
      if (this._pageSettings.enableLotIdInput) {
        this._palletId = this.palletId.value
      }
      let orderProducts = await this._validateUnloading()
      if (!orderProducts || !orderProducts.errors) {
        this.orderProductData = { records: [] }
        if (orderProducts?.length > 1) {
          this.selectOrderProduct(orderProducts)
        } else {
          if (this._isRequireSerialNumberScanningInbound) {
            return this._scanSerialNumberUnload()
          } else {
            if (!(this._enableProductScanning && (this._enableInputQty < 0 || this._enableInputQty > 3)))
              return await this._qtyUnload()
          }
          this._scanProductUnload()
        }
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _scanProductUnload() {
    if (this._scanning) return this._showToast({ message: i18next.t('text.previous_scan_is_executing') })
    this._scanning = true

    if (this._isRequiredCheckExpiry && this.__minInboundShelfLife > 0) {
      const answer = await this._checkExpiry()
      if (answer && !answer.value) return (this._scanning = false)
    }

    try {
      await client.mutate({
        mutation: gql`
          mutation scanProductUnload(
            $worksheetDetailName: String!
            $productBarcode: String!
            $inventory: InventoryPatch!
          ) {
            scanProductUnload(
              worksheetDetailName: $worksheetDetailName
              productBarcode: $productBarcode
              inventory: $inventory
            )
          }
        `,
        variables: {
          worksheetDetailName: this._selectedOrderProduct.name,
          productBarcode: this._productBarcodeScanned,
          inventory: {
            palletId: this._enableCartonLabel ? null : this.lotInput?.value?.trim(),
            cartonId: this._enableCartonLabel ? this.cartonInput?.value?.trim() : null,
            expirationDate: this._isRequiredCheckExpiry ? this.expDateInput?.value : null,
            manufactureDate: this._getManufactureDateValue(),
            conditionOfGoods: this._conditionOfGoods || CONDITION_OF_GOODS.GOOD.value
          }
        }
      })

    } catch (e) {
      // for cartonId + product scanning
      // show custom toast for incorrect product barcodes
      // full errors for everything else
      if (!(e instanceof TypeError)) this._showToast(e)
      else this._showToast({ message: i18next.t('text.incorrect_product_barcode') })
    } finally {
      await this._fetchProducts(this._orderNo)
      this._scanning = false
    }
  }

  async _scanSerialNumberUnload() {
    if (this._scanning) {
      return this._showToast({ message: i18next.t('text.previous_scan_is_executing') })
    }
    this._scanning = true

    if (this.serialNumberInput && !this.serialNumberInput.value) {
      this._scanning = false
      this._focusOnSerialNumberInput()
      return this._showToast({ message: i18next.t('text.enter_serial_number') })
    }

    if (this._isRequiredCheckExpiry && this.__minInboundShelfLife > 0) {
      const answer = await this._checkExpiry()
      if (answer && !answer.value) return (this._scanning = false)
    }

    try {
      await client.mutate({
        mutation: gql`
          mutation scanSerialNumberUnload(
            $worksheetDetailName: String!
            $serialNumber: String!
            $inventory: InventoryPatch!
          ) {
            scanSerialNumberUnload(
              worksheetDetailName: $worksheetDetailName
              serialNumber: $serialNumber
              inventory: $inventory
            )
          }
        `,
        variables: {
          worksheetDetailName: this._selectedOrderProduct.name,
          serialNumber: this.serialNumberInput?.value?.trim(),
          inventory: {
            palletId: this._enableCartonLabel ? null : this.lotInput?.value?.trim(),
            cartonId: this._enableCartonLabel ? this.cartonInput?.value?.trim() : null,
            expirationDate: this._isRequiredCheckExpiry ? this.expDateInput?.value : null,
            manufactureDate: this._getManufactureDateValue(),
            conditionOfGoods: this._conditionOfGoods || CONDITION_OF_GOODS.GOOD.value
          }
        }
      })
    } catch (e) {
      // for cartonId + product scanning
      // show custom toast for incorrect product barcodes
      // full errors for everything else
      if (!(e instanceof TypeError)) this._showToast(e)
      else this._showToast({ message: i18next.t('text.incorrect_serial_number') })
    } finally {
      await this._fetchProducts(this._orderNo)
      this._scanning = false
    }
  }

  async _checkExpiryFormat() {
    const inputExpDate = this.expDateInput?.value
    let intValueYear = parseInt(inputExpDate)

    if (intValueYear > 9999) {
      this._scanning = false
      throw new Error(i18next.t('text.please_enter_the_correct_date_format'))
    }
  }

  async _checkExpiry() {
    let date = new Date()
    const acceptBefore = new Date(date.setDate(date.getDate() + this.__minInboundShelfLife))
    let expiryDate = new Date(this.expDateInput?.value)
    if (expiryDate < acceptBefore) {
      return await CustomAlert({
        title: i18next.t('title.item_unloading_is_expiring_soon'),
        confirmButton: { text: i18next.t('button.confirm') },
        cancelButton: { text: i18next.t('button.cancel') }
      })
    }
  }

  async _checkLotInput() {
    if (this._selectedOrderProduct.palletId)
      if (this.lotInput?.value !== this._selectedOrderProduct.palletId)
        throw new Error(i18next.t('text.incorrect_lot_id'))
  }

  async _qtyUnload() {
    if (this._scanning) return this._showToast({ message: i18next.t('text.previous_scan_is_executing') })
    this._scanning = true

    if (this._isRequiredCheckExpiry && this.__minInboundShelfLife > 0) {
      const answer = await this._checkExpiry()
      if (answer && !answer.value) return (this._scanning = false)
    }

    try {
      // Add validation for qty
      const qty = parseFloat(this.actualQtyInput?.value)
      if (isNaN(qty)) {
        throw new Error(i18next.t('text.invalid_quantity'))
      }

      await client.mutate({
        mutation: gql`
          mutation unload(
            $worksheetDetailName: String!
            $inventory: InventoryPatch!
            $productBarcode: String
            $orderType: String
          ) {
            unload(
              worksheetDetailName: $worksheetDetailName
              inventory: $inventory
              productBarcode: $productBarcode
              orderType: $orderType
            ) {
              worksheetInfo {
                arrivalNotice {
                  id
                  name
                }
              }
            }
          }
        `,
        variables: {
          orderType: this.refOrderType === AVAIL_ORDER_TYPES.ARRIVAL_NOTICE.value ? 'arrivalNotice' : 'returnOrder',
          worksheetDetailName: this._selectedOrderProduct?.name || '',
          inventory: {
            palletId: this._enableCartonLabel ? null : this.lotInput?.value?.trim(),
            cartonId: this._enableCartonLabel ? this.cartonInput?.value?.trim() : null,
            qty: qty, // Use the validated qty
            expirationDate: this._isRequiredCheckExpiry ? this.expDateInput?.value : null,
            manufactureDate: this._getManufactureDateValue(),
            reusablePallet: this._usingReusablePallet ? this._reusablePallet : null,
            conditionOfGoods: this._conditionOfGoods || CONDITION_OF_GOODS.GOOD.value
          },
          productBarcode: this._enableProductScanning
            ? this._productBarcodeScanned
              ? this._productBarcodeScanned
              : this._productBarcode
            : null
        }
      })
    } catch (e) {
      this._showToast(e)
    } finally {
      await this._fetchProducts(this._orderNo)
      this._scanning = false
    }
  }

  async fetchProductDetail(gtin, productId) {
    return await client.query({
      query: gql`
        query productDetail($gtin: String!, $productId: String!, $customerCompanyDomainId: String!) {
          productDetail(gtin: $gtin, productId: $productId, customerCompanyDomainId: $customerCompanyDomainId) {
            id
            name
            packingType
            packingSize
            gtin
            product {
              id
              name
              sku
              isRequiredCheckExpiry
              isRequiredCheckManufactureDate
              isRequireSerialNumberScanningInbound
              isInventoryDecimal
              minInboundShelfLife
            }
          }
        }
      `,
      variables: {
        gtin,
        productId,
        customerCompanyDomainId: this.customerCompanyDomainId
      }
    })
  }

  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
                isRequiredCheckManufactureDate
                isRequireSerialNumberScanningInbound
                isInventoryDecimal
                minInboundShelfLife
              }
            }
            total
          }
        }
      `,
      variables: {
        gtin,
        customerCompanyDomainId: this.customerCompanyDomainId
      }
    })
  }

  //need to enhance
  async _checkedLotScannedProduct() {
    const response = await this.fetchProductDetail('', this._selectedOrderProduct.product.id)
    if (!response.errors) {
      this.__minInboundShelfLife = response.data.productDetail.product.minInboundShelfLife
    }
  }

  async _checkedScannedProduct() {
    //fetch any product details that has entered gtin
    const response = await this.fetchProductDetails(
      this._productBarcodeScanned ? this._productBarcodeScanned : this._productBarcode.trim() || ''
    )

    if (!response.errors) {
      let orderProductData = this.orderProductData.records
      let checkedProductDetails = response.data.checkGtin.items
      if (this.productBarcodeInput) this.productBarcodeInput.value = `${checkedProductDetails[0].gtin}`
      this._packingTypeScanned = checkedProductDetails ? `${checkedProductDetails[0].packingType}` : ''
      this._productBarcodeScanned = checkedProductDetails[0].gtin
      this._previousSelectedOrderProduct = this._selectedOrderProduct

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

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

      if (!foundOrderProducts?.length && !foundParentProducts?.length) {
        if (this._enableInputQty >= 0 && this._enableInputQty < 4 && !this._isRequireSerialNumberScanningInbound) {
          if (this.actualQtyInput) this.actualQtyInput.value = ''
        }

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

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

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

      if (this._selectedOrderProduct && this._selectedOrderProduct.product.sku !== product.sku)
        this._selectedOrderProduct = null

      if (foundOrderProducts?.length > 1) {
        if (foundOrderProducts.some(op => op.product.isRequiredCheckExpiry)) this._isRequiredCheckExpiry = true
        if (foundOrderProducts.some(op => op.product.isRequiredCheckManufactureDate)) this._isRequiredCheckManufactureDate = true

        if (this._isRequireSerialNumberScanningInbound) {
          if (!this._selectedOrderProduct) {
            return foundOrderProducts
          }
        } else {
          return foundOrderProducts
        }
      } else if (foundOrderProducts?.length == 1) {
        this.__minInboundShelfLife = product.minInboundShelfLife
        this._isRequiredCheckExpiry = product.isRequiredCheckExpiry
        this._isRequiredCheckManufactureDate = product.isRequiredCheckManufactureDate
        this._isRequireSerialNumberScanningInbound = product.isRequireSerialNumberScanningInbound
        this._selectedOrderProduct = foundOrderProducts[0]
      } else if (foundOrderProducts?.length == 0 && foundParentProducts?.length > 0) {
        this.__minInboundShelfLife = product.minInboundShelfLife
        this._isRequiredCheckExpiry = product.isRequiredCheckExpiry
        this._isRequiredCheckManufactureDate = product.isRequiredCheckManufactureDate
        this._isRequireSerialNumberScanningInbound = product.isRequireSerialNumberScanningInbound
        this._selectedOrderProduct = foundParentProducts[0]
      }
    } else {
      return response
    }
  }

  selectOrderProduct(orderProducts) {
    openPopup(
      html`
        <select-product-batch
          .orderProducts="${orderProducts}"
          @selected-batch="${async e => {
            this._selectedOrderProduct = e.detail
            this.__minInboundShelfLife = this._selectedOrderProduct.product.minInboundShelfLife
            this._isRequiredCheckExpiry = this._selectedOrderProduct.product.isRequiredCheckExpiry
            this._isRequiredCheckManufactureDate = this._selectedOrderProduct.product.isRequiredCheckManufactureDate
            this._isRequireSerialNumberScanningInbound =
              this._selectedOrderProduct.product.isRequireSerialNumberScanningInbound
            if (e.detail.product.isRequireSerialNumberScanningInbound) {
              this._isRequireSerialNumberScanningInbound = true
              if (!this.serialNumberInput?.value) {
                this._focusOnSerialNumberInput()
                this._showToast(Error(i18next.t('text.serial_number_is_empty')))
              }
              await this._scanSerialNumberUnload()
            } else {
              if (this._enableProductScanning && (this._enableInputQty < 0 || this._enableInputQty > 3)) {
                await this._scanProductUnload()
              } else {
                await this._qtyUnload()
              }
            }
          }}"
        ></select-product-batch>
      `,
      {
        backdrop: true,
        size: 'medium',
        title: i18next.t('title.select_product_batch')
      }
    )
  }

  async _validateUnloading() {
    if (!this._enableCartonLabel) {
      if (!this.lotInput?.value) {
        this._focusOnLotInput()
        throw new Error(i18next.t('text.lot_id_is_empty'))
      }
    } else {
      if (!this.cartonInput?.value) {
        this._focusOnCartonInput()
        throw new Error(i18next.t('text.carton_id_is_empty'))
      }
    }
    
    if (!this._enableProductScanning) {
      if (!this._selectedOrderProduct) throw new Error(i18next.t('text.target_does_not_selected'))

      if (this._isRequireSerialNumberScanningInbound) {
        if (!this.serialNumberInput?.value) {
          this._focusOnSerialNumberInput()
          throw new Error(i18next.t('text.serial_number_is_empty'))
        }
      } else {
        // 4. qty value existing
        if (!parseFloat(this.actualQtyInput?.value)) {
          this._focusOnActualQtyInput()
          throw new Error(i18next.t('text.qty_is_empty'))
        }

        // 5. qty value has to be positive
        if (!(parseFloat(this.actualQtyInput?.value) > 0)) {
          setTimeout(() => this.actualQtyInput?.select(), 100)
          throw new Error(i18next.t('text.qty_should_be_positive'))
        }

        // Check if quantity has decimals and validate against product settings
        const qty = parseFloat(this.actualQtyInput?.value)
        if (qty % 1 !== 0 && !this._selectedOrderProduct.product.isInventoryDecimal) {
          throw new Error(i18next.t('text.decimal_quantities_not_allowed'))
        }
      }

      if (this._isRequiredCheckExpiry) {
        if (!this.expDateInput?.value) {
          this._focusOnExpiryDateInput()
          throw new Error(i18next.t('text.expiry_date_is_empty'))
        }

        await this._checkExpiryFormat()
      }

      if (this._isRequiredCheckManufactureDate && this.manufactureDateInput?.value) {
        const value = this.manufactureDateInput?.value
        const today = new Date()
        today.setHours(0, 0, 0, 0)
        const manufactureDate = new Date(value)
        manufactureDate.setHours(0, 0, 0, 0)

        if (manufactureDate > today) {
          this._focusOnManufactureDateInput()
          throw new Error(i18next.t('text.manufacture-date-cannot-be-later-than-today'))
        }
      }

      if (this._pageSettings.enableLotIdInput) {
        await this._checkLotInput()
      }
    }

    if (this._enableProductScanning) {
      if (!this.productBarcodeInput?.value) {
        this._focusOnProductBarcodeInput()
        throw new Error(i18next.t('text.scan_product_barcode'))
      }

      let orderProducts = await this._checkedScannedProduct()

      if (this._enableInputQty >= 0 && this._enableInputQty < 4 && !this._isRequireSerialNumberScanningInbound) {
        if (!parseFloat(this.actualQtyInput?.value)) {
          this._focusOnActualQtyInput()
          throw new Error(i18next.t('text.enter_qty'))
        }

        if (!(parseFloat(this.actualQtyInput?.value) > 0)) {
          setTimeout(() => this.actualQtyInput?.select(), 100)
          throw new Error(i18next.t('text.qty_should_be_positive'))
        }

        // Check if quantity has decimals and validate against product settings
        const qty = parseFloat(this.actualQtyInput?.value)
        if (qty % 1 !== 0 && !this._selectedOrderProduct.product.isInventoryDecimal) {
          throw new Error(i18next.t('text.decimal_quantities_not_allowed'))
        }
      } else if (this._isRequireSerialNumberScanningInbound) {
        if (!this.serialNumberInput?.value) {
          this._focusOnSerialNumberInput()
          throw new Error(i18next.t('text.enter_serial_number'))
        }
      }

      if (this._isRequiredCheckExpiry) {
        if (!this.expDateInput?.value) {
          this._focusOnExpiryDateInput()
          throw new Error(i18next.t('text.expiry_date_is_empty'))
        }

        await this._checkExpiryFormat()
      }

      if (this._isRequiredCheckManufactureDate && this.manufactureDateInput?.value) {
        const value = this.manufactureDateInput?.value
        const today = new Date()
        today.setHours(0, 0, 0, 0)
        const manufactureDate = new Date(value)
        manufactureDate.setHours(0, 0, 0, 0)

        if (manufactureDate > today) {
          this._focusOnManufactureDateInput()
          throw new Error(i18next.t('text.manufacture-date-cannot-be-later-than-today'))
        }
      }

      return orderProducts
    }
  }

  async _openIssueNote() {
    await sleep(1010)
    openPopup(
      html`
        <popup-note
          .title="${i18next.t('title.issue')}"
          .value="${this._selectedOrderProduct.issue}"
          @submit="${async e => {
            await this.updateOrderProductIssue(this._selectedOrderProduct, e.detail.value)
          }}"
        ></popup-note>
      `,
      {
        backdrop: true,
        size: 'medium',
        title: i18next.t('title.unloading_issue')
      }
    )
  }

  async updateOrderProductIssue(orderProduct, issue) {
    try {
      if (!issue) return
      const response = await client.mutate({
        mutation: gql`
          mutation updateWorksheetDetail($id: String!, $patch: WorksheetDetailPatch!) {
            updateWorksheetDetail(id: $id, patch: $patch) {
              id
              name
            }
          }
        `,
        variables: { id: orderProduct.id, patch: { issue } }
      })

      if (!response.errors) {
        this._showToast({ message: i18next.t('text.issue_updated') })
        this._fetchProducts(this._orderNo)
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _completePartially() {
    try {
      this._validateCompletePartially()

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

      if (!result.value) return

      let response
      if (this.refOrderType === AVAIL_ORDER_TYPES.ARRIVAL_NOTICE.value) {
        response = await client.mutate({
          mutation: gql`
            mutation completeUnloadingPartially($arrivalNoticeNo: String!, $worksheetDetail: [WorksheetDetailPatch!]) {
              completeUnloadingPartially(arrivalNoticeNo: $arrivalNoticeNo, worksheetDetail: $worksheetDetail)
            }
          `,
          variables: {
            arrivalNoticeNo: this._orderNo,
            worksheetDetail: this._getWorksheetDetails('partial').filter(wsd => wsd.actualPackQty > 0)
          }
        })
      } else {
        response = await client.mutate({
          mutation: gql`
            mutation completeUnloadReturnPartially($returnOrderNo: String!, $worksheetDetail: [WorksheetDetailPatch!]) {
              completeUnloadReturnPartially(returnOrderNo: $returnOrderNo, worksheetDetail: $worksheetDetail)
            }
          `,
          variables: {
            returnOrderNo: this._orderNo,
            worksheetDetail: this._getWorksheetDetails('partial').filter(wsd => wsd.actualPackQty > 0)
          }
        })
      }

      if (!response.errors) {
        if (this._enableProductScanning && this.cartonInput?.value) this.cartonInput.value = ''
        await this._fetchProducts(this._orderNo)
        
            try {
              const generatePartialPutawayWorksheetResponse = await client.mutate({
                      mutation: gql`
                        mutation generatePartialPutawayWorksheet( $arrivalNoticeId: String!) {
                          generatePartialPutawayWorksheet(arrivalNoticeId: $arrivalNoticeId)
                        }
                      `,
                      variables: { arrivalNoticeId: this._orderId }
                    })
              
            } catch (error) {
              this._showToast(error)
            }
          
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  _validateCompletePartially() {
    if (!this._unloadedInventories?.length) {
      throw new Error(i18next.t('text.nothing_unloaded'))
    }
  }

  async _completeQC() {
    try {
      this._validateSelectedInventory()
      const response = await client.mutate({
        mutation: gql`
          mutation completeProductScanUnload($worksheetDetailName: String!, $inventory: InventoryPatch!) {
            completeProductScanUnload(worksheetDetailName: $worksheetDetailName, inventory: $inventory)
          }
        `,
        variables: {
          worksheetDetailName: this._selectedOrderProduct?.name || '',
          inventory: {
            palletId: this._enableCartonLabel ? null : this.lotInput?.value?.trim(),
            cartonId: this._enableCartonLabel ? this.cartonInput?.value?.trim() : null,
            expirationDate: this.expDateInput?.value ? this.expDateInput.value : null,
            manufactureDate: this.manufactureDateInput?.value ? this.manufactureDateInput.value : null
          }
        }
      })

      if (!response.errors) {
        this._selectedInventory = null
        if (this.cartonInput) this.cartonInput.value = ''

        await this._fetchProducts(this._orderNo)
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _complete() {
    try {
      this._validateComplete()

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

      if (!result.value) return

      if (this.refOrderType === AVAIL_ORDER_TYPES.ARRIVAL_NOTICE.value) {
        const response = await client.mutate({
          mutation: gql`
            mutation completeUnloading($arrivalNoticeNo: String!, $worksheetDetails: [WorksheetDetailPatch]) {
              completeUnloading(arrivalNoticeNo: $arrivalNoticeNo, worksheetDetails: $worksheetDetails)
            }
          `,
          variables: { arrivalNoticeNo: this._orderNo, worksheetDetails: this._getWorksheetDetails() }
        })

        if (!response.errors) {
          await CustomAlert({
            title: i18next.t('title.completed'),
            text: i18next.t('text.unloading_completed'),
            confirmButton: { text: i18next.t('button.confirm') }
          })

          this._orderNo = null
          this._clearView()
        }
      } else {
        const response = await client.mutate({
          mutation: gql`
            mutation completeUnloadReturning($returnOrderNo: String!, $worksheetDetails: [WorksheetDetailPatch]) {
              completeUnloadReturning(returnOrderNo: $returnOrderNo, worksheetDetails: $worksheetDetails)
            }
          `,
          variables: { returnOrderNo: this._orderNo, worksheetDetails: this._getWorksheetDetails() }
        })

        if (!response.errors) {
          await CustomAlert({
            title: i18next.t('title.completed'),
            text: i18next.t('text.unloading_completed'),
            confirmButton: { text: i18next.t('button.confirm') }
          })

          this._orderNo = null
          this._clearView()
        }
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _unloadSelected() {
    try {
      if (!this._selectedOrderProduct) throw new Error(i18next.t('text.target_is_not_selected'))
      await this._validateUnloading()
      await this._checkedLotScannedProduct()

      if (this._isRequireSerialNumberScanningInbound) return this._scanSerialNumberUnload()
      await this._qtyUnload()
    } catch (e) {
      this._showToast(e)
    }
  }

  async _openUndoUnloadPopup() {
    await sleep(1010)
    openPopup(
      html`
        <undo-unload-popup
          .enableCartonLabel="${this._enableCartonLabel}"
          .enableProductScanning="${this._enableProductScanning}"
          .orderType="${this.refOrderType}"
          .orderNo="${this._orderNo}"
          .unloadedInventories="${this._unloadedInventories}"
          @undo-inventory="${async e => {
            history.back()
            await this._fetchProducts(this._orderNo)
            this._showToast({ message: i18next.t('text.inventory_undo_successfully') })
          }}"
        ></undo-unload-popup>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.undo_unload')
      }
    )
  }

  async _openConditionPopup() {
    if (!this.orderProductData || !this.orderProductData.records || this.orderProductData.records.length === 0) {
      this._showToast({ message: i18next.t('text.no_products_found'), type: 'warning' })
      return
    }

    const worksheetDetailNames = this.orderProductData.records.map(record => record.name)
    
    await sleep(1010)
    openPopup(
      html`
        <condition-of-goods-popup
          .unloadedInventories="${this._unloadedInventories}"
          .worksheetDetailNames="${worksheetDetailNames}"
          @condition-updated="${async e => {
            this._showToast({ message: i18next.t('text.info_update_successfully') })
            await this._fetchProducts(this._orderNo)
          }}"
        ></condition-of-goods-popup>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.condition_of_goods')
      }
    )
  }

  _openWorksheetInformation() {
    openPopup(
      html`
        <unloading-worksheet-information-popup 
          .worksheetInformation="${this._worksheetInfo}"
          .unloadedInventories="${this._unloadedInventories || []}"
          .orderProductData="${this.orderProductData?.records || []}"
        ></unloading-worksheet-information-popup>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.order_information')
      }
    )
  }

  async _reusablePalletMode() {
    await sleep(1010)
    openPopup(
      html`
        <popup-reusable-pallet
          .label="${i18next.t('label.reusable_pallet_id')}"
          @submit=${async e => {
            this._usingReusablePallet = true
            this._reusablePallet = e.detail.reusablePallet
            this._worksheetInfo.plasticPallet = this._reusablePallet.name
            this.updateContext()
          }}
          @normalPallet=${async () => this._normalPalletMode()}
        ></popup-reusable-pallet>
      `,
      {
        backdrop: true,
        escapable: false,
        size: 'small',
        title: i18next.t('title.unloading_with_reusable_pallet')
      }
    )
  }

  _unflattenData(flattenedData) {
    if (!flattenedData || typeof flattenedData !== 'object') return flattenedData

    const unflattened = {}
    const nested = {}

    Object.keys(flattenedData).forEach(key => {
      if (key.includes('.')) {
        const [rootKey, ...rest] = key.split('.')
        const restKeys = rest.join('.')

        if (!nested[rootKey]) nested[rootKey] = {}
        nested[rootKey][restKeys] = flattenedData[key]
      } else {
        unflattened[key] = flattenedData[key]
      }
    })

    Object.keys(nested).forEach(key => {
      unflattened[key] = this._unflattenData(nested[key])
    })

    return unflattened
  }

  async _openSerialNumberList() {
    await sleep(1010)
    openPopup(
      html`
        <serial-number-list-popup
          .title="${i18next.t('title.serial_number_list')}"
          .orderType="${i18next.t('text.inbound')}"
          .orderId="${this._orderId}"
          .data="${this._selectedOrderProduct}"
          @close-serial-number-list="${async e => {
            await this._fetchProducts(this._orderNo)
          }}"
        ></serial-number-list-popup>
      `,
      {
        closable: false,
        backdrop: false,
        escapable: false,
        size: 'large',
        title: i18next.t('title.serial_number_list')
      }
    )
  }

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

    if (!result.value) return
    this._usingReusablePallet = false
    this._reusablePalletName = this._worksheetInfo.plasticPallet = ''
    this._showToast({ message: i18next.t('text.now_using_normal_pallet') })
    this.updateContext()
  }

  _validateComplete() {
    if (this.refOrderType === AVAIL_ORDER_TYPES.ARRIVAL_NOTICE.value) {
      if (!this._orderNo) {
        throw new Error(i18next.t('text.there_is_no_arrival_notice_no'))
      }

      if (
        !this.orderProductData.records
          .filter(task =>
            task.packQty === 0
              ? false
              : (task.palletQty ? task.actualPalletQty !== task.palletQty : false) ||
                task.actualPackQty !== task.packQty
          )
          .every(task => task.issue)
      )
        throw new Error(i18next.t('text.there_is_no_issue_noted'))
    } else if (this.refOrderType === AVAIL_ORDER_TYPES.RETURN_ORDER.value) {
      if (!this._orderNo) {
        throw new Error(i18next.t('text.there_is_no_return_order_no'))
      }

      if (!this.orderProductData.records.filter(task => task.actualPackQty !== task.packQty).every(task => task.issue))
        throw new Error(i18next.t('text.there_is_no_issue_noted'))
    }
  }

  _getWorksheetDetails(unloadType) {
    return this.orderProductData.records.map(task => {
      return unloadType == 'partial'
        ? { name: task.name, issue: task.issue ? task.issue : null, actualPackQty: task.actualPackQty }
        : { name: task.name, issue: task.issue ? task.issue : null }
    })
  }

  async _openVasPopup() {
    await sleep(1010)
    openPopup(
      html`
        <palletizing-pallet-popup
          .orderNo="${this._orderNo}"
          .data="${this.orderVasData}"
          @order-vas-data="${e => {
            this._fetchProducts(this._orderNo)
          }}"
        ></palletizing-pallet-popup>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.palletizing_with_pallets')
      }
    )
  }

  _focusOnOrderNoField() {
    setTimeout(() => this.orderNoInput.focus(), 100)
  }

  _focusOnLotInput() {
    setTimeout(() => this.lotInput?.focus(), 100)
  }

  _focusOnCartonInput() {
    if (this._enableProductScanning && this.productBarcodeInput) this.productBarcodeInput.readOnly = false
    setTimeout(() => this.cartonInput?.focus(), 100)
  }

  _focusOnActualQtyInput() {
    setTimeout(() => this.actualQtyInput?.focus(), 100)
  }

  _focusOnExpiryDateInput() {
    setTimeout(() => this.expDateInput?.focus(), 100)
  }

  _focusOnManufactureDateInput() {
    setTimeout(() => this.manufactureDateInput?.focus(), 100)
  }

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

  _focusOnSerialNumberInput() {
    setTimeout(() => this.serialNumberInput?.focus(), 100)
  }

  orderTypeChangeHandler(e) {
    this.refOrderType = e.currentTarget.value
    this._clearView()
    this.orderNoInput?.select()
  }

  _getFieldSequence(isMobile = false) {
    const conditions = {
      carton: this._enableCartonLabel,
      product: this._enableProductScanning,
      qty: this._enableInputQty >= 0 && this._enableInputQty < 4 && !this._isRequireSerialNumberScanningInbound,
      mfg: this._isRequiredCheckManufactureDate,
      exp: this._isRequiredCheckExpiry,
      serial: this._isRequireSerialNumberScanningInbound
    }

    const conditionKey = [
      conditions.carton ? 'carton' : 'lot',
      conditions.product ? 'product' : '',
      conditions.qty ? 'qty' : '',
      conditions.mfg ? 'mfg' : '',
      conditions.exp ? 'exp' : '',
      conditions.serial ? 'serial' : ''
    ].filter(Boolean).join('_')

    if (isMobile) {
      const getConditionRow = () => 
        conditions.product ? ['conditionOfGoods', 'packingType'] : ['conditionOfGoods']

      // Predefined sequences for mobile (supports nested arrays for side-by-side)
      const sequences = {
        'carton_product': ['cartonId', 'productBarcode', getConditionRow()],
        'carton_product_qty': ['cartonId', 'productBarcode', getConditionRow(), 'qty'],
        'carton_product_exp_serial': ['cartonId', 'productBarcode', ['expiryDate', 'serialNumber'], getConditionRow()],
        'lot_qty_mfg_exp': [['lotId', 'qty'], ['manufactureDate', 'expiryDate'], getConditionRow()],
        'carton_product_serial': ['cartonId', 'productBarcode', 'serialNumber', getConditionRow()],
        'carton_product_mfg_exp': ['cartonId', 'productBarcode', ['manufactureDate', 'expiryDate'], getConditionRow()],
        'carton_product_mfg_exp_serial': ['cartonId', ['productBarcode', 'serialNumber'], ['manufactureDate', 'expiryDate'], getConditionRow()],
        'lot_qty_exp': ['lotId', ['expiryDate', 'qty'], getConditionRow()],
        'carton_product_exp': ['cartonId', 'productBarcode', 'expiryDate', getConditionRow()],
        'carton_product_qty_exp': ['cartonId', 'productBarcode', ['expiryDate', 'qty'], getConditionRow()],
        'carton_product_qty_mfg_exp': ['cartonId', ['productBarcode', 'qty'], ['manufactureDate', 'expiryDate'], getConditionRow()],
        'lot_qty': [['lotId', 'qty'], getConditionRow()],
        'carton_product_qty_mfg': ['cartonId', 'productBarcode', ['qty', 'manufactureDate'], getConditionRow()],
      }

      return sequences[conditionKey] || this._buildDynamicSequence(conditions, getConditionRow, isMobile)
    } else {
      // Web sequences (simple flat arrays, you can add specific combos here later)
      const sequences = {
        'carton_product': ['cartonId', 'productBarcode', 'packingType', 'conditionOfGoods'],
        'carton_product_qty': ['cartonId', 'productBarcode', 'packingType', 'conditionOfGoods', 'qty'],
        'carton_product_exp_serial': ['cartonId', 'productBarcode', 'packingType', 'conditionOfGoods', 'expiryDate', 'serialNumber'],
        'lotId_qty_mfg_exp': ['lotId', 'conditionOfGoods', 'packingType', 'manufactureDate', 'expiryDate', 'qty'],
        'carton_product_sn': ['cartonId', 'productBarcode', 'packingType', 'conditionOfGoods', 'serialNumber'],
        'carton_product_mfg_exp': ['cartonId', 'productBarcode', 'packingType', 'conditionOfGoods', 'manufactureDate', 'expiryDate'],
        'carton_product_mfg_exp_serial': ['cartonId', 'productBarcode', 'packingType', 'conditionOfGoods', 'manufactureDate', 'expiryDate', 'serialNumber'],
        'lotId_qty_exp': ['lotId', 'conditionOfGoods', 'packingType', 'expiryDate', 'qty'],
        'carton_product_exp': ['cartonId', 'productBarcode', 'packingType', 'conditionOfGoods', 'expiryDate'],
        'carton_product_qty_exp': ['cartonId', 'productBarcode', 'packingType', 'conditionOfGoods', 'expiryDate', 'qty'],
        'carton_product_qty_mfg_exp': ['cartonId', 'productBarcode', 'packingType', 'conditionOfGoods', 'manufactureDate', 'expiryDate', 'qty'],
        'lotId_qty': ['lotId', 'conditionOfGoods', 'packingType', 'qty'],
      }

      return sequences[conditionKey] || this._buildDynamicSequence(conditions, null, isMobile)
    }
  }

  _buildDynamicSequence(conditions, getConditionRow, isMobile = false) {
    if (isMobile) {
      const fields = [
        conditions.carton ? 'cartonId' : 'lotId',
        conditions.product && 'productBarcode',
        conditions.qty && 'qty',
        conditions.mfg && 'manufactureDate',
        conditions.exp && 'expiryDate',
        conditions.serial && 'serialNumber'
      ].filter(Boolean)

      return [...fields, getConditionRow()]
    } else {
      const fields = [
        conditions.carton ? 'cartonId' : 'lotId',
        conditions.product && 'productBarcode',
        conditions.product && 'packingType',
        conditions.qty && 'qty',
        conditions.mfg && 'manufactureDate',
        conditions.exp && 'expiryDate',
        conditions.serial && 'serialNumber',
        'conditionOfGoods'
      ].filter(Boolean)
      
      return fields
    }
  }

  _renderFormFields(isMobile = false) {
    const sequence = this._getFieldSequence(isMobile)
    const keypressHandler = async e => {
      if (e.keyCode === 13) {
        e.preventDefault()
        await this._transactionHandler(e)
      }
    }

    if (isMobile) {
      return sequence.map(item => {
        if (Array.isArray(item)) {
          return html`
            <fieldset class="side-by-side-row">
              ${item.map(fieldName => html`<fieldset>${this._renderFieldContent(fieldName, keypressHandler)}</fieldset>`)}
            </fieldset>
          `
        }
        return html`<fieldset>${this._renderFieldContent(item, keypressHandler)}</fieldset>`
      })
    } else {
      return html`
        <fieldset>
          ${sequence.map(fieldName => this._renderFieldContent(fieldName, keypressHandler))}
        </fieldset>
      `
    }
  }

  _renderFieldContent(fieldName, keypressHandler) {
    switch (fieldName) {
      case 'cartonId':
        return html`
          <label>${i18next.t('label.carton_id')}</label>
          <barcode-scanable-input
            name="cartonId"
            .value=${this._cartonId}
            without-enter
            custom-input
            ?scan-only=${this._pageSettings?.cameraScanOnly}
            @keypress="${keypressHandler}"
          ></barcode-scanable-input>
        `
      case 'lotId':
        return html`
          <label>${i18next.t('label.lot_id')}</label>
          <barcode-scanable-input
            name="lotId"
            .value=${this._palletId}
            without-enter
            custom-input
            ?scan-only=${this._pageSettings?.cameraScanOnly}
            @keypress="${keypressHandler}"
          ></barcode-scanable-input>
        `
      case 'productBarcode':
        return html`
          <label>${i18next.t('label.product_barcode')}</label>
          <barcode-scanable-input
            name="productBarcode"
            .value=${this._productBarcode}
            without-enter
            custom-input
            ?scan-only=${this._pageSettings?.cameraScanOnly}
            @keypress="${keypressHandler}"
          ></barcode-scanable-input>
        `
      case 'packingType':
        return html`
          <label>${i18next.t('label.packing_type')}</label>
          <input name="packingType" type="text" value="${this._packingTypeScanned}" disabled="true" />
        `
      case 'qty':
        return html`
          <label>${i18next.t('label.actual_qty')}</label>
          <input
            name="qty"
            type="number"
            min="1"
            @keypress="${keypressHandler}"
          />
        `
      case 'manufactureDate':
        return html`
          <label>${i18next.t('label.manufacture_date')}</label>
          <input
            name="manufactureDate"
            type="date"
            @keypress="${keypressHandler}"
          />
        `
      case 'expiryDate':
        return html`
          <label>${i18next.t('label.expiry_date')}</label>
          <input
            name="expDate"
            type="date"
            @keypress="${keypressHandler}"
          />
        `
      case 'serialNumber':
        return html`
          <label>${i18next.t('label.serial_number')}</label>
          <barcode-scanable-input
            name="serialNumber"
            without-enter
            custom-input
            ?scan-only=${this._pageSettings?.cameraScanOnly}
            @keypress="${keypressHandler}"
          ></barcode-scanable-input>
        `
      case 'conditionOfGoods':
        return html`
          <label>${i18next.t('label.condition')}</label>
          <select name="conditionOfGoods" .value="${this._conditionOfGoods || CONDITION_OF_GOODS.GOOD.value}" @change="${this.conditionOfGoodsChangeHandler.bind(this)}">
            ${Object.keys(CONDITION_OF_GOODS).map(key =>
              html`
                <option value="${CONDITION_OF_GOODS[key].value}" ?selected="${this._conditionOfGoods === CONDITION_OF_GOODS[key].value}">
                  ${i18next.t(`label.${CONDITION_OF_GOODS[key].name}`)}
                </option>
              `
            )}
          </select>
        `
      default:
        return html``
    }
  }

  async _exportableData() {
    try {
      let records = this.orderProductData.records

      var headerSetting = this.orderProductConfig.columns
        .filter(column => column.type !== 'gutter' && column.imex !== undefined)
        .map(column => {
          return column.imex
        })

      var data = records.map(item => {
        return {
          id: item.id,
          ...this.orderProductConfig.columns
            .filter(column => column.type !== 'gutter' && column.imex !== undefined)
            .reduce((record, column) => {
              record[column.imex.key] = column.imex.key
                .split('.')
                .reduce((obj, key) => (obj && obj[key] !== 'undefined' ? obj[key] : undefined), item)
              return record
            }, {})
        }
      })

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

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

window.customElements.define('unload-product', UnloadProduct)
