import '@things-factory/barcode-ui'
import '@things-factory/grist-ui'
import './release-good-info'
import './delivery-order-info'
import '../show-ro-list-popup'
import '../transport-vehicles-popup'
import '../tote-popup'
import './load-by-qty'
import './warehouse-return-popup'

import { gql } from 'graphql-tag'
import { html, LitElement } from 'lit-element'
import { i18next } from '@things-factory/i18n-base'
import { openPopup } from '@things-factory/layout-base'
import { client, CustomAlert, gqlContext } from '@things-factory/shell'
import { CommonButtonStyles } from '@things-factory/styles'
import { WORKSHEET_STATUS } from '../../constants'
import { fetchPageSettings, showToast } from '../../../util'

// Constants
const SETTINGS = {
  QC_SKU: 'qc-sku',
  DISABLE_QC: 'disable-QC-in-loading',
  ENABLE_TOTE_SCANNING: 'enable-tote-scanning',
  DISPLAY_CONVERSION: 'display-conversion'
}

const TAB_TYPES = {
  QC: 'QC',
  LOADING: 'LOADING',
  LOADED: 'LOADED'
}

// GraphQL Queries
const FIND_LOADABLE_RELEASE_GOOD = gql`
  query findLoadableReleaseGood($orderNo: String!, $tabName: String!) {
    findLoadableReleaseGood(orderNo: $orderNo, tabName: $tabName) {
      id
      name
      refNo
      refNo2
      status
      district
      attentionCompany
      attentionTo
      deliveryAddress1
      deliveryAddress2
      sortedBy {
        name
      }
      itemCounts {
        total
        pending
      }
      tabCounts {
        LOADING {
          total
          completed
          pending
        }
        LOADED {
          total
          completed
          pending
        }
      }
    }
  }
`

const FIND_LOADABLE_RELEASE_GOOD_BY_BIN = gql`
  query findLoadableReleaseGoodByBin($orderNo: String!) {
    findLoadableReleaseGoodByBin(orderNo: $orderNo) {
      worksheetInfo {
        binLocationName
        isReleaseGoodScan
        releaseGood {
          id
          name
          attentionCompany
          attentionTo
          deliveryAddress1
          deliveryAddress2
          status
          refNo
          refNo2
          refNo3
          rtm
        }
      }
      worksheetDetailInfos {
        id
        name
        batchId
        releaseQty
        pickedQty
        sortedQty
        status
        packingType
        packingSize
        conversion
        expirationDate
        binLocation {
          name
        }
        releaseGood {
          id
          name
          attentionCompany
          attentionTo
          deliveryAddress1
          deliveryAddress2
          status
          refNo
          refNo2
          refNo3
        }
        productName
        productSku
        relatedOrderInv {
          id
          batchId
          packingType
          refWorksheetId
          packedQty
          releaseQty
          product {
            id
            sku
            name
            isInventoryDecimal
          }
          productDetail {
            id
            productBarcodes {
              gtin
            }
          }
          orderProduct {
            id
          }
        }
      }
    }
  }
`

const LOADING_WORKSHEET_V2 = gql`
  query loadingWorksheetv2($orderNo: String!, $filter: String) {
    loadingWorksheetv2(orderNo: $orderNo, filter: $filter) {
      worksheetInfo {
        worksheet {
          id
          name
          type
          status
        }
        bizplaceName
        startedAt
        releaseGood {
          id
          name
          attentionCompany
          attentionTo
          deliveryAddress1
          deliveryAddress2
          status
          refNo
          refNo2
          refNo3
          remark
          rtm
        }
      }
      worksheetDetailInfos {
        id
        name
        batchId
        releaseQty
        pickedQty
        sortedQty
        status
        packingType
        packingSize
        conversion
        expirationDate
        binLocation {
          name
        }
        productName
        productSku
        relatedOrderInv {
          id
          batchId
          packingType
          refWorksheetId
          packedQty
          releaseQty
          product {
            id
            sku
            name
            isInventoryDecimal
          }
          productDetail {
            id
            productBarcodes {
              gtin
            }
          }
          orderProduct {
            id
          }
        }
      }
    }
  }
`

const GET_DELIVERY_ORDERS = gql`
  query getDeliveryOrders($releaseGoodNo: String!) {
    getDeliveryOrders(releaseGoodNo: $releaseGoodNo) {
      completable
      deliveryOrders {
        id
        name
        truckNo
        orderInventories {
          batchId
          packingSize
          packingType
          pickedQty
          product {
            name
            sku
          }
          inventory {
            expirationDate
          }
        }
      }
    }
  }
`

const SORT_ITEM = gql`
  mutation sortItem(
    $releaseGoodNo: String!
    $productBarcode: String!
    $qty: Float!
    $worksheetDetails: [Object!]
    $toteNo: String
  ) {
    sortItem(
      releaseGoodNo: $releaseGoodNo
      productBarcode: $productBarcode
      qty: $qty
      worksheetDetails: $worksheetDetails
      toteNo: $toteNo
    )
  }
`

const COMPLETE_LOAD = gql`
  mutation completeLoad($orderId: String!, $worksheetId: String!) {
    completeLoad(orderId: $orderId, worksheetId: $worksheetId)
  }
`

const COMPLETE_LOAD_PARTIALLY = gql`
  mutation completeLoadPartially($orderId: String!, $worksheetId: String!) {
    completeLoadPartially(orderId: $orderId, worksheetId: $worksheetId)
  }
`

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

export class LoadingExecutionBase extends LitElement {
  static get properties() {
    return {
      parent: Object,
      state: Object,
      worksheet: {
        type: Object,
        hasChanged: (newVal, oldVal) => {
          if (!newVal && !oldVal) return true
          if (!oldVal && newVal) return true
          if (oldVal && !newVal) return false

          const oldData = JSON.stringify(oldVal)
          const newData = JSON.stringify(newVal)
          return newData.length > oldData.length
        }
      },
      worksheetDetails: Object,
      deliveryOrdersInfo: Object,
      qcData: Array,
      loadingData: Array,
      loadedData: Array,
      _executionData: Object,
      _disableQC: Boolean,
      _useQcSku: Boolean,
      _enableToteScanning: Boolean,
      _enableToteScanningValue: Number,
      _hasRefWorksheetId: Boolean,
      toteNo: String,
      _tabCounts: Object,
      _needsSealing: Boolean,
      _displayConversion: Boolean
    }
  }

  constructor() {
    super()
    this._useQcSku = false
    this._enableToteScanning = false
    this._enableToteScanningValue = 0
    this._hasRefWorksheetId = false
    this.toteNo = ''
    this._needsSealing = false
    this._worksheetState = null
    this._executionData = {
      ownCollection: false,
      orderId: null,
      worksheetId: null
    }
  }

  set worksheet(value) {
    if (
      !this._worksheetState ||
      (value && JSON.stringify(value).length > JSON.stringify(this._worksheetState).length)
    ) {
      this._worksheetState = value
      this.requestUpdate('worksheet', this.worksheet)
    }
  }

  get worksheet() {
    return this._worksheetState
  }

  updated(changedProperties) {
    if (changedProperties.has('worksheet')) {
      this._currentReleaseGood = {
        releaseGood: this.worksheet?.releaseGood,
        bizplaceName: this.worksheet?.bizplaceName,
        startedAt: this.worksheet?.startedAt
      }
    }
  }

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

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

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

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

  get warehouseReturnButton() {
    return {
      title: i18next.t('button.warehouse_return'),
      action: async () => {
        await this.warehouseReturn()
      },
      ...CommonButtonStyles.inspect
    }
  }

  get orderListButton() {
    return {
      title: i18next.t('button.order_list'),
      action: async () => {
        try {
          // If isReleaseGoodScan is true, use release good name; otherwise use binLocationName if QC SKU mode
          const orderNo = this.worksheet?.isReleaseGoodScan
            ? this.worksheet?.releaseGood?.name
            : this._useQcSku
            ? this.worksheet?.binLocationName
            : this.worksheet?.releaseGood?.name

          if (!orderNo) {
            throw new Error(i18next.t('text.no_order_selected'))
          }

          const response = await client.query({
            query: FIND_LOADABLE_RELEASE_GOOD,
            variables: {
              orderNo,
              tabName: this.state?.activeTab || TAB_TYPES.LOADING
            },
            context: gqlContext(),
            fetchPolicy: 'network-only' // Always fetch fresh data to show updated item counts
          })

          if (!response.errors) {
            const orders = response.data.findLoadableReleaseGood
            if (orders.length) {
              await this.selectReleaseOrderPopup(orders, this.state?.activeTab)
            } else {
              throw new Error(i18next.t('text.no_release_order_found'))
            }
          }
        } catch (e) {
          showToast(e)
        }
      },
      ...CommonButtonStyles.edit
    }
  }

  get toteButton() {
    return {
      title: i18next.t('button.tote'),
      action: async () => {
        await this.openTotePopup()
      },
      ...CommonButtonStyles.activate
    }
  }

  async openTotePopup() {
    try {
      const orderNo = this._currentReleaseGood?.releaseGood?.name
      const bizplace = this._currentReleaseGood?.bizplaceName

      if (!orderNo) {
        throw new Error(i18next.t('text.no_release_order_selected'))
      }

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

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

      openPopup(
        html`
          <tote-popup
            .orderNo="${orderNo}"
            .toteNo="${this.toteNo}"
            .bizplace="${bizplace}"
            .minimumSealNumber="${minimumSealNumber}"
            @completed="${async e => {
              this.toteNo = e.detail
              // Check seal status after tote is sealed (may have changed)
              await this._checkAndUpdateSealStatus()

              // Refresh data for both QC and LOADING tabs to reflect any undone items
              // Use the same logic as validateSorting to determine which order/bin to refresh
              const orderNoForRefresh = this.worksheet?.isReleaseGoodScan
                ? this.worksheet?.releaseGood?.name
                : this._useQcSku
                ? this.worksheet?.binLocationName
                : this.worksheet?.releaseGood?.name
              const useQcSkuForRefresh = this._useQcSku && !this.worksheet?.isReleaseGoodScan

              const [qcData, loadingData] = await Promise.all([
                this.fetchData(orderNoForRefresh, 'qc', useQcSkuForRefresh),
                this.fetchData(orderNoForRefresh, 'loading', useQcSkuForRefresh)
              ])

              // QC tab: items that still need QC (sortedQty < pickedQty)
              this.qcData = qcData.filter(data => data?.sortedQty < data?.pickedQty) || []
              // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
              this.loadingData =
                loadingData.filter(data => data?.sortedQty >= data?.pickedQty && data.status != WORKSHEET_STATUS.DONE.value) || []

              // Update button visibility after sealing (Tote button may need to be hidden if sealing is complete)
              this.getButton()
              // Update QC banner state after data is refreshed
              if (this._updateQcBannerState) {
                await this._updateQcBannerState()
              }
            }}"
          ></tote-popup>
        `,
        {
          closable: false,
          backdrop: false,
          escapable: false,
          size: 'large',
          title: i18next.t('title.tote_scanning')
        }
      )
    } catch (e) {
      showToast(e)
    }
  }

  get groupPackingListButton() {
    // Only return button when active tab is LOADING
    if (this.state?.activeTab !== TAB_TYPES.LOADING) {
      return null
    }
    return {
      title: i18next.t('button.group_packing_list'),
      action: async () => {},
      ...CommonButtonStyles.inspect
    }
  }

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

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

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

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

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

  get loadingDataView() {
    return this.shadowRoot.querySelector('product-list-view#loading')
  }

  get qcDataView() {
    return this.shadowRoot.querySelector('product-list-view#qc')
  }

  // Core Methods
  async openTab(e, tabName) {
    // Validate seals before allowing switch to LOADING tab
    // Validation applies when tote scanning is enabled and switching from QC or LOADED to LOADING
    // The validation query checks if totes exist and are properly sealed based on minimum-seal-number
    if (
      tabName === TAB_TYPES.LOADING &&
      (this.state?.activeTab === TAB_TYPES.QC || this.state?.activeTab === TAB_TYPES.LOADED) &&
      this._enableToteScanning
    ) {
      const orderNo = this._currentReleaseGood?.releaseGood?.name || this.worksheet?.releaseGood?.name
      if (orderNo) {
        try {
          const validationResponse = await client.query({
            query: VALIDATE_QC_SEALS,
            variables: { releaseGoodNo: orderNo },
            context: gqlContext()
          })

          if (!validationResponse.errors && validationResponse.data?.validateQcSeals) {
            const { valid, error, minimumSealNumber } = validationResponse.data.validateQcSeals
            // Only block if valid is false AND minimumSealNumber > 0
            // If minimumSealNumber is 0, allow switching even if valid is false
            if (!valid && (minimumSealNumber || 0) > 0) {
              throw new Error(error || 'Seal validation failed')
            }
          }
        } catch (e) {
          showToast(e)
          // Don't change tab on validation failure
          return
        }
      }
    }

    this.state = { ...this.state, activeTab: tabName }
    await this.updateComplete

    if (tabName === TAB_TYPES.QC) {
      // If isReleaseGoodScan is true, use release good name; otherwise use binLocationName if QC SKU mode
      const orderNoForQc = this.worksheet?.isReleaseGoodScan
        ? this.worksheet?.releaseGood?.name
        : this._useQcSku
        ? this.worksheet?.binLocationName
        : this.worksheet?.releaseGood?.name
      const useQcSkuForFetch = this._useQcSku && !this.worksheet?.isReleaseGoodScan
      // Fetch both QC and LOADING data so banner can check if items are ready for loading
      const [qcData, loadingData] = await Promise.all([
        this.fetchData(orderNoForQc, 'qc', useQcSkuForFetch),
        this.fetchData(orderNoForQc, 'loading', useQcSkuForFetch)
      ])

      // QC tab: items that still need QC (sortedQty != pickedQty)
      this.qcData = qcData.filter(data => data?.sortedQty != data?.pickedQty) || []
      // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
      this.loadingData =
        loadingData.filter(data => data?.sortedQty >= data?.pickedQty && data.status != WORKSHEET_STATUS.DONE.value) || []

      // Check seal status when opening QC tab (needed for Tote button visibility)
      await this._checkAndUpdateSealStatus()
      // Update button visibility after fetching QC data (which updates _hasRefWorksheetId)
      this.getButton()
      // Update QC banner state after fetching QC data
      if (this._updateQcBannerState) {
        await this._updateQcBannerState()
      }
    } else if (tabName === TAB_TYPES.LOADING) {
      // Use bin-based query only if QC SKU mode AND it's not a release good scan
      if (this._useQcSku && this.worksheet?.binLocationName && !this.worksheet?.isReleaseGoodScan) {
        // If there's already a selected release order, refresh data for it first
        // This ensures that if user closes the popup without selection, current data is shown
        // IMPORTANT: Use bin location name with useQcSku=true to filter by specific bin
        if (this._currentReleaseGood?.releaseGood?.name) {
          // Fetch data using bin location (not release order name) to get only items from this bin
          let binData = await this.fetchData(this.worksheet.binLocationName, 'loading', true)
          // Filter to only show items for the currently selected release order AND from this bin
          // Note: FIND_LOADABLE_RELEASE_GOOD_BY_BIN returns OrderInventory status ('LOADED'),
          // not WorksheetDetail status ('DONE'), so we filter by 'LOADED'
          // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
          this.loadingData = (binData || []).filter(
            data => data?.sortedQty >= data?.pickedQty && data?.status !== 'LOADED' && data?.releaseGood?.name === this._currentReleaseGood.releaseGood.name
          )
        }

        const response = await client.query({
          query: FIND_LOADABLE_RELEASE_GOOD,
          variables: {
            orderNo: this.worksheet?.binLocationName,
            tabName: this.state?.activeTab || TAB_TYPES.LOADING
          },
          context: gqlContext(),
          fetchPolicy: 'network-only' // Always fetch fresh data to show updated item counts
        })

        if (!response.errors) {
          let records = response.data.findLoadableReleaseGood
          if (records.length) {
            // Store tabCounts from first record if available
            if (records[0]?.tabCounts) {
              this._tabCounts = records[0].tabCounts
            }
            await this.selectReleaseOrderPopup(records, this.state?.activeTab)
            if (this.state.activeTab !== TAB_TYPES.LOADING) {
              this.state = { ...this.state, activeTab: TAB_TYPES.LOADING }
              await this.updateComplete
            }
          } else {
            throw new Error(i18next.t('text.no_release_order_found'))
          }
        }
      } else {
        const rawLoadingData = await this.fetchData(this.worksheet?.releaseGood?.name, 'loading', this._useQcSku)

        // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
        this.loadingData = (rawLoadingData || []).filter(
          data => data?.sortedQty >= data?.pickedQty && data?.status !== WORKSHEET_STATUS.DONE.value
        )

        // Refresh tabCounts when switching to LOADING tab
        if (this._currentReleaseGood?.releaseGood?.name || this.worksheet?.releaseGood?.name) {
          const orderNo = this._currentReleaseGood?.releaseGood?.name || this.worksheet?.releaseGood?.name
          try {
            const response = await client.query({
              query: FIND_LOADABLE_RELEASE_GOOD,
              variables: {
                orderNo,
                tabName: TAB_TYPES.LOADING
              },
              context: gqlContext(),
              fetchPolicy: 'network-only' // Always fetch fresh data to show updated counts
            })
            if (!response.errors && response.data.findLoadableReleaseGood.length > 0) {
              const releaseGood = response.data.findLoadableReleaseGood[0]
              if (releaseGood.tabCounts) {
                this._tabCounts = releaseGood.tabCounts
              }
            }
          } catch (e) {
            console.error('Error refreshing tabCounts:', e)
          }
        }
      }
    } else if (tabName === TAB_TYPES.LOADED) {
      // Ensure _currentReleaseGood is set from worksheet if not already set
      if (!this._currentReleaseGood?.releaseGood?.name && this.worksheet?.releaseGood?.name) {
        this._currentReleaseGood = {
          releaseGood: this.worksheet.releaseGood,
          bizplaceName: this.worksheet?.bizplaceName,
          startedAt: this.worksheet?.startedAt
        }
      }

      // If we have a current release good, use reload() which works with order number (no bin required)
      if (this._currentReleaseGood?.releaseGood?.name) {
        await this.reload()
        // reload() already calls getButton(), so we can return here
        return
      }

      // If using QC SKU mode and need to query by bin -> require bin location
      // Only enforce bin selection when actually needed for bin-based operations
      if (this._useQcSku) {
        if (!this.worksheet?.binLocationName) {
          throw new Error(i18next.t('text.no_bin_location_selected'))
        }

        const response = await client.query({
          query: FIND_LOADABLE_RELEASE_GOOD,
          variables: {
            orderNo: this.worksheet.binLocationName,
            tabName: this.state?.activeTab || TAB_TYPES.LOADED
          },
          context: gqlContext(),
          fetchPolicy: 'network-only' // Always fetch fresh data to show updated item counts
        })

        if (!response.errors) {
          let records = response.data.findLoadableReleaseGood
          if (records.length) {
            // Store tabCounts from first record if available
            if (records[0]?.tabCounts) {
              this._tabCounts = records[0].tabCounts
            }
            await this.selectReleaseOrderPopup(records, this.state?.activeTab)
            // selectReleaseOrderPopup will handle data fetch and getButton() call
            return
          } else {
            throw new Error(i18next.t('text.no_release_order_found'))
          }
        }
      } else {
        // Not using QC SKU mode and no current release good -> try to use order number from worksheet
        // Allow switching to LOADED tab even without bin selection
        if (this.worksheet?.releaseGood?.name) {
          await this.reload()
          return
        } else {
          // No order selected at all -> show error
          throw new Error(i18next.t('text.no_release_order_selected'))
        }
      }
    }

    this.getButton()
  }

  async selectReleaseOrderPopup(orders, tabName) {
    let releaseGoods = orders.map(ro => ({
      ...ro,
      sortedByUser: ro.sortedBy?.name
    }))

    openPopup(
      html`
        <show-ro-list-popup
          .releaseOrders=${releaseGoods}
          .sorting=${false}
          @ro-selected-loading=${async e => {
            const selectedRO = e.detail.ro

            this.worksheet = {
              ...this.worksheet,
              releaseGood: selectedRO,
              binLocationName: this.worksheet?.binLocationName
            }

            this._currentReleaseGood = {
              releaseGood: selectedRO,
              bizplaceName: this.worksheet?.bizplaceName,
              startedAt: this.worksheet?.startedAt
            }

            // Reset tote selection when switching RO
            this.toteNo = ''
            // Reset refWorksheetId flag - will be updated when data is fetched
            this._hasRefWorksheetId = false
            // Reset seal status - will be updated after data is fetched
            this._needsSealing = false

            this._executionData = {
              ...this._executionData,
              orderId: selectedRO.id
            }

            // Store tabCounts for banner display
            if (selectedRO.tabCounts) {
              this._tabCounts = selectedRO.tabCounts
            }

            // UX-safe tab selection: Auto-switch to LOADED if LOADING is empty but LOADED has items
            let finalTabName = tabName
            if (selectedRO.tabCounts) {
              const loadingCounts = selectedRO.tabCounts.LOADING || { total: 0, pending: 0 }
              const loadedCounts = selectedRO.tabCounts.LOADED || { total: 0, pending: 0 }

              // If current tab is LOADING and it's empty, but LOADED has items, switch to LOADED
              if (tabName === TAB_TYPES.LOADING && loadingCounts.total === 0 && loadedCounts.total > 0) {
                finalTabName = TAB_TYPES.LOADED
              }
            }

            this.state = { ...this.state, activeTab: finalTabName }
            await this.updateComplete

            if (finalTabName === TAB_TYPES.LOADING) {
              // In QC SKU mode with bin location, fetch by bin and filter by selected order
              // to preserve the bin-scoped view
              if (this._useQcSku && this.worksheet?.binLocationName && !this.worksheet?.isReleaseGoodScan) {
                let binData = await this.fetchData(this.worksheet.binLocationName, 'loading', true)

                // Filter to only show items for the selected release order from this bin
                // Note: FIND_LOADABLE_RELEASE_GOOD_BY_BIN returns OrderInventory status ('LOADED')
                // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
                this.loadingData = (binData || []).filter(
                  data => data?.sortedQty >= data?.pickedQty && data?.status !== 'LOADED' && data?.releaseGood?.name === selectedRO.name
                )
              } else {
                this.loadingData = await this.fetchData(selectedRO.name, 'loading', false)
                // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
                this.loadingData = (this.loadingData || []).filter(
                  data => data?.sortedQty >= data?.pickedQty && data?.status !== WORKSHEET_STATUS.DONE.value
                )
              }
            } else if (finalTabName === TAB_TYPES.LOADED) {
              this.loadedData = await fetchDeliveryOrders(selectedRO.name)
              if (!this.worksheet?.worksheet?.id) {
                await this.fetchData(selectedRO.name, 'loading', this._useQcSku)
              }
            } else if (finalTabName === TAB_TYPES.QC) {
              // Fetch both QC and LOADING data so banner can check if items are ready for loading
              // In QC SKU mode with bin location, fetch by bin and filter by selected order
              if (this._useQcSku && this.worksheet?.binLocationName && !this.worksheet?.isReleaseGoodScan) {
                let binData = await this.fetchData(this.worksheet.binLocationName, 'qc', true)
                // Filter to only show items for the selected release order from this bin
                // Note: FIND_LOADABLE_RELEASE_GOOD_BY_BIN returns OrderInventory status
                this.qcData = (binData || []).filter(
                  data =>
                    data?.sortedQty < data?.pickedQty &&
                    data?.status !== 'LOADED' &&
                    data?.releaseGood?.name === selectedRO.name
                )
                // Also fetch loading data for banner
                // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
                let binLoadingData = await this.fetchData(this.worksheet.binLocationName, 'loading', true)
                this.loadingData = (binLoadingData || []).filter(
                  data =>
                    data?.sortedQty >= data?.pickedQty && data?.status !== 'LOADED' && data?.releaseGood?.name === selectedRO.name
                )
              } else {
                const [qcData, loadingData] = await Promise.all([
                  this.fetchData(selectedRO.name, 'qc', this._useQcSku),
                  this.fetchData(selectedRO.name, 'loading', this._useQcSku)
                ])
                // QC tab: items that still need QC (sortedQty < pickedQty)
                this.qcData = qcData.filter(data => data?.sortedQty < data?.pickedQty) || []
                // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
                this.loadingData =
                  loadingData.filter(data => data?.sortedQty >= data?.pickedQty && data.status != WORKSHEET_STATUS.DONE.value) || []
              }
              // fetchData updates _hasRefWorksheetId, so update buttons after data is fetched
            }

            await this.updateComplete
            // Check seal status after order is selected (needed for Tote button visibility)
            await this._checkAndUpdateSealStatus()
            // Update button visibility after data is fetched (which updates _hasRefWorksheetId)
            this.getButton()
            // Update QC banner state after data is fetched
            if (this._updateQcBannerState) {
              await this._updateQcBannerState()
            }
          }}
        ></show-ro-list-popup>
      `,
      {
        backdrop: true,
        size: 'medium',
        title: i18next.t('title.release_orders')
      }
    )
  }

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

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

  async resetView() {
    const pageSettings = await fetchPageSettings({
      settings: {
        name: 'name',
        operator: 'in',
        value: [SETTINGS.DISABLE_QC, SETTINGS.QC_SKU, SETTINGS.ENABLE_TOTE_SCANNING, SETTINGS.DISPLAY_CONVERSION]
      }
    })
    const settingsMap = new Map(pageSettings?.settings?.map(setting => [setting.name, setting.value]) || [])

    this._disableQC = settingsMap.get(SETTINGS.DISABLE_QC) === 'true'
    this._useQcSku = settingsMap.get(SETTINGS.QC_SKU) === 'true'
    this._enableToteScanningValue = parseInt(settingsMap.get(SETTINGS.ENABLE_TOTE_SCANNING) || '0', 10)
    this._displayConversion = settingsMap.get(SETTINGS.DISPLAY_CONVERSION) === 'true'
    this._enableToteScanning = this._enableToteScanningValue >= 1

    let activeTab = TAB_TYPES.QC
    if (this._disableQC && !this._useQcSku) {
      activeTab = TAB_TYPES.LOADING
    }

    this.state = { ...this.state, activeTab }

    const currentWorksheet = this.worksheet

    // Check if any order inventory has refWorksheetId (when data comes from props)
    if (this.worksheetDetails && this.worksheetDetails.length > 0) {
      this._hasRefWorksheetId = this.worksheetDetails.some(
        item => item?.relatedOrderInv?.refWorksheetId != null && item?.relatedOrderInv?.refWorksheetId !== ''
      )
    }

    // QC tab: items that still need QC (sortedQty < pickedQty)
    this.qcData = this.worksheetDetails.filter(data => data?.sortedQty < data?.pickedQty) || []
    // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
    this.loadingData =
      this.worksheetDetails.filter(data => data?.sortedQty >= data?.pickedQty && data.status != WORKSHEET_STATUS.DONE.value) || []
    this.loadedData = this.deliveryOrdersInfo || []

    this._executionData = {
      ...this._executionData,
      orderId: currentWorksheet?.releaseGood?.id,
      worksheetId: currentWorksheet?.worksheet?.id
    }

    // reset tote when resetting view
    this.toteNo = ''

    if (!this.worksheet && this.worksheetDetails?.length > 0) {
      this.worksheet = {
        ...currentWorksheet,
        binLocationName: this.worksheetDetails[0]?.binLocation?.name
      }
    }

    this.getButton()
  }

  /**
   * Reloads all data and counts after mutations (load, undo, etc.)
   * This ensures banner and button visibility are always accurate
   * Always fetches counts for BOTH LOADING and LOADED tabs
   */
  async reload() {
    // If no order selected, clear stale state and show error
    if (!this._currentReleaseGood?.releaseGood?.name && !this.worksheet?.releaseGood?.name) {
      // Clear stale state to prevent incorrect UI
      this._tabCounts = null
      this.loadedData = null
      showToast({ type: 'error', message: i18next.t('text.no_release_order_selected') })
      this.getButton()
      return
    }

    const orderNo = this._currentReleaseGood?.releaseGood?.name || this.worksheet?.releaseGood?.name

    try {
      // Query for BOTH LOADING and LOADED tabs to ensure complete tabCounts
      const [loadingResponse, loadedResponse] = await Promise.all([
        client.query({
          query: FIND_LOADABLE_RELEASE_GOOD,
          variables: {
            orderNo,
            tabName: TAB_TYPES.LOADING
          },
          context: gqlContext(),
          fetchPolicy: 'network-only' // Always fetch fresh data to show updated counts
        }),
        client.query({
          query: FIND_LOADABLE_RELEASE_GOOD,
          variables: {
            orderNo,
            tabName: TAB_TYPES.LOADED
          },
          context: gqlContext(),
          fetchPolicy: 'network-only' // Always fetch fresh data to show updated counts
        })
      ])

      // Merge tabCounts from both queries
      let mergedTabCounts = {
        LOADING: null,
        LOADED: null
      }

      // Extract LOADING counts
      if (!loadingResponse.errors && loadingResponse.data.findLoadableReleaseGood.length > 0) {
        const loadingReleaseGood = loadingResponse.data.findLoadableReleaseGood[0]
        if (loadingReleaseGood.tabCounts?.LOADING) {
          mergedTabCounts.LOADING = loadingReleaseGood.tabCounts.LOADING
        }
        // Also get LOADED counts from LOADING query if available (backend returns both)
        if (loadingReleaseGood.tabCounts?.LOADED) {
          mergedTabCounts.LOADED = loadingReleaseGood.tabCounts.LOADED
        }
      }

      // Extract LOADED counts (prefer from LOADED query if LOADING query didn't have it)
      if (!loadedResponse.errors && loadedResponse.data.findLoadableReleaseGood.length > 0) {
        const loadedReleaseGood = loadedResponse.data.findLoadableReleaseGood[0]
        if (loadedReleaseGood.tabCounts?.LOADED) {
          mergedTabCounts.LOADED = loadedReleaseGood.tabCounts.LOADED
        }
        // Also get LOADING counts from LOADED query if available (backend returns both)
        if (loadedReleaseGood.tabCounts?.LOADING && !mergedTabCounts.LOADING) {
          mergedTabCounts.LOADING = loadedReleaseGood.tabCounts.LOADING
        }
      }

      // Set merged tabCounts (only if we got at least one count)
      if (mergedTabCounts.LOADING || mergedTabCounts.LOADED) {
        this._tabCounts = mergedTabCounts
      } else {
        // Both queries returned empty or had errors - clear stale tabCounts
        this._tabCounts = null
      }

      // Refresh loadedData
      if (orderNo) {
        this.loadedData = await fetchDeliveryOrders(orderNo)
      }

      // Always refresh loadingData (not just when on LOADING tab)
      // This ensures banner condition (which uses loadingData.length) is accurate after undo/load mutations
      if (orderNo) {
        // In bin scan mode (qcSku enabled with bin location), fetch by bin and filter by selected RO
        // This ensures we only show items from the scanned bin, not all items for the RO
        if (this._useQcSku && this.worksheet?.binLocationName && !this.worksheet?.isReleaseGoodScan) {
          let binData = await this.fetchData(this.worksheet.binLocationName, 'loading', true)
          // Filter to only show items for the selected release order from this bin
          // Note: FIND_LOADABLE_RELEASE_GOOD_BY_BIN returns OrderInventory status ('LOADED')
          // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
          this.loadingData = (binData || []).filter(
            data => data?.sortedQty >= data?.pickedQty && data?.status !== 'LOADED' && data?.releaseGood?.name === orderNo
          )
        } else {
          // Standard mode: fetch by release order name
          this.loadingData = await this.fetchData(orderNo, 'loading', false)
          // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
          this.loadingData = (this.loadingData || []).filter(
            data => data?.sortedQty >= data?.pickedQty && data?.status !== WORKSHEET_STATUS.DONE.value
          )
        }
      }

      // Auto-switch to LOADED tab if LOADING tab is now empty but LOADED has items
      // This provides better UX after loading completes
      if (
        this.state?.activeTab === TAB_TYPES.LOADING &&
        (!this.loadingData || this.loadingData.length === 0) &&
        this.loadedData &&
        this.loadedData.length > 0
      ) {
        this.state = { ...this.state, activeTab: TAB_TYPES.LOADED }
      }

      // Update button visibility
      await this.updateComplete
      this.getButton()

      // Update QC banner state after reload (in case we're on QC tab)
      if (this._updateQcBannerState) {
        await this._updateQcBannerState()
      }

      // Request update to refresh banner visibility after data reload
      this.requestUpdate()
    } catch (e) {
      console.error('Error reloading data:', e)
      // Clear stale state on error to prevent incorrect UI
      this._tabCounts = null
      this.loadedData = null
      showToast({
        type: 'error',
        message: i18next.t('text.failed_to_refresh_data', { defaultValue: 'Failed to refresh data' })
      })
      this.getButton()
    }
  }

  /**
   * Checks if sealing is needed and updates _needsSealing property
   * This can be overridden by child classes to provide custom seal checking logic
   */
  async _checkAndUpdateSealStatus() {
    if (!this._enableToteScanning) {
      this._needsSealing = false
      return
    }

    const orderNo = this._currentReleaseGood?.releaseGood?.name || this.worksheet?.releaseGood?.name
    if (!orderNo) {
      this._needsSealing = false
      return
    }

    try {
      const validationResponse = await client.query({
        query: VALIDATE_QC_SEALS,
        variables: { releaseGoodNo: orderNo },
        context: gqlContext()
      })

      if (!validationResponse.errors && validationResponse.data?.validateQcSeals) {
        const { valid, minimumSealNumber } = validationResponse.data.validateQcSeals
        const minSealNum = minimumSealNumber || 0
        // Sealing is needed if valid is false AND minimumSealNumber > 0
        this._needsSealing = !valid && minSealNum > 0
      } else {
        this._needsSealing = false
      }
    } catch (e) {
      // On error, assume sealing might be needed to be safe
      this._needsSealing = true
    }
  }

  getButton() {
    let activeTab = this.state?.activeTab

    let buttons = [this.backButton]
    // Tote button visibility logic for QC tab:
    if (activeTab === TAB_TYPES.QC && this._enableToteScanning && this._useQcSku === false) {
      const shouldShowToteButton =
        (this._disableQC === false && this._hasRefWorksheetId) ||
        (this._disableQC === false && this._enableToteScanningValue === 2 && !this._hasRefWorksheetId) ||
        this._needsSealing === true

      if (shouldShowToteButton) {
        buttons.unshift(this.toteButton)
      }
    }
    if (activeTab === TAB_TYPES.LOADING) {
      if (this.loadingData?.length > 0) {
        if (this._useQcSku) {
          buttons.unshift(this.orderListButton)
        }
        // Only add button if getter returns a valid button (checks activeTab internally)
        const groupPackingButton = this.groupPackingListButton
        if (groupPackingButton) {
          buttons.unshift(groupPackingButton)
        }
        buttons.unshift(this.warehouseReturnButton)
        buttons.unshift(this.loadButton)
      }
    } else if (activeTab === TAB_TYPES.LOADED) {
      if (this._useQcSku) {
        buttons.unshift(this.orderListButton)
      }
      // Show COMPLETE button based on:
      // 1. Prefer _tabCounts.LOADED.total > 0 (most reliable)
      // 2. Only block if loadedData.completable === false explicitly
      // 3. Also check loadedData.deliveryOrders as fallback
      const hasLoadedItems =
        this._tabCounts?.LOADED?.total > 0 ||
        (this.loadedData?.deliveryOrders && this.loadedData.deliveryOrders.length > 0)
      const canComplete = this.loadedData?.completable !== false

      if (hasLoadedItems && canComplete) {
        buttons.unshift(this.completeButton)
      } else if (
        this.loadedData?.deliveryOrders &&
        this.loadedData.deliveryOrders.length > 0 &&
        this._currentReleaseGood?.releaseGood?.status !== 'LOADING'
      ) {
        buttons.unshift(this.partialButton)
      }
    }

    this.parent.configContext(buttons)
  }

  _getTotalItem() {
    let activeTab = this.state?.activeTab
    let total = 0

    if (activeTab === TAB_TYPES.QC) {
      total = this.qcData?.length
    } else if (activeTab === TAB_TYPES.LOADING) {
      total = this.loadingData?.length
    } else if (activeTab === TAB_TYPES.LOADED) {
      total = this.loadedData?.deliveryOrders?.length
    }

    return total
  }

  // UI Methods
  selectWarehouseTruck() {
    openPopup(
      html`
        <transport-vehicles-popup
          @selected="${e => {
            this.warehouseTruckInput.value = e.detail.name
          }}"
        ></transport-vehicles-popup>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('label.lorry_no')
      }
    )
  }

  async showOrderInfo() {
    await openPopup(
      html`
        <release-good-info
          .data=${this._currentReleaseGood}
          .releaseGoodNo=${this._currentReleaseGood?.releaseGood?.name}
        ></release-good-info>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.order_information')
      }
    )
  }

  async openDoInfoPopup(doNo) {
    const targetDO = this.loadedData.deliveryOrders.find(delivery => delivery.name === doNo)
    await openPopup(
      html`
        <delivery-order-info
          ._doNo="${doNo}"
          ._truckNo="${targetDO?.truckNo}"
          .data="${targetDO}"
          @undo=${async e => {
            showToast({ type: 'success', message: 'undo loading completed' })
            // Reload all data and counts to update banner and button visibility
            await this.reload()
          }}
        ></delivery-order-info>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.info')
      }
    )
  }

  // Action Methods
  async _sort(e) {
    if (e.keyCode !== 13) return
    e.preventDefault()
    try {
      await this.validateSorting()
      let selectedData = this.qcData.filter(data => this.qcDataView.selectedItems.includes(data.id))

      // If isReleaseGoodScan is true, use release good name; otherwise use binLocationName if QC SKU mode
      const releaseGoodNo = this.worksheet?.isReleaseGoodScan
        ? this.worksheet?.releaseGood?.name
        : this._useQcSku
        ? this.worksheet?.binLocationName
        : this.worksheet?.releaseGood?.name

      const response = await client.mutate({
        mutation: SORT_ITEM,
        variables: {
          releaseGoodNo,
          productBarcode: this.productBarcodeInput.value,
          qty: parseFloat(this.qtyInput.value),
          worksheetDetails: selectedData,
          toteNo: this._enableToteScanning && this.toteNo !== '' ? this.toteNo : null
        }
      })

      if (!response.errors) {
        this.productBarcodeInput.value = ''
        this.qtyInput.value = ''
        this._focusOnProductBarcodeInput()

        // Fetch data for both QC and LOADING tabs to ensure banner condition is accurate
        // If isReleaseGoodScan is true, use release good name; otherwise use binLocationName if QC SKU mode
        const orderNoForRefresh = this.worksheet?.isReleaseGoodScan
          ? this.worksheet?.releaseGood?.name
          : this._useQcSku
          ? this.worksheet?.binLocationName
          : this.worksheet?.releaseGood?.name
        const useQcSkuForRefresh = this._useQcSku && !this.worksheet?.isReleaseGoodScan

        const [qcData, loadingData] = await Promise.all([
          this.fetchData(orderNoForRefresh, 'qc', useQcSkuForRefresh),
          this.fetchData(orderNoForRefresh, 'loading', useQcSkuForRefresh)
        ])

        // QC tab: items that still need QC (sortedQty < pickedQty)
        this.qcData = qcData.filter(data => data?.sortedQty < data?.pickedQty) || []
        // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
        this.loadingData =
          loadingData.filter(data => data?.sortedQty >= data?.pickedQty && data.status != WORKSHEET_STATUS.DONE.value) || []

        // Update QC banner state after data changes
        if (this._updateQcBannerState) {
          await this._updateQcBannerState()
        }
      }
    } catch (e) {
      showToast(e)
    }
  }

  async validateSorting() {
    if (!this.productBarcodeInput.value) {
      throw new Error(i18next.t('text.product_barcode_is_empty'))
    } else {
      this._focusOnQtyInput()
    }
    if (!this.qtyInput.value || parseFloat(this.qtyInput.value) <= 0) {
      throw new Error(i18next.t('text.invalid_qty'))
    }
  }

  async load() {
    try {
      let data = this.loadingData.filter(data => this.loadingDataView.selectedItems.includes(data.id))
      if (data.length < 1) data = this.loadingData

      const ownCollection = this._currentReleaseGood?.ownCollection ? true : false
      let vehicleNo = ownCollection ? this.truckNoInput?.value || '' : this.warehouseTruckInput?.value || ''

      if (!this._currentReleaseGood?.releaseGood?.name) {
        throw new Error(i18next.t('text.no_release_order_selected'))
      }

      const loadData = {
        releaseGoodNo: this._currentReleaseGood.releaseGood.name,
        vehicleNo,
        ownCollection,
        totalPalletQty: parseInt(this.totalPalletQtyInput ? this.totalPalletQtyInput.value : 0)
      }

      openPopup(
        html`
          <load-by-qty
            ._data=${data}
            ._executionData=${loadData}
            @loading=${async e => {
              showToast({ type: 'success', message: 'loaded successfully' })
              // Reload all data and counts to update banner and button visibility
              await this.reload()
            }}
          ></load-by-qty>
        `,
        {
          backdrop: true,
          size: 'large',
          title: i18next.t('title.load_by_qty')
        }
      )
    } catch (e) {
      showToast(e)
    }
  }

  async completeLoad() {
    try {
      if (!this._executionData?.worksheetId) {
        console.error('Missing worksheetId in completeLoad:', {
          executionData: this._executionData,
          worksheet: this.worksheet
        })
        throw new Error('Worksheet ID is required for completing load')
      }

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

      if (!result.value) return

      const response = await client.mutate({
        mutation: COMPLETE_LOAD,
        variables: {
          orderId: this._currentReleaseGood?.releaseGood.name,
          worksheetId: this._executionData.worksheetId
        }
      })

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

        // --- Clear previous order and UI data ---
        this._currentReleaseGood = null
        this.loadedData = []
        this.loadingData = []
        this.qcData = []
        this.worksheet = null
        this.worksheetDetails = null
        this._tabCounts = null
        this._hasRefWorksheetId = false
        this.toteNo = ''
        this.state = null

        setTimeout(async () => {
          try {
            // If isReleaseGoodScan is true, use release good name; otherwise use binLocationName if QC SKU mode
            const orderNo = this.worksheet?.isReleaseGoodScan
              ? this.worksheet?.releaseGood?.name
              : this._useQcSku
              ? this.worksheet?.binLocationName
              : this.worksheet?.releaseGood?.name
            if (!orderNo) {
              resetContext(this.parent)
              return
            }

            const response = await client.query({
              query: FIND_LOADABLE_RELEASE_GOOD,
              variables: {
                orderNo,
                tabName: this.state?.activeTab || TAB_TYPES.LOADING
              },
              context: gqlContext(),
              fetchPolicy: 'network-only' // Always fetch fresh data to show updated item counts
            })

            if (!response.errors) {
              const orders = response.data.findLoadableReleaseGood
              // Filter out orders that are already DONE
              const activeOrders = orders.filter(order => order.status !== 'DONE')
              if (activeOrders.length) {
                await this.selectReleaseOrderPopup(activeOrders, this.state?.activeTab)
              } else {
                // No more active orders, reset to initial scan screen
                resetContext(this.parent)
              }
            }
          } catch (e) {
            console.error('Error finding next release order:', e)
            resetContext(this.parent)
          }
        }, 3000)
      }
    } catch (e) {
      showToast(e)
    }
  }

  async partialLoad() {
    try {
      if (!this._executionData?.worksheetId) {
        console.error('Missing worksheetId in partialLoad:', {
          executionData: this._executionData,
          worksheet: this.worksheet
        })
        throw new Error('Worksheet ID is required for partial loading')
      }

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

      if (!result.value) return

      const response = await client.mutate({
        mutation: COMPLETE_LOAD_PARTIALLY,
        variables: {
          orderId: this._currentReleaseGood?.releaseGood.name,
          worksheetId: this._executionData.worksheetId
        }
      })

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

        // --- Clear previous order and UI data ---
        this._currentReleaseGood = null
        this.loadedData = []
        this.loadingData = []
        this.qcData = []
        this.worksheet = null
        this.worksheetDetails = null
        this._tabCounts = null
        this._hasRefWorksheetId = false
        this.toteNo = ''
        this.state = null

        setTimeout(async () => {
          try {
            // If isReleaseGoodScan is true, use release good name; otherwise use binLocationName if QC SKU mode
            const orderNo = this.worksheet?.isReleaseGoodScan
              ? this.worksheet?.releaseGood?.name
              : this._useQcSku
              ? this.worksheet?.binLocationName
              : this.worksheet?.releaseGood?.name
            if (!orderNo) {
              resetContext(this.parent)
              return
            }

            const response = await client.query({
              query: FIND_LOADABLE_RELEASE_GOOD,
              variables: {
                orderNo,
                tabName: this.state?.activeTab || TAB_TYPES.LOADING
              },
              context: gqlContext(),
              fetchPolicy: 'network-only' // Always fetch fresh data to show updated item counts
            })

            if (!response.errors) {
              const orders = response.data.findLoadableReleaseGood
              // Filter out orders that are already DONE
              const activeOrders = orders.filter(order => order.status !== 'DONE')
              if (activeOrders.length) {
                await this.selectReleaseOrderPopup(activeOrders, this.state?.activeTab)
              } else {
                // No more active orders, reset to initial scan screen
                resetContext(this.parent)
              }
            }
          } catch (e) {
            console.error('Error finding next release order:', e)
            resetContext(this.parent)
          }
        }, 3000)
      }
    } catch (e) {
      showToast(e)
    }
  }

  async warehouseReturn() {
    let data = this.loadingData.filter(data => this.loadingDataView.selectedItems.includes(data.id))
    if (data.length < 1) data = this.loadingData

    openPopup(
      html`
        <warehouse-return-popup
          ._data=${data}
          ._releaseGoodNo=${this.worksheet?.releaseGood?.name}
          @warehouse-return=${async e => {
            showToast({ type: 'success', message: 'Returning worksheet generated' })
            this.loadingData = await this.fetchData(this.worksheet?.releaseGood?.name, 'loading', this._useQcSku)
            // Loading tab: items that have completed QC (sortedQty >= pickedQty) and are not yet loaded
            this.loadingData = (this.loadingData || []).filter(
              data => data?.sortedQty >= data?.pickedQty && data?.status !== WORKSHEET_STATUS.DONE.value
            )
          }}
        ></warehouse-return-popup>
      `,
      {
        title: i18next.t('title.warehouse_return'),
        size: 'large',
        backdrop: true
      }
    )
  }

  async fetchData(orderNo, tab, useQcSku) {
    try {
      // Use FIND_LOADABLE_RELEASE_GOOD_BY_BIN for both QC and LOADING tabs when useQcSku is true (bin scan)
      if (useQcSku && !this.worksheet?.isReleaseGoodScan) {
        const response = await client.query({
          query: FIND_LOADABLE_RELEASE_GOOD_BY_BIN,
          variables: { orderNo }
        })

        if (!response.errors) {
          const data = response.data.findLoadableReleaseGoodByBin.worksheetDetailInfos

          // Check if any order inventory has refWorksheetId
          this._hasRefWorksheetId = data.some(
            item => item?.relatedOrderInv?.refWorksheetId != null && item?.relatedOrderInv?.refWorksheetId !== ''
          )
          // Combine same products for better UX display, but preserve all order inventory references
          // so backend can distribute loadedQty across multiple order inventories
          const combined = combineSameProductsWithAllRefs(data)
          return combined
        }
      } else {
        const response = await client.query({
          query: LOADING_WORKSHEET_V2,
          variables: { orderNo, filter: tab }
        })

        if (!response.errors) {
          const worksheetInfo = response.data.loadingWorksheetv2.worksheetInfo

          if (!worksheetInfo?.worksheet?.id) {
            console.error('Missing worksheet ID in API response:', worksheetInfo)
            throw new Error('Worksheet information is incomplete')
          }

          this.worksheet = {
            ...this.worksheet,
            releaseGood: worksheetInfo.releaseGood,
            bizplaceName: worksheetInfo.bizplaceName,
            startedAt: worksheetInfo.startedAt,
            worksheet: worksheetInfo.worksheet,
            binLocationName: this.worksheet?.binLocationName || orderNo
          }

          this._executionData = {
            ...this._executionData,
            orderId: worksheetInfo.releaseGood?.id,
            worksheetId: worksheetInfo.worksheet.id
          }

          const worksheetDetailInfos = response.data.loadingWorksheetv2.worksheetDetailInfos

          // Check if any order inventory has refWorksheetId
          this._hasRefWorksheetId = worksheetDetailInfos.some(
            item => item?.relatedOrderInv?.refWorksheetId != null && item?.relatedOrderInv?.refWorksheetId !== ''
          )

          return worksheetDetailInfos
        }
      }
    } catch (e) {
      showToast(e)
    }
  }
}

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

/**
 * Combines same products for UI display while preserving ALL order inventory references.
 * This allows the backend to distribute loadedQty across multiple order inventories.
 *
 * @param {Array} data - Array of worksheet detail items
 * @returns {Array} Combined items with aggregated quantities and allRelatedOrderInvs array
 */
function combineSameProductsWithAllRefs(data) {
  const combinedMap = new Map()

  data.forEach(item => {
    // Include status in key to keep LOADING and LOADED items separate
    const key = `${item.relatedOrderInv?.product?.sku}_${item.batchId}_${item.expirationDate}_${item.status}`

    if (combinedMap.has(key)) {
      const existing = combinedMap.get(key)
      existing.releaseQty += item.releaseQty
      existing.pickedQty += item.pickedQty
      existing.sortedQty += item.sortedQty
      // Store all order inventory references for backend to process
      existing.allRelatedOrderInvs.push({
        id: item.relatedOrderInv?.id,
        pickedQty: item.pickedQty,
        sortedQty: item.sortedQty,
        releaseQty: item.releaseQty
      })
    } else {
      combinedMap.set(key, {
        ...item,
        // Initialize array with first order inventory reference
        allRelatedOrderInvs: [
          {
            id: item.relatedOrderInv?.id,
            pickedQty: item.pickedQty,
            sortedQty: item.sortedQty,
            releaseQty: item.releaseQty
          }
        ]
      })
    }
  })

  return Array.from(combinedMap.values())
}

async function fetchDeliveryOrders(releaseGoodName) {
  try {
    const response = await client.query({
      query: GET_DELIVERY_ORDERS,
      variables: { releaseGoodNo: releaseGoodName }
    })

    if (!response.errors) {
      return response.data.getDeliveryOrders
    }
  } catch (e) {
    showToast(e)
  }
}
