import '@things-factory/form-ui'
import '@things-factory/grist-ui'
import '@things-factory/worksheet-ui'
import '../batch-picking-popup'
import '../print-airway-bill-popup'
import '../../../outbound/generate-worksheet-popup'

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

import { GhostPrint } from '@operato/ghost-print'
import { DirectPrinter } from '@things-factory/barcode-base'
import { getCodeByName } from '@things-factory/code-base'
import { i18next, localize } from '@things-factory/i18n-base'
import { openPopup, openOverlay } from '@things-factory/layout-base'
import { client, navigate, PageView, store } from '@things-factory/shell'
import { CommonButtonStyles, ScrollbarStyles } from '@things-factory/styles'
import { isMobileDevice, sleep } from '@things-factory/utils'

import { fetchSettingRule } from '../../../../fetch-setting-value'
import { INTENALIZED_AWB_SETTING_KEY, ORDER_STATUS } from '../../../constants'

import { applyParamsToFields } from '../../../../util'

const HEADER_JSON = {
  'Content-Type': 'application/json',
  Accept: 'application/json'
}

function formatLocalDate(date = new Date()) {
  const year = date.getFullYear()
  const month = String(date.getMonth() + 1).padStart(2, '0')
  const day = String(date.getDate()).padStart(2, '0')
  return `${year}-${month}-${day}`
}

class B2cOrderRequests extends connect(store)(localize(i18next)(PageView)) {
  static get styles() {
    return [
      ScrollbarStyles,
      css`
        :host {
          display: flex;
          flex-direction: column;
          overflow: hidden;
        }

        search-form {
          overflow: visible;
        }

        data-grist {
          overflow-y: auto;
          flex: 1;
        }
      `
    ]
  }

  static get properties() {
    return {
      _internalizedAWBLabel: Object,
      _searchFields: Array,
      config: Object,
      data: Object,
      _betaSetting: Boolean
    }
  }

  stateChanged(state) {
    let internalizedAWBSetting = state.dashboard[INTENALIZED_AWB_SETTING_KEY]
    this._internalizedAWBLabel = (internalizedAWBSetting && internalizedAWBSetting.board) || {}
  }

  render() {
    return html`
      <search-form id="search-form" .fields=${this._searchFields} @submit=${e => this.dataGrist.fetch()}></search-form>

      <data-grist
        .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
        .config=${this.config}
        .fetchHandler="${this.fetchHandler.bind(this)}"
      ></data-grist>
    `
  }

  get context() {
    let actions = [
      {
        title: i18next.t('button.print_invoice'),
        action: this._printInvoices.bind(this),
        ...CommonButtonStyles.label
      },
      {
        title: i18next.t('button.generate_packing_list'),
        action: this._generatePackingList.bind(this),
        ...CommonButtonStyles.label
      },
      {
        title: i18next.t('button.generate_worksheet'),
        action: this._betaSetting ? this._openWorksheetOverlay.bind(this) : this._openGenerateWorksheetPopup.bind(this),
        ...CommonButtonStyles.activate
      }
    ]

    if (Object.keys(this._internalizedAWBLabel).length) {
      actions = [
        {
          title: i18next.t('button.print_internal_awb'),
          action: this._printInternalAWB.bind(this),
          ...CommonButtonStyles.label
        },
        ...actions
      ]
    }

    return {
      title: i18next.t('title.release_order_requests'),
      help: 'operato-wms/release-order/b2c-order-requests',
      actions,
      exportable: {
        name: i18next.t('title.release_order_requests'),
        data: this._exportableData.bind(this)
      }
    }
  }

  pageUpdated(changes, lifecycle) {
    if (this.active) {
      let { params } = lifecycle
      if (params && this._searchFields) {
        if (params.range) {
          const today = new Date()
          params.toDate = formatLocalDate(today)
          switch (params.range) {
            case '1d':
              {
                const from = new Date()
                from.setDate(from.getDate() - 1)
                params.fromDate = formatLocalDate(from)
              }
              break
            case '1w':
              {
                const from = new Date()
                from.setDate(from.getDate() - 7)
                params.fromDate = formatLocalDate(from)
              }
              break
            case '1m':
              {
                const from = new Date()
                from.setMonth(from.getMonth() - 1)
                params.fromDate = formatLocalDate(from)
              }
              break
            default:
              break
          }
        }
        const updatedFields = applyParamsToFields(this._searchFields, params)
        this._searchFields = updatedFields
        const searchForm = this.searchForm
        if (searchForm) {
          searchForm.fields = updatedFields
        }
      }
      this.dataGrist.fetch()
    }
  }

  async pageInitialized() {
    const _enablePrintSCAwb = await fetchSettingRule('enable-print-sellercraft-airway-bill')
    const _orderStatus = await getCodeByName('RO_REQUESTS_STATUS')
    const _partners = await this._fetchPartners()
    const _shippingProviders = await this._fetchShippingProviders()
    this._betaSetting = await fetchSettingRule('enable-beta-feature')

    this._searchFields = [
      {
        label: i18next.t('field.order_info'),
        name: 'name',
        type: 'text',
        props: { searchOper: 'i_like' }
      },
      {
        label: i18next.t('field.company'),
        name: 'bizplaceId',
        type: 'select',
        options: [
          { value: '' },
          ..._partners
            .map(partner => {
              return {
                name: `${partner.name} ${partner.description ? `(${partner.description})` : ''}`,
                value: partner.id
              }
            })
            .sort(this._compareValues('name', 'asc'))
        ],
        props: { searchOper: 'eq' }
      },
      {
        label: i18next.t('field.status'),
        name: 'status',
        type: 'select',
        options: [
          { value: '' },
          ..._orderStatus
            .filter(res => {
              return res.name !== 'LOADING'
            })
            .map(status => {
              return { name: i18next.t(`label.${status.description}`), value: status.name }
            })
        ],
        props: { searchOper: 'eq' }
      },
      {
        label: i18next.t('field.from'),
        name: 'fromDate',
        type: 'date',
        props: {
          searchOper: 'gte'
        },
        value: (() => {
          let date = new Date()
          date.setMonth(date.getMonth() - 1)
          return formatLocalDate(date)
        })(),
        handlers: { change: this._modifyDateRange.bind(this) }
      },
      {
        label: i18next.t('field.to'),
        name: 'toDate',
        type: 'date',
        props: {
          searchOper: 'lte',
          min: (() => {
            let date = new Date()
            date.setMonth(date.getMonth() - 1)
            return formatLocalDate(date)
          })(),
          max: (() => {
            let date = new Date()
            date.setDate(date.getDate())
            return formatLocalDate(date)
          })()
        },
        value: formatLocalDate(new Date())
      },
      {
        label: i18next.t('field.transporter'),
        name: 'transporter',
        type: 'select',
        props: {
          searchOper: 'i_like'
        },
        options: [
          { value: '' },
          ..._shippingProviders.map(item => {
            return {
              name: item.transporter,
              value: item.transporter == 'EFMX' ? 'EMS' : item.transporter
            }
          })
        ]
      },
      {
        label: i18next.t('field.no_of_items'),
        name: 'noOfItems',
        type: 'select',
        props: {
          searchOper: 'gte'
        },
        options: [{ value: '' }, { name: 'Single Item', value: 1 }, { name: 'Multiple Item', value: 2 }]
      },
      {
        label: i18next.t('field.product_info'),
        name: 'sku',
        type: 'text',
        props: { searchOper: 'i_like' }
      },
      {
        label: i18next.t('field.platform'),
        name: 'platformCode',
        type: 'select',
        props: {
          searchOper: 'eq'
        },
        options: [
          { value: '' },
          { name: 'Lazada', value: 'LZD' },
          { name: 'Shopee', value: 'SHP' },
          { name: 'Shopify', value: 'SPF' },
          { name: 'Tiktok', value: 'TTK' },
          { name: 'Magento', value: 'MGT' },
          { name: 'Generic', value: 'GENERIC' }
        ]
      },
      {
        label: i18next.t('field.order_remark'),
        name: 'orderRemark',
        type: 'checkbox',
        props: { searchOper: 'eq' },
        attrs: ['indeterminate']
      },
      {
        label: i18next.t('field.manifested'),
        name: 'manifested',
        type: 'checkbox',
        props: { searchOper: 'eq' },
        attrs: ['indeterminate']
      },
      {
        label: i18next.t('field.lmd'),
        name: 'lmdOption',
        type: 'checkbox',
        props: { searchOper: 'eq' },
        attrs: ['indeterminate']
      },
      {
        label: i18next.t('field.priority_delivery'),
        name: 'priorityDelivery',
        type: 'checkbox',
        props: { searchOper: 'eq' },
        attrs: ['indeterminate']
      }
    ]

    this.config = {
      rows: { selectable: { multiple: true }, appendable: false },
      list: { fields: ['name', 'bizplace', 'releaseDate', 'status', 'updatedAt'] },
      pagination: { limit: 100, pages: [20, 50, 100, 200, 500] },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        { type: 'gutter', gutterName: 'row-selector', multiple: true },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'reorder',
          handlers: {
            click: (columns, data, column, record, rowIndex) => {
              navigate(`b2c_release_order_detail/${record.name}`)
            }
          }
        },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'receipt',
          hidden: !_enablePrintSCAwb,
          handlers: {
            click: async (columns, data, column, record, rowIndex) => {
              if (record.status == 'DONE') {
                this._openAwbPopup({ releaseGoodId: record.id, bizplaceId: record.bizplace.id })
              }
            }
          }
        },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'assignment_return',
          handlers: {
            click: (columns, data, column, record, rowIndex) => {
              const { status, name } = record
              if (status === ORDER_STATUS.DONE.value) {
                navigate(`create_return_order?releaseOrderNo=${name}`)
              } else {
                this._showToast({ type: 'error', message: 'Only "DONE" order can create return order.' })
              }
            }
          }
        },
        {
          type: 'boolean',
          name: 'orderRemark',
          header: i18next.t('field.order_remark'),
          imex: { header: i18next.t('field.order_remark'), key: 'orderRemark', width: 20, type: 'string' },
          sortable: true,
          width: 100
        },
        {
          type: 'boolean',
          name: 'priorityDelivery',
          header: i18next.t('field.priority_delivery'),
          imex: { header: i18next.t('field.priority_delivery'), key: 'priorityDelivery', width: 20, type: 'string' },
          sortable: true,
          width: 120
        },
        {
          type: 'string',
          name: 'name',
          header: i18next.t('field.ro'),
          imex: { header: i18next.t('field.ro'), key: 'name', width: 25, type: 'string' },
          sortable: true,
          width: 150
        },
        {
          type: 'object',
          name: 'bizplace',
          header: i18next.t('field.company'),
          imex: { header: i18next.t('field.company'), key: 'bizplace.name', width: 25, type: 'string' },
          sortable: true,
          width: 200
        },
        {
          type: 'object',
          name: 'draftReleaseGood',
          header: i18next.t('field.draft_order_no'),
          imex: { header: i18next.t('field.draft_order_no'), key: 'draftReleaseGood.name', width: 30, type: 'string' },
          sortable: true,
          width: 250
        },
        {
          type: 'string',
          name: 'refNo',
          header: i18next.t('field.ref_no'),
          imex: { header: i18next.t('field.ref_no'), key: 'refNo', width: 25, type: 'string' },
          sortable: true,
          width: 160
        },
        {
          type: 'string',
          name: 'refNo2',
          header: i18next.t('field.ref_no_2'),
          imex: { header: i18next.t('field.ref_no_2'), key: 'refNo2', width: 25, type: 'string' },
          sortable: true,
          width: 160
        },
        {
          type: 'string',
          name: 'refNo3',
          header: i18next.t('field.ref_no_3'),
          imex: { header: i18next.t('field.ref_no_3'), key: 'refNo3', width: 25, type: 'string' },
          sortable: true,
          width: 160
        },
        {
          type: 'string',
          name: 'postalCode',
          header: i18next.t('field.postal_code'),
          imex: { header: i18next.t('field.postal_code'), key: 'postalCode', width: 25, type: 'string' },
          sortable: true,
          width: 100
        },
        {
          type: 'boolean',
          name: 'lmdOption',
          header: i18next.t('field.lmd'),
          imex: { header: i18next.t('field.lmd'), key: 'lmdOption', width: 20, type: 'string' },
          sortable: true,
          width: 100
        },
        {
          type: 'boolean',
          name: 'codOption',
          header: i18next.t('field.cod'),
          imex: { header: i18next.t('field.cod'), key: 'codOption', width: 20, type: 'string' },
          sortable: true,
          width: 100
        },
        {
          type: 'string',
          name: 'transporter',
          header: i18next.t('field.transporter'),
          imex: { header: i18next.t('field.transporter'), key: 'transporter', width: 25, type: 'string' },
          sortable: true,
          width: 120
        },
        {
          type: 'string',
          name: 'platformCode',
          header: i18next.t('field.platform'),
          imex: { header: i18next.t('field.platform'), key: 'platformCode', width: 25, type: 'string' },
          sortable: true,
          width: 120
        },
        {
          type: 'date',
          name: 'releaseDate',
          header: i18next.t('field.release_date'),
          imex: { header: i18next.t('field.release_date'), key: 'releaseDate', width: 25, type: 'string' },
          sortable: true,
          width: 120
        },
        {
          type: 'string',
          name: 'status',
          header: i18next.t('field.status'),
          imex: { header: i18next.t('field.status'), key: 'status', width: 25, type: 'string' },
          sortable: true,
          width: 120
        },
        {
          type: 'integer',
          name: 'noOfItems',
          header: i18next.t('field.no_of_items'),
          imex: { header: i18next.t('field.no_of_items'), key: 'noOfItems', width: 25, type: 'integer' },
          sortable: true,
          width: 120
        },
        {
          type: 'datetime',
          name: 'acceptedAt',
          header: i18next.t('field.accepted_at'),
          imex: { header: i18next.t('field.accepted_at'), key: 'acceptedAt', width: 25, type: 'datetime' },
          sortable: true,
          width: 160
        },
        {
          type: 'datetime',
          name: 'updatedAt',
          header: i18next.t('field.updated_at'),
          imex: { header: i18next.t('field.updated_at'), key: 'updatedAt', width: 25, type: 'datetime' },
          sortable: true,
          width: 160
        },
        {
          type: 'object',
          name: 'acceptedBy',
          header: i18next.t('field.accepted_by'),
          imex: { header: i18next.t('field.accepted_by'), key: 'acceptedBy.name', width: 25, type: 'string' },
          sortable: true,
          width: 200
        },
        {
          type: 'object',
          name: 'updater',
          header: i18next.t('field.updated_by'),
          imex: { header: i18next.t('field.updated_by'), key: 'updater.name', width: 25, type: 'string' },
          sortable: true,
          width: 200
        },
        {
          type: 'string',
          name: 'attentionTo',
          header: i18next.t('field.receiver_name'),
          imex: { header: i18next.t('field.receiver_name'), key: 'attentionTo', width: 25, type: 'string' },
          width: 200,
          hidden: true
        },
        {
          type: 'string',
          name: 'attentionCompany',
          header: i18next.t('field.receiver_company'),
          imex: { header: i18next.t('field.receiver_company'), key: 'attentionCompany', width: 25, type: 'string' },
          width: 200,
          hidden: true
        },
        {
          type: 'string',
          name: 'deliveryAddress1',
          header: i18next.t('field.delivery_address'),
          imex: { header: i18next.t('field.delivery_address'), key: 'deliveryAddress1', width: 75, type: 'string' },
          width: 200,
          hidden: true
        },
        {
          type: 'string',
          name: 'city',
          header: i18next.t('field.city'),
          imex: { header: i18next.t('field.city'), key: 'city', width: 25, type: 'string' },
          width: 200,
          hidden: true
        },
        {
          type: 'string',
          name: 'state',
          header: i18next.t('field.state'),
          imex: { header: i18next.t('field.state'), key: 'state', width: 25, type: 'string' },
          width: 200,
          hidden: true
        },
        {
          type: 'string',
          name: 'country',
          header: i18next.t('field.country'),
          imex: { header: i18next.t('field.country'), key: 'country', width: 20, type: 'string' },
          width: 200,
          hidden: true
        },
        {
          type: 'string',
          name: 'phone1',
          header: `${i18next.t('field.contact')} 1`,
          imex: { header: `${i18next.t('field.contact')} 1`, key: 'phone1', width: 25, type: 'string' },
          width: 200,
          hidden: true
        },
        {
          type: 'string',
          name: 'phone2',
          header: `${i18next.t('field.contact')} 2`,
          imex: { header: `${i18next.t('field.contact')} 2`, key: 'phone2', width: 25, type: 'string' },
          width: 200,
          hidden: true
        },
        {
          type: 'string',
          name: 'email',
          header: i18next.t('field.email'),
          imex: { header: i18next.t('field.email'), key: 'email', width: 25, type: 'string' },
          width: 200,
          hidden: true
        }
      ]
    }
  }

  get searchForm() {
    return this.shadowRoot.querySelector('search-form')
  }

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

  get _toDateInput() {
    return this.searchForm.shadowRoot.querySelector('input[name=toDate]')
  }

  async fetchHandler({ page, limit, sorters = [] }) {
    const response = await client.query({
      query: gql`
        query releaseGoodRequests($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
          releaseGoodRequests(filters: $filters, pagination: $pagination, sortings: $sortings) {
            items {
              id
              trackingNo
              deliveryAddress1
              deliveryAddress2
              deliveryAddress3
              deliveryAddress4
              deliveryAddress5
              attentionTo
              attentionCompany
              city
              state
              postalCode
              country
              phone1
              phone2
              email
              postalCode
              marketplaceOrderStatus
              transporter
              orderRemark
              bizplace {
                id
                name
                description
              }
              draftReleaseGood {
                id
                name
              }
              name
              releaseDate
              status
              refNo
              refNo2
              refNo3
              noOfItems
              createdAt
              updatedAt
              acceptedAt
              acceptedBy {
                id
                name
                description
              }
              updater {
                id
                name
                description
              }
              airwayBill
              trackingNo
              lmdOption
              codOption
              priorityDelivery
              platformCode
            }
            total
          }
        }
      `,
      variables: {
        filters: [...(await this.searchForm.getQueryFilters()), { name: 'type', operator: 'eq', value: 'b2c' }],
        pagination: { page, limit },
        sortings: sorters
      }
    })

    if (!response.errors) {
      return {
        total: response.data.releaseGoodRequests.total || 0,
        records: (response.data.releaseGoodRequests.items || []).map(item => {
          let transporterData = item?.transporter == 'EMS' ? 'EFMX' : item.transporter
          let platformCode = item?.platformCode == 'MGT' ? 'Magento' : item.platformCode
          return {
            ...item,
            bizplaceName: item.bizplace.name,
            transporter: transporterData,
            platformCode: platformCode
          }
        })
      }
    }
  }

  async _fetchPartners() {
    const filters = []
    const response = await client.query({
      query: gql`
        query partnersBizplaces($filters: [Filter!]) {
          partnersBizplaces(filters: $filters) {
            items {
              id
              name
              description
            }
          }
        }
      `,
      variables: { filters }
    })

    if (!response.errors) {
      return response.data.partnersBizplaces.items
    }
  }

  async _fetchShippingProviders() {
    const response = await client.query({
      query: gql`
        query shippingProviders {
          shippingProviders {
            items {
              transporter
            }
          }
        }
      `
    })

    if (!response.errors) {
      return response.data.shippingProviders.items
    }
  }

  _generatePackingList() {
    try {
      if (this.dataGrist.selected?.length == 0) {
        throw new Error(i18next.t('text.select_orders_to_generate_merged_packing_list_for_printing'))
      }

      let roNos =
        this.dataGrist.selected
          .filter(
            record =>
              record.status === ORDER_STATUS.PENDING_WORKSHEET.value ||
              record.status === ORDER_STATUS.READY_TO_PICK.value ||
              record.status === ORDER_STATUS.PICKING.value ||
              record.status === ORDER_STATUS.PACKING.value ||
              record.status === ORDER_STATUS.LOADING.value
          )
          .map(record => record.name) || []

      if (roNos?.length) {
        const searchParams = new URLSearchParams()
        searchParams.append('orderNo', roNos)
        navigate(`bulk_print_packing_list?${searchParams.toString()}`)
      } else {
        throw new Error(i18next.t('text.order_status_not_qualified_for_bulk_printing_packing_list'))
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _printInvoices() {
    try {
      if (this.dataGrist.selected && this.dataGrist.selected.length > 0) {
        var timezoneOffSet = new Date().getTimezoneOffset().toString()
        let url = new URL(window.location)

        let data = await Promise.all(
          this.dataGrist.selected.map(async x => {
            return x.id
          })
        )

        let response = await fetch(`/view_invoices/${timezoneOffSet}`, {
          method: 'POST',
          credentials: 'include',
          headers: HEADER_JSON,
          body: JSON.stringify({ roIds: data })
        }).then(function (response) {
          return response.text()
        })

        this._callPrintDialog(response)

        // navigate(`print_invoices/${data}`)
      } else {
        throw new Error(`Please select a record.`)
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _printInternalAWB() {
    const records = this.dataGrist.selected
    var labelId = this._internalizedAWBLabel && this._internalizedAWBLabel.id

    if (!labelId) {
      this._showToast({
        type: 'error',
        message: `${i18next.t('text.no_label_setting_was_found')}. ${i18next.t('text.please_check_your_setting')}`
      })
    } else {
      for (var record of records) {
        var searchParams = new URLSearchParams()
        searchParams.append('createdAt', this._getDateString(new Date(parseInt(record.createdAt))))
        searchParams.append('refNo', record.refNo)
        searchParams.append('name', record.name)
        searchParams.append('noOfItems', record.noOfItems)
        searchParams.append('postalCode', record.postalCode)
        searchParams.append('attentionTo', record.attentionTo)
        searchParams.append('deliveryAddress1', record.deliveryAddress1 || '')
        searchParams.append('deliveryAddress2', record.deliveryAddress2 || '')
        searchParams.append('deliveryAddress3', record.deliveryAddress3 || '')
        searchParams.append('deliveryAddress4', record.deliveryAddress4 || '')
        searchParams.append('deliveryAddress5', record.deliveryAddress5 || '')
        searchParams.append('phone1', record.phone1 || '')
        searchParams.append('phone2', record.phone2 || '')
        searchParams.append('trackingNo', record.trackingNo)
        searchParams.append('transporter', record.transporter)
        // if (!record?.cartonId) throw new Error(i18next.t('text.selected_inventory_not_using_carton_label'))

        /* for pallet record mapping */
        // searchParams.append('carton', record.cartonId)
        // searchParams.append('customer', record.bizplace.name)

        try {
          const response = await fetch(`label-command/${labelId}?${searchParams.toString()}`, {
            method: 'GET'
          })

          if (response.status !== 200) {
            throw `Error : Can't get label command from server (response: ${response.status})`
          }

          var command = await response.text()

          if (!this.printer) {
            this.printer = new DirectPrinter()
          }

          await this.printer.print(command)
        } catch (e) {
          this._showToast(e)

          delete this.printer
          break
        }
      }
    }
  }

  async _callPrintDialog(contentSource, isSellerCraft = false) {
    if (isSellerCraft) {
      GhostPrint.print({
        src: contentSource,
        onfinish: () => {
          this._showToast({ message: i18next.t('text.document_has_been_generated') })
        }
      })
    } else {
      GhostPrint.print({
        srcdoc: contentSource,
        onfinish: () => {
          this._showToast({ message: i18next.t('text.document_has_been_generated') })
        }
      })
    }
  }

  _compareValues(key, order = 'asc') {
    return function innerSort(a, b) {
      if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
        return 0
      }

      const varA = typeof a[key] === 'string' ? a[key].toUpperCase() : a[key]
      const varB = typeof b[key] === 'string' ? b[key].toUpperCase() : b[key]

      let comparison = 0
      if (varA > varB) {
        comparison = 1
      } else if (varA < varB) {
        comparison = -1
      }
      return order === 'desc' ? comparison * -1 : comparison
    }
  }

  get _columns() {
    return this.config.columns
  }

  _openWorksheetOverlay() {
    // Load smart-batch-picking setting and expose globally for overlay to pass to popup
    fetchSettingRule('smart-batch-picking').then(val => {
      window.__SMART_BATCH_PICKING__ = String(val).toLowerCase() === 'true'
      window.__WORKSHEET_TYPE__ = 'b2c'
      openOverlay('context-toolbar-overlay', {
        template: html` <worksheet-overlay-template></worksheet-overlay-template> `
      })
    })
  }

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

  async _openGenerateWorksheetPopup() {
    try {
      if (this.dataGrist.selected.length <= 0)
        throw new Error(i18next.t('text.please_select_one_or_more_order_to_generate_worksheet'))

      let releaseGoodNos = []

      let readyToPick = this.dataGrist.selected.filter(itm => itm.status === ORDER_STATUS.PENDING_WORKSHEET.value)
      releaseGoodNos = readyToPick.map(record => record.name)

      if (releaseGoodNos.length <= 0)
        throw new Error(i18next.t('text.selected_release_order(s)_is_not_in_pending_worksheet_status'))

      await sleep(1010)
      openPopup(html` <generate-worksheet-popup .gristData=${this.dataGrist}></generate-worksheet-popup> `, {
        backdrop: true,
        size: 'medium',
        title: i18next.t('title.generate_picking_worksheet')
      })
    } catch (e) {
      this._showToast(e)
    }
  }

  _openAwbPopup({ releaseGoodId, bizplaceId }) {
    openPopup(
      html`
        <print-airway-bill-popup
          .releaseGoodId="${releaseGoodId}"
          .bizplaceId="${bizplaceId}"
        ></print-airway-bill-popup>
      `,
      {
        backdrop: true,
        size: 'small',
        title: i18next.t('title.awb')
      }
    )
  }

  _compareValues(key, order = 'asc') {
    return function innerSort(a, b) {
      if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
        return 0
      }

      const varA = typeof a[key] === 'string' ? a[key].toUpperCase() : a[key]
      const varB = typeof b[key] === 'string' ? b[key].toUpperCase() : b[key]

      let comparison = 0
      if (varA > varB) {
        comparison = 1
      } else if (varA < varB) {
        comparison = -1
      }
      return order === 'desc' ? comparison * -1 : comparison
    }
  }

  _modifyDateRange(e) {
    const fromDate = e.currentTarget.value

    if (this._toDateInput?.value < fromDate) this._toDateInput.value = fromDate

    let min = new Date(fromDate)
    let oneMonthRange = new Date(fromDate)
    oneMonthRange.setMonth(oneMonthRange.getMonth() + 1)
    oneMonthRange = formatLocalDate(oneMonthRange)

    min = formatLocalDate(min)

    this._toDateInput.min = min
    this._toDateInput.value = oneMonthRange
    this._toDateInput.max = oneMonthRange
  }

  _exportableData() {
    let records = []
    if (this.dataGrist.selected && this.dataGrist.selected.length > 0) {
      records = this.dataGrist.selected
    } else {
      records = this.dataGrist.data.records
    }

    var headerSetting = this.dataGrist._config.columns
      .filter(column => column.type !== 'gutter' /* && column.record !== undefined */ && column.imex !== undefined)
      .map(column => {
        return column.imex
      })

    var data = records.map(item => {
      return {
        id: item.id,
        ...this._columns
          .filter(column => column.type !== 'gutter' /* && column.record !== undefined */ && 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)
            if (column.type === 'datetime') {
              record[column.imex.key] = new Date(parseInt(record[column.imex.key])).toLocaleString()
            }
            return record
          }, {})
      }
    })
    return { header: headerSetting, data: data }
  }

  _getDateString(date) {
    return `${date.getDate()} ${date.toLocaleString('default', { month: 'short' })} ${date.getFullYear()}`
  }
}

window.customElements.define('b2c-order-requests', B2cOrderRequests)
