import '@things-factory/biz-ui'
import '@things-factory/grist-ui'
import '@things-factory/import-ui'
import '@things-factory/warehouse-ui'
import '../../components/select-product-popup'
import '../../components/select-transport-vehicle-popup'
import '../../order/vas-order/popup/vas-create-popup'

import gql from 'graphql-tag'
import { css, html } from 'lit-element'

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

import { fetchSettingRule } from '../../../fetch-setting-value'
import { VAS_BATCH_AND_PRODUCT_TYPE, VAS_BATCH_NO_TYPE, VAS_PRODUCT_TYPE } from '../../constants'

//vas --inbound
class CreateArrivalNotice extends localize(i18next)(PageView) {
  static get properties() {
    return {
      /**
       * @description
       * flag for whether use transportation from warehouse or not.
       * true =>
       */
      _ganNo: String,
      _importedOrder: Boolean,
      _checkTransport: Boolean,
      _checkTransportCrossDocking: Boolean,
      _ownTransport: Boolean,
      _warehouseTransport: Boolean,
      _enableTransportationServiceSetting: Boolean,
      _enableTransportForCustomClearanceSetting: Boolean,
      _disableTransport: Boolean,
      _crossDocking: Boolean,
      _hasContainer: Boolean,
      _looseItem: Boolean,
      _enableLotIdInput: Boolean,
      _files: Array,
      _disableCrossDockingSetting: Boolean,
      _hideCrossDockingSetting: Boolean,
      _strictProductSelection: Boolean,
      containerSizes: Array,
      productGristConfig: Object,
      vasGristConfig: Object,
      productData: Object,
      vasData: Object,
      _bizplaces: Object,
      _bizplaceId: String,
      _bizplaceName: String,
      _showAddProducts: Boolean,
      _vasExpanded: Boolean,
      _transportVehicle: String,
      enableDirectInbound: Boolean
    }
  }

  static get styles() {
    return [
      MultiColumnFormStyles,
      css`
        :host {
          display: flex;
          flex-direction: column;
          overflow-x: auto;
        }
        .container {
          flex: 1;
          display: flex;
          overflow: hidden;
          min-height: 50vh;
        }
        [options-wrap] * {
          vertical-align: middle;
        }
        [options-wrap] input[type='checkbox'] {
          left: 0;
        }
        [options-wrap] input[type='checkbox'] + label {
          margin-right: 20px;
          padding-left: 0;
        }
        [attachment-wrap] {
          display: flex;
          flex-direction: row;
          grid-column: span 24;
        }
        [attachment-wrap] label {
          width: 8%;
        }
        [attachment-wrap] file-uploader {
          flex: 1;
          margin: 0 var(--margin-wide) 0 var(--margin-narrow);
        }
        .grist {
          background-color: var(--main-section-background-color);
          display: flex;
          flex-direction: column;
          flex: 1;
          overflow-y: auto;
        }
        data-grist {
          overflow-y: hidden;
          flex: 1;
        }
        data-grist#vas-grist {
          padding-top: 0px;
          height: 100%;
        }
        h2 {
          padding: var(--subtitle-padding);
          font: var(--subtitle-font);
          color: var(--subtitle-text-color);
          border-bottom: var(--subtitle-border-bottom);
        }
        .grist h2 {
          margin: var(--grist-title-margin);
          border: var(--grist-title-border);
          font: var(--grist-title-font);
          color: var(--secondary-color);
        }
        .grist h2 mwc-icon {
          vertical-align: middle;
          margin: var(--grist-title-icon-margin);
          font-size: var(--grist-title-icon-size);
          color: var(--grist-title-icon-color);
        }
        h2 + data-grist {
          padding-top: var(--grist-title-with-grid-padding);
        }
        h2.vas-title {
          padding-bottom: 0px;
        }
        #button-add-products {
          padding: 10px 15px 0 0;
          float: right;
        }
        .button-div {
          margin: 0 0 -30px 0;
        }
        .vas[collapsable] {
          height: 40%;
        }
        .vas[collapsable][collapsed] {
          display: none;
        }
        [hidden] {
          display: none;
        }
        @media screen and (max-width: 460px) {
          [attachment-wrap] {
            grid-column: span 12;
          }
        }
        @media screen and (min-width: 1201px) and (max-width: 2000px) {
          [attachment-wrap] {
            grid-column: span 36;
          }
        }
        @media screen and (min-width: 2001px) {
          [attachment-wrap] {
            grid-column: span 48;
          }
          [attachment-wrap] label {
            width: 6%;
          }
        }
      `
    ]
  }

  get context() {
    this._vasExpanded = false
    return {
      title: i18next.t('title.create_arrival_notice'),
      actions: [
        {
          title: i18next.t('button.submit'),
          action: this._generateArrivalNotice.bind(this),
          ...CommonButtonStyles.submit
        }
      ],
      exportable: {
        name: i18next.t('title.create_arrival_notice'),
        data: this._exportableData.bind(this)
      },
      importable: {
        handler: this._importableData.bind(this)
      },
      help: 'operato-wms/arrival-notice/create-arrival-notice'
    }
  }

  render() {
    return html`
      <form name="arrivalNotice" class="multi-column-form" autocomplete="off">
        <fieldset>
          <legend>${i18next.t('title.arrival_notice')}</legend>

          <label>${i18next.t('label.options')}</label>
          <div options-wrap>
            <input
              id="ownTransport"
              type="checkbox"
              name="ownTransport"
              ?checked="${this._ownTransport}"
              ?disabled="${this._disableTransport}"
              @change="${e => {
        this._ownTransport = e.currentTarget.checked
        if (this._ownTransport) {
          this._warehouseTransportInput.checked = false
          this._warehouseTransport = false
        } else {
          this._warehouseTransportInput.checked = true
          this._warehouseTransport = true
        }
        this._validateTransport()
      }}"
            />
            <label for="ownTransport">${i18next.t('label.own_transport')}</label>

            <input
              id="warehouseTransport"
              type="checkbox"
              name="warehouseTransport"
              ?disabled="${this._disableTransport}"
              ?checked="${this._warehouseTransport}"
              @change="${e => {
        this._warehouseTransport = e.currentTarget.checked
        if (this._warehouseTransport) {
          this._ownTransportInput.checked = false
          this._ownTransport = false
        } else {
          this._ownTransportInput.checked = true
          this._ownTransport = true
        }
        this._validateTransport()
      }}"
            />
            <label for="warehouseTransport">${i18next.t('label.warehouse_transport')}</label>

            <input
              id="container"
              type="checkbox"
              name="container"
              ?checked="${this._hasContainer}"
              @change="${e => {
        this._hasContainer = e.currentTarget.checked
      }}"
              ?hidden="${!this._checkTransport}"
            />
            <label for="container" ?hidden="${!this._checkTransport}">${i18next.t('label.container')}</label>

            <input
              id="looseItem"
              type="checkbox"
              name="looseItem"
              ?checked="${this._looseItem}"
              ?hidden="${!this._checkTransport}"
            />
            <label for="looseItem" ?hidden="${!this._checkTransport}">${i18next.t('label.loose_item')}</label>

            <input
              id="importedOrder"
              type="checkbox"
              name="importCargo"
              ?checked="${this._importedOrder}"
              @change="${e => {
        this._importedOrder = e.currentTarget.checked
        this._validateImportCargo()
      }}"
              ?hidden="${!this._checkTransport}"
            />
            <label for="importedOrder" ?hidden="${!this._checkTransport}">${i18next.t('label.import_cargo')}</label>

            <input
              id="crossDocking"
              type="checkbox"
              name="crossDocking"
              ?checked="${this._crossDocking}"
              ?disabled="${this._disableCrossDockingSetting}"
              ?hidden="${!this._checkTransportCrossDocking}"
              @change="${e => (this._crossDocking = e.currentTarget.checked)}"
            />
            <label for="crossDocking" ?hidden="${!this._checkTransportCrossDocking}"
              >${i18next.t('label.cross_docking')}</label
            >
          </div>

          <label>${i18next.t('label.company')}</label>
          <input
            type="text"
            name="bizplaceName"
            value="${this._bizplaceName}"
            @click="${this._openBizplacesSelector.bind(this)}"
          />

          <label ?hidden="${!this._checkTransport}">${i18next.t('label.ref_no')}${`1`}</label>
          <input name="refNo" ?hidden="${!this._checkTransport}" />

          <label ?hidden="${!this._checkTransport}">${i18next.t('label.ref_no')}${`2`}</label>
          <input name="refNo2" ?hidden="${!this._checkTransport}" />

          <label ?hidden="${!this._checkTransport}">${i18next.t('label.ref_no')}${`3`}</label>
          <input name="refNo3" ?hidden="${!this._checkTransport}" />

          <label ?hidden="${!this._checkTransport}">${i18next.t('label.eta_date')}</label>
          <input name="etaDate" type="date" ?hidden="${!this._checkTransport}" required />

          <label ?hidden="${!this._hasContainer}">${i18next.t('label.container_no')}</label>
          <input ?hidden="${!this._hasContainer}" type="text" name="containerNo" />

          <label ?hidden="${!this._hasContainer}">${i18next.t('label.container_size')}</label>
          <select name="containerSize" ?hidden="${!this._hasContainer}">
            <option value="">--${i18next.t('label.please_select_a_container_size')}--</option>
            ${(this.containerSizes || []).map(
        containerSize =>
          html`
                  <option value="${containerSize && containerSize.name}">${containerSize && containerSize.name}</option>
                `
      )}
          </select>

          <label ?hidden="${!this._ownTransport}">${i18next.t('label.do_no')}</label>
          <input name="deliveryOrderNo" ?hidden="${!this._ownTransport}" />

          <label ?hidden="${!this._ownTransport}">${i18next.t('label.truck_no')}</label>
          <input ?hidden="${!this._ownTransport}" name="truckNo" />

          <label ?hidden="${!this._warehouseTransport}">${i18next.t('label.transport_vehicle')}</label>
          <input 
            ?hidden="${!this._warehouseTransport}" 
            name="transportVehicle"
            value=${this._transportVehicle}
            @click="${this._openTransportVehicleSelector.bind(this)}"
          />

          <div attachment-wrap>
            <label ?hidden="${!this._checkTransport}">${i18next.t('label.upload_documents')}</label>
            <file-uploader
              name="attachments"
              id="uploadDocument"
              label="${i18next.t('label.select_file')}"
              accept="*"
              multiple="true"
              ?hidden="${!this._checkTransport}"
              custom-input
            ></file-uploader>
          </div>
        </fieldset>
      </form>

      <div class="container">
        <div class="grist">
          <div class="button-div">
            <mwc-button
              id="button-add-products"
              label=${i18next.t('button.bulk_select_products')}
              raised
              @click=${e => this._openBulkProductSelection()}
              ?disabled=${!this._showAddProducts}
            ></mwc-button>
          </div>
          <h2><mwc-icon>list_alt</mwc-icon>${i18next.t('title.product')}</h2>
          <data-grist
            id="product-grist"
            .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
            .config=${this.productGristConfig}
            .data="${this.productData}"
            @record-change="${this._onProductChangeHandler.bind(this)}"
          ></data-grist>

          <h2
            @click="${e => {
        this._vasExpanded = !this._vasExpanded
      }}"
            class="vas-title"
          >
            <mwc-icon>list_alt</mwc-icon>${i18next.t('title.vas')}<mwc-icon
              >${this._vasExpanded ? 'expand_less' : 'expand_more'}</mwc-icon
            >
          </h2>
            <div class="vas" collapsable ?collapsed=${!this._vasExpanded}>
              <data-grist
                id="vas-grist"
                .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
                .config=${this.vasGristConfig}
                .data="${this.vasData}"
              ></data-grist>
            </div>
          </div>
        </div>
      </div>
    `
  }

  constructor() {
    super()
    this.productData = { records: [] }
    this.vasData = { records: [] }
    this._bizplaceId = ''
    this._bizplaceName = ''
    this.containerSizes = []
    this._importedOrder = false
    this._hasContainer = false
    this._ownTransport = false
    this._warehouseTransport = false
    this._checkTransport = false
    this._checkTransportCrossDocking = false
    this._crossDocking = false
    this._orderType = null
    this._enableTransportationServiceSetting = false
    this._enableLotIdInput = false
    this._enableTransportForCustomClearanceSetting = false
    this._disableTransport = false
    this._strictProductSelection = false
    this._showAddProducts = false
    this._vasExpanded = false
    this._transportVehicle = ''
    this.enableDirectInbound = false
  }

  get arrivalNoticeForm() {
    return this.shadowRoot.querySelector('form[name=arrivalNotice]')
  }

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

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

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

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

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

  get _document() {
    return this.shadowRoot.querySelector('#uploadDocument')
  }

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

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

  get _productColumns() {
    return this.productGrist.columns
  }

  async pageInitialized() {
    this._enableTransportationServiceSetting = await fetchSettingRule('enable-transportation-service')
    this._enableTransportForCustomClearanceSetting = await fetchSettingRule('enable-custom-clearance-transportation')
    this._enableLotIdInput = await fetchSettingRule('enable-lot-id-input')
    this._disableCrossDockingSetting = await fetchSettingRule('disable-cross-dock')
    this._hideCrossDockingSetting = await fetchSettingRule('hide-cross-dock')
    this._bizplaces = await this.fetchBizplaces()
    this.enableDirectInbound = await fetchSettingRule('enable-direct-gan-order')

    this._validateTransport()

    this.containerSizes = await getCodeByName('CONTAINER_SIZES')
    this.uomValueUnits = await getCodeByName('WEIGHT_UNITS')
    this.packingTypes = await getCodeByName('PACKING_TYPES')
    this._configureProductGrist()

    this.vasGristConfig = {
      list: { fields: ['ready', 'targetType', 'targetDisplay', 'packingType'] },
      pagination: { infinite: true },
      rows: {
        handlers: {
          click: (columns, data, column, record, rowIndex) => {
            this._selectedVasRecord = record
            this._selectedVasRecordIdx = rowIndex
            if (column.name) {
              this.openVasCreatePopup(record)
            }
          }
        }
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'close',
          handlers: {
            click: (columns, data, column, record, rowIndex) => {
              this.vasData = { ...this.vasData, records: data.records.filter((_, idx) => idx !== rowIndex) }
            }
          }
        },
        {
          type: 'boolean',
          name: 'ready',
          header: i18next.t('field.ready'),
          width: 40
        },
        {
          type: 'string',
          name: 'targetType',
          header: i18next.t('field.target_type'),
          label: true,
          width: 150
        },
        {
          type: 'string',
          name: 'targetDisplay',
          header: i18next.t('field.target'),
          label: true,
          width: 250
        },
        {
          type: 'string',
          name: 'packingType',
          header: i18next.t('field.packing_type'),
          label: true,
          width: 250
        },
        {
          type: 'integer',
          name: 'qty',
          header: i18next.t('field.qty'),
          width: 100
        },
        {
          type: 'float',
          name: 'uomValue',
          header: i18next.t('field.uom_value'),
          width: 100
        },
        {
          type: 'integer',
          name: 'vasCount',
          header: i18next.t('field.vas_count'),
          width: 100
        }
      ]
    }
  }

  updated(changedProps) {
    if (changedProps.has('_crossDocking')) {
      this._configureProductGrist()
    }
  }

  /**
   * @description Configure product grist
   * Product grist columns should be change by value of _crossDocking
   */
  _configureProductGrist() {
    let editable = this._bizplaceId ? true : false
    let strictEditable = this._bizplaceId ? !this._strictProductSelection : false

    const crossDockingColumns = [
      {
        type: 'integer',
        name: 'releaseQty',
        header: i18next.t('field.release_qty'),
        imex: { header: i18next.t('field.release_qty'), key: 'releaseQty', width: 25, type: 'float' },
        record: { editable: editable },
        width: 160
      },
      {
        type: 'float',
        name: 'releaseUomValue',
        header: i18next.t('field.release_uom_value'),
        imex: { header: i18next.t('field.release_uom_value'), key: 'releaseUomValue', width: 25, type: 'float' },
        record: { editable: editable },
        width: 160
      }
    ]

    const productGristColumns = [
      { type: 'gutter', gutterName: 'sequence' },
      {
        type: 'gutter',
        gutterName: 'button',
        icon: 'close',
        handlers: {
          click: (columns, data, column, record, rowIndex) => {
            this.productData = {
              ...this.productData,
              records: data.records.filter((_, idx) => idx !== rowIndex)
            }
            this._updateVasTargets()
          }
        }
      },
      {
        type: 'string',
        name: 'batchId',
        header: i18next.t('field.batch_id'),
        imex: { header: i18next.t('field.batch_id'), key: 'batchId', width: 25, type: 'string' },
        record: { editable: editable },
        width: 150
      },
      {
        type: 'string',
        name: 'productName',
        header: i18next.t('field.product'),
        imex: { header: i18next.t('field.product'), key: 'productName', width: 50, type: 'string' },
        handlers: {
          click: (columns, data, column, record, rowIndex) => {
            this._openProductSelection(rowIndex)
          }
        },
        width: 350
      },
      {
        type: 'string',
        name: 'productBrand',
        header: i18next.t('field.brand'),
        imex: { header: i18next.t('field.brand'), key: 'productBrand', width: 25, type: 'string' },
        width: 150
      },
      {
        type: 'object',
        name: 'product',
        hidden: true
      },
      {
        type: 'select',
        name: 'packingType',
        header: i18next.t('field.packing_type'),
        imex: {
          header: i18next.t('field.packing_type'),
          key: 'packingType',
          width: 20,
          type: 'array',
          arrData: this.packingTypes
        },
        record: {
          editable: strictEditable,
          options: [
            {
              display: '',
              value: ''
            },
            ...(this.packingTypes || []).map(type => {
              return { display: type.name, value: type.name }
            })
          ]
        },
        width: 150
      },
      {
        type: 'select',
        name: 'uom',
        header: i18next.t('field.uom'),
        imex: {
          header: i18next.t('field.uom'),
          key: 'uom',
          width: 20,
          type: 'array',
          arrData: this.uomValueUnits
        },
        record: {
          editable: strictEditable,
          options: [
            {
              display: '',
              value: ''
            },
            ...(this.uomValueUnits || []).map(unit => {
              return { display: unit.name, value: unit.name }
            })
          ]
        },
        width: 80
      },
      {
        type: 'float',
        name: 'uomValue',
        header: i18next.t('field.uom_value'),
        imex: { header: i18next.t('field.uom_value'), key: 'uomValue', width: 25, type: 'float' },
        record: { editable: strictEditable, options: { min: 0 } },
        width: 100
      },
      {
        type: 'float',
        name: 'packQty',
        header: i18next.t('field.pack_qty'),
        imex: { header: i18next.t('field.pack_qty'), key: 'packQty', width: 25, type: 'float' },
        record: { editable: editable, options: { min: 0 } },
        width: 80
      },
      {
        type: 'float',
        name: 'totalUomValue',
        header: i18next.t('field.total_uom_value'),
        width: 120
      },
      ...(!this.enableDirectInbound ? [{
        type: 'integer',
        name: 'palletQty',
        header: i18next.t('field.pallet_qty'),
        imex: { header: i18next.t('field.pallet_qty'), key: 'palletQty', width: 25, type: 'float' },
        record: { editable: editable, options: { min: 0 } },
        width: 80
      }] : []),
      ...(this._enableLotIdInput
        ? [
          {
            type: 'string',
            name: 'palletId',
            header: i18next.t('field.lot_id'),
            imex: { header: i18next.t('field.lot_id'), key: 'palletId', width: 25, type: 'string' },
            record: { editable: editable },
            width: 150
          }
        ]
        : []),
      {
        type: 'date',
        name: 'manufactureDate',
        header: i18next.t('field.manufacture_date'),
        imex: { header: i18next.t('field.manufacture_date'), key: 'manufactureDate', width: 25, type: 'string' },
        record: { editable: editable },
        width: 150
      },
      {
        type: 'string',
        name: 'batchIdRef',
        header: i18next.t('field.batch_no_ref'),
        imex: { header: i18next.t('field.batch_id_ref'), key: 'batchIdRef', width: 25, type: 'string' },
        record: { editable: editable },
        width: 150
      },
      {
        type: 'float',
        name: 'unitPrice',
        header: i18next.t('field.unit_price'),
        imex: { header: i18next.t('field.unit_price'), key: 'unitPrice', width: 25, type: 'string' },
        record: { editable: editable },
        sortable: true,
        width: 80
      },
      {
        type: 'string',
        name: 'remark',
        header: i18next.t('field.remark'),
        imex: { header: i18next.t('field.remark'), key: 'remark', width: 25, type: 'string' },
        record: { editable: editable },
        width: 300
      }
    ]

    if (this._crossDocking) {
      const packQtyColumnIdx =
        productGristColumns.findIndex(column => column.name === 'packQty') || productGristColumns.length - 1
      productGristColumns.splice(packQtyColumnIdx + 1, 0, ...crossDockingColumns)
    }

    const expDateColumn = {
      type: 'date',
      name: 'expDate',
      header: i18next.t('field.exp_date'),
      imex: { key: 'expDate', width: 25, type: 'string' },
      record: { editable: editable },
      width: 150
    }

    const locationColumn = {
      type: 'object',
      name: 'location',
      header: i18next.t('field.location'),
      imex: { header: i18next.t('field.location'), key: 'location', width: 30, type: 'object' },
      handlers: {
        click: (columns, data, column, record, rowIndex) => {
          this._openLocationSelection(rowIndex)
        }
      },
      width: 150
    }

    const warehouseColumn = {
      type: 'object',
      name: 'warehouse',
      header: i18next.t('field.warehouse'),
      imex: { key: 'warehouse', width: 30, type: 'string' },
      width: 150,
      recordFormatter: (warehouse, updatedRecords) => warehouse?.name || ''
    }

    function insertAfter(cols, anchorName, newCols) {
      const idx = cols.findIndex(c => c.name === anchorName)
      if (idx >= 0) cols.splice(idx + 1, 0, ...newCols)
    }

    if (this.enableDirectInbound) {
      const anchor = this._enableLotIdInput ? 'palletId' : 'totalUomValue'
      insertAfter(productGristColumns, anchor, [expDateColumn])

      insertAfter(productGristColumns, 'batchIdRef', [warehouseColumn, locationColumn])
    }

    this.productGristConfig = {
      pagination: { infinite: true },
      list: { fields: ['batch_no', 'product', 'packingType', 'totalUomValue'] },
      rows: {
        classifier: (record, rowIndex) => {
          return {
            emphasized: record.isError
          }
        }
      },
      columns: productGristColumns
    }
  }

  _openTransportVehicleSelector() {
    openPopup(
      html`
        <select-transport-vehicle-popup
          @selected="${e => {
          // value of e might be wrong, need to recheck
          this._transportVehicle = `${e.detail.regNumber}` + ` - ${e.detail.size}`
        }}"
        ></select-transport-vehicle-popup>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.transport_vehicle')
      }
    )
  }

  _openBulkProductSelection() {
    if (this._bizplaceId) {
      openPopup(
        html`
          <select-product-popup
            .bizplaceId="${this._bizplaceId}"
            .selectMultipleProducts="${this._showAddProducts}"
            @selected="${e => {
            let newRecords = []
            for (let i = 0; i < e.detail.multipleProducts.length; i++) {
              newRecords.push(e.detail.multipleProducts[i])
            }
            this.productData = {
              ...this.productData,
              records: [...this.productGrist.dirtyData.records, ...newRecords]
            }
          }}"
          ></select-product-popup>
        `,
        {
          backdrop: true,
          size: 'large',
          title: i18next.t('title.select_item')
        }
      )
    } else {
      this._showToast({ message: i18next.t('text.company_selection_is_empty') })
    }
  }

  _openProductSelection(rowIndex) {
    if (this._bizplaceId) {
      openPopup(
        html`
          <select-product-popup
            .bizplaceId="${this._bizplaceId}"
            @selected="${e => {
            // let newRecord = []
            // newRecord.push(e.detail)

            if (this.productGrist.dirtyData.records.length !== rowIndex) {
              // update existing product data records
              this.productGrist.dirtyData.records.splice(rowIndex, 1, {
                ...this.productGrist.dirtyData.records[rowIndex],
                ...e.detail,
                isRequiredCheckExpiry: e.detail.product.isRequiredCheckExpiry
              })

              this.productGrist.dirtyData.records = this.productGrist.dirtyData.records.map(item => {
                return {
                  ...item,
                  totalUomValue:
                    item?.packQty > 0
                      ? item?.totalUomValue
                        ? item.totalUomValue
                        : `${parseFloat(item.packQty * e.detail.uomValue).toFixed(3)} ${e.detail.uom}`
                      : null
                }
              })

              this.productData = {
                ...this.productData,
                records: [...this.productGrist.dirtyData.records]
              }
            } else {
              this.productData = {
                ...this.productData,
                records: [...this.productGrist.dirtyData.records, e.detail]
              }
            }
          }}"
          ></select-product-popup>
        `,
        {
          backdrop: true,
          size: 'large',
          title: i18next.t('title.select_item')
        }
      )
    } else {
      this._showToast({ message: i18next.t('text.company_selection_is_empty') })
    }
  }

  _openLocationSelection(rowIndex) {
    if (this._bizplaceId) {
      openPopup(
        html`
          <buffer-location-selector
            .bizplaceId="${this._bizplaceId}"
            .enableDirectInbound="${this.enableDirectInbound}"
            @selected="${async e => {
            this.productGrist.dirtyData.records[rowIndex].location = { id: e.detail.id, name: e.detail.name }
            this.productGrist.dirtyData.records[rowIndex].warehouse = {
              id: e.detail.warehouse.id,
              name: e.detail.warehouse.name
            }
            this.productGrist.dirtyData.records[rowIndex].__dirty__ = true

            if (this.productGrist.dirtyData.records.length !== rowIndex) {
              this.productGrist.dirtyData.records.splice(rowIndex, 1, {
                ...this.productGrist.dirtyData.records[rowIndex]
              })

              this.productData = {
                ...this.productData,
                records: [...this.productGrist.dirtyData.records]
              }

              this.productGrist.data.records = this.productGrist.dirtyData.records
            } else {
              this.productData = {
                ...this.productData,
                records: [...this.productGrist.dirtyData.records]
              }
            }
          }}"
          ></buffer-location-selector>
        `,
        {
          backdrop: true,
          size: 'large',
          title: i18next.t('title.select_location', 'Select Location')
        }
      )
    } else {
      this._showToast({ message: i18next.t('text.company_selection_is_empty') })
    }
  }

  _onProductChangeHandler(event) {
    try {
      if (!this.enableDirectInbound) {
        this._checkProductDuplication
      }
      let changeRecord = event.detail.after
      const changedColumn = event.detail.column.name

      if (event.detail.column.name == 'product') {
        let primaryValue = !!changeRecord.product?.primaryValue
        changeRecord.uomValue = primaryValue ? changeRecord.product.primaryValue : null
        let primaryUnit = !!changeRecord.product?.primaryUnit
        changeRecord.uom = primaryUnit ? changeRecord.product.primaryUnit : ''
        let packingType = !!changeRecord.product?.packingType
        changeRecord.packingType = packingType ? changeRecord.product.packingType : ''
      }

      const amountRelatedColumns = ['uomValue', 'uom', 'packQty', 'releaseQty', 'releaseUomValue']
      if (amountRelatedColumns.indexOf(changedColumn) >= 0) {
        const calcedAmount = this._calcAmount(changedColumn, changeRecord)
        for (let key in calcedAmount) {
          changeRecord[key] = calcedAmount[key]
        }
      }

      if (changedColumn === 'manufactureDate') {
        //verify date value
        let getYear = parseInt(changeRecord.manufactureDate.slice(0, 4))
        let getMonth = parseInt(changeRecord.manufactureDate.slice(5, 7))
        let getDay = parseInt(changeRecord.manufactureDate.slice(8, 10))

        //year must be greater than year 2000
        //month must be between 1 and 12
        //day must be between 1 and 31
        if (getYear < 2000 || getMonth < 1 || getMonth > 12 || getDay < 1 || getDay > 31) {
          throw Error(i18next.t('text.please_enter_the_correct_date_format'))
        }

        //verify date range
        let today = new Date()
        let manufactureDate = new Date(changeRecord.manufactureDate)

        if (manufactureDate > today) {
          delete event.detail.after[event.detail.column.name]
          throw Error(i18next.t('text.manufacture-date-cannot-be-later-than-today'))
        }
      }

      if (changedColumn === 'unitPrice') {
        changeRecord.unitPrice = changeRecord.unitPrice.toFixed(2)
        let unitPrice = changeRecord.unitPrice
        if (typeof unitPrice === undefined || unitPrice < 0 || isNaN(unitPrice)) {
          throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.unit_price') } }))
        }
      }

      if (changedColumn === 'expDate') {
        if (!changeRecord.product?.isRequiredCheckExpiry) {
          delete changeRecord.expDate
          throw new Error(
            i18next.t('text.exp_date_not_required_for_this_product', {
              defaultValue: 'Expiry date is not required for this product'
            })
          )
        }
      }

      this._updateVasTargets(true)
    } catch (e) {
      const beforeValue = event.detail.before && event.detail.before[event.detail.column.name]
      if (beforeValue) {
        event.detail.after[event.detail.column.name] = beforeValue
      } else {
        delete event.detail.after[event.detail.column.name]
      }

      this._showToast(e)
    }
  }

  _checkProductDuplication() {
    // batchId, product.id, packingType
    if (!this._enableLotIdInput) {
      let isDuplicated = false
      const completedRows = this.productGrist.dirtyData.records
        .filter(record => record.batchId && record.product && record.product.id && record.packingType)
        .map(record => `${record.batchId.toLowerCase()}-${record.product.id}-${record.packingType}`)

      completedRows.forEach((row, idx, rows) => {
        if (rows.lastIndexOf(row) !== idx) {
          isDuplicated = true
        }
      })
      if (isDuplicated) {
        throw new Error(i18next.t('text.there_is_duplicated_products'))
      }
    } else {
      let isDuplicated = false
      const completedRows = this.productGrist.dirtyData.records
        .filter(record => record.palletId)
        .map(record => `${record.palletId}`)

      completedRows.forEach((row, idx, rows) => {
        if (rows.lastIndexOf(row) !== idx) {
          isDuplicated = true
        }
      })

      if (isDuplicated) {
        throw new Error(i18next.t('Lot ID is already in used. Please use a new Lot ID'))
      }
    }
  }

  _calcAmount(changedColumn, changeRecord) {
    let calcedRecord = {}
    let { uomValue, uom, packQty, releaseQty, releaseUomValue } = changeRecord

    if (changedColumn === 'uomValue' && (!uomValue || uomValue < 0))
      throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.uom_value') } }))
    if (changedColumn === 'packQty' && (!packQty || packQty < 0))
      throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.pack_qty') } }))
    if (changedColumn === 'uom' && !uom)
      throw new Error(i18next.t('text.x_is_empty', { state: { x: i18next.t('label.uom') } }))

    if (uomValue && uom && packQty) {
      calcedRecord.totalUomValue = `${(uomValue * packQty).toFixed(3)} ${uom}`
    } else {
      calcedRecord.totalUomValue = ''
    }

    if (!this._crossDocking) return calcedRecord

    if (changedColumn === 'releaseQty' || changedColumn === 'releaseUomValue') {
      if (isNaN(releaseQty)) {
        releaseQty = 0
        calcedRecord.releaseQty = releaseQty
      }
      if (isNaN(releaseUomValue)) {
        releaseUomValue = 0
        calcedRecord.releaseUomValue = releaseUomValue
      }

      if (!uomValue)
        throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.uom_value') } }))
      if (!packQty)
        throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.pack_qty') } }))

      if (changedColumn === 'releaseQty') {
        if (typeof releaseQty === undefined || releaseQty < 0) {
          throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.release_qty') } }))
        }
        if (releaseQty > packQty) {
          throw new Error(i18next.t('text.x_exceed_limit', { state: { x: i18next.t('label.release_qty') } }))
        }
        calcedRecord.releaseUomValue = releaseQty * uomValue
      } else {
        if (releaseUomValue === undefined || releaseUomValue < 0) {
          throw new Error(
            i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.release_uom_value') } })
          )
        }

        if (releaseUomValue > packQty * uomValue) {
          throw new Error(i18next.t('text.x_exceed_limit', { state: { x: i18next.t('label.release_uom_value') } }))
        }

        if (releaseUomValue % uomValue) {
          throw new Error(i18next.t('text.invalid_x', { state: { x: i18next.t('label.release_uom_value') } }))
        }

        calcedRecord.releaseQty = releaseUomValue / uomValue
      }
    }

    return calcedRecord
  }

  async fetchLocation() {
    const filter = []
    if (this.enableDirectInbound) {
      filter.push({
        name: 'type',
        operator: 'noteq',
        value: 'BIN'
      })
    }
    const response = await client.query({
      query: gql`
        query locations($filters: [Filter!]!) {
          locations(filters: $filters) {
            items {
              id
              name
              warehouse {
                name
              }
            }
          }
        }
      `,
      variables: { filters: filter }
    })
    return response.data.locations.items
  }

  async fetchBizplaces() {
    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) {
      const bizplaces = response.data.partnersBizplaces.items
      if (bizplaces.length == 1) {
        this._bizplaceId = bizplaces[0].id
        this._bizplaceName = bizplaces[0].name
        this._showAddProducts = true
      } else {
        this._bizplaceId = ''
        this._bizplaceName = ''
      }

      return bizplaces
    }
  }

  async _generateArrivalNotice() {
    try {
      this._validateForm()
      this._validateProducts()
      this._validateVas()

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

      if (!result.value) return

      let arrivalNotice = this._getFormInfo()
      delete arrivalNotice.warehouseTransport
      arrivalNotice.orderProducts = this._getOrderProducts()
      const fileUploader = this._document?.files ? this._document.files : undefined

      if (!this.enableDirectInbound && arrivalNotice.orderProducts.some(orderProduct => !orderProduct.palletQty)) {
        const result = await CustomAlert({
          title: i18next.t('title.are_you_sure'),
          text: i18next.t('text.there_is_no_pallet_qty'),
          confirmButton: { text: i18next.t('button.submit') },
          cancelButton: { text: i18next.t('button.cancel') }
        })

        if (!result.value) return
      }

      await this._executeRelatedTrxs()
      arrivalNotice.orderVass = this._getOrderVass()

      delete arrivalNotice.bizplaceId
      delete arrivalNotice.bizplaceName

      const mutation = this.enableDirectInbound
        ? gql`
            mutation submitDirectInboundArrivalNotice($arrivalNotice: NewArrivalNotice!, $files: [Upload!]) {
              submitDirectInboundArrivalNotice(arrivalNotice: $arrivalNotice, files: $files) {
                id
                name
                crossDocking
              }
            }
          `
        : gql`
            mutation generateArrivalNotice($arrivalNotice: NewArrivalNotice!, $files: [Upload!]) {
              generateArrivalNotice(arrivalNotice: $arrivalNotice, files: $files) {
                id
                name
                crossDocking
              }
            }
          `

      const response = await client.mutate({
        mutation,
        variables: {
          arrivalNotice: {
            ...arrivalNotice,
            partnerBizplaceId: this._bizplaceId,
            ownTransport: this._importedOrder ? true : this._ownTransport
          },
          files: fileUploader
        },
        context: { hasUpload: true }
      })

      if (!response.errors) {
        this._clearView()

        const resultData = this.enableDirectInbound
          ? response.data.submitDirectInboundArrivalNotice
          : response.data.generateArrivalNotice

        const ganNo = resultData.name
        const isCrossDocking = resultData.crossDocking
        if (!isCrossDocking) {
          navigate(`arrival_notice_detail/${ganNo}`)
        } else {
          const result = await CustomAlert({
            title: i18next.t('title.move_to_page', { state: { page: i18next.t('title.create_release_order') } }),
            text: i18next.t('text.creating_release_order_is_required'),
            confirmButton: { text: i18next.t('button.move') },
            cancelButton: { text: i18next.t('button.cancel') }
          })

          if (!result.value) {
            navigate(`arrival_notice_detail/${ganNo}`)
          } else {
            navigate(`create_release_order/${ganNo}`)
          }
        }

        this._showToast({ message: i18next.t('arrival_notice_created') })
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _executeRelatedTrxs() {
    if (!this.vasGrist.dirtyData || !this.vasGrist.dirtyData.records || !this.vasGrist.dirtyData.records.length) return

    try {
      this.vasData = {
        ...this.vasData,
        records: await (async () => {
          const records = this.vasGrist.dirtyData.records
          for (let record of records) {
            const orderVass = record.orderVass
            for (let orderVas of orderVass) {
              if (orderVas.operationGuide && orderVas.operationGuide.transactions) {
                const trxs = orderVas.operationGuide.transactions
                for (let trx of trxs) {
                  orderVas.operationGuide = await trx(orderVas.operationGuide)
                }
              }
            }
          }
        })()
      }
    } catch (e) {
      throw e
    }
  }

  _validateImportCargo() {
    //Validation for Import Cargo
    if (this._importedOrder) {
      if (!this._enableTransportationServiceSetting) {
        this._disableTransport = true
        this._ownTransportInput.checked = true
        this._ownTransport = true
      } else {
        if (this._enableTransportForCustomClearanceSetting) {
          this._disableTransport = false
        } else {
          this._disableTransport = true
          this._ownTransportInput.checked = true
          this._ownTransport = true
          this._warehouseTransportInput.checked = false
          this._warehouseTransport = false
        }
      }
    } else {
      if (!this._enableTransportationServiceSetting) {
        this._disableTransport = true
        this._ownTransportInput.checked = true
        this._ownTransport = true
      } else {
        this._disableTransport = false
      }
    }
  }

  _validateTransport() {
    //Check if warehouse provide transport
    if (!this._importedOrder) {
      if (!this._enableTransportationServiceSetting) {
        this._disableTransport = true
        this._ownTransportInput.checked = true
        this._ownTransport = true
      }
    }

    if (this._ownTransport || this._warehouseTransport) {
      this._checkTransport = true
      this._checkTransportCrossDocking = true
      if (this._hideCrossDockingSetting) {
        this._checkTransportCrossDocking = false
      }
    }
  }

  _validateForm() {
    if (!this.arrivalNoticeForm.checkValidity()) throw new Error(i18next.t('text.arrival_notice_form_invalid'))

    if (this._hasContainer) {
      if (!this.containerNo.value) throw new Error(i18next.t('text.container_no_is_empty'))

      if (!this.arrivalNoticeForm?.containerSize?.value || this.arrivalNoticeForm?.containerSize?.value == '')
        throw new Error(i18next.t('text.please_select_container_size'))
    }

    if (this._ownTransport && this._warehouseTransport)
      throw new Error(i18next.t('text.you_can_only_select_one_transport_type'))
    else if (!this._ownTransport && !this._warehouseTransport)
      throw new Error(i18next.t('text.please_select_transport_type'))
  }

  _validateProducts(records = []) {
    // no records
    if (!records?.length) records = this.productGrist.dirtyData.records
    if (!records?.length) throw new Error(i18next.t('text.no_products'))

    if (!this.enableDirectInbound) {
      this._checkProductDuplication()
    }

    const checkRequiredFields = ({ batchId, packQty }) => !batchId || !packQty

    // required field (batchId, packingType, uomValue, uom, packQty, palletQty)
    if (records.some(checkRequiredFields)) {
      throw new Error(i18next.t('text.empty_value_in_list'))
    }

    if (records.some(({ unitPrice }) => isNaN(unitPrice))) {
      throw new Error(i18next.t('text.invalid_unit_price_not_a_number'))
    }

    if (records.some(({ unitPrice }) => unitPrice < 0)) {
      throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.unit_price') } }))
    }

    if (records.some(({ packQty }) => packQty < 0)) {
      throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.pack_qty') } }))
    }

    if (records.some(({ uomValue }) => uomValue < 0)) {
      throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.uom_value') } }))
    }

    if (!this.enableDirectInbound && records.some(({ palletQty }) => palletQty < 0)) {
      throw new Error(i18next.t('text.x_should_be_positive', { state: { x: i18next.t('label.pallet_qty') } }))
    }

    if (!this.enableDirectInbound && records.some(({ palletQty, packQty }) => palletQty > packQty)) {
      throw new Error(
        i18next.t('text.a_cannot_exceed_b', {
          state: {
            a: i18next.t('field.pallet_qty'),
            b: i18next.t('field.pack_qty')
          }
        })
      )
    }

    if (this._crossDocking && records.every(({ releaseQty, releaseUomValue }) => !releaseQty || !releaseUomValue)) {
      throw new Error(
        i18next.t('text.invalid_x', {
          state: {
            x: `${i18next.t('label.release_qty')}, ${i18next.t('label.release_uom_value')}`
          }
        })
      )
    }

    if (this.enableDirectInbound) {
      records.forEach((record, index) => {
        if (this.enableDirectInbound && record?.product?.isRequiredCheckExpiry && !record.expDate) {
          throw new Error(
            i18next.t('text.exp_date_required_for_product', {
              defaultValue: `Row ${index + 1}: Expiry date is required for product "${record.product.name}"`,
              product: record.product.name,
              row: index + 1
            })
          )
        }

        if (!record.location || (!record.location.id && !record.location.name)) {
          throw new Error(
            i18next.t('text.location_required_for_product', {
              defaultValue: `Row ${index + 1}: Location is missing for product "${record.product?.name}"`,
              product: record.product.name,
              row: index + 1
            })
          )
        }
      })
    }
  }

  _validateVas() {
    if (!this.vasGrist.dirtyData.records.every(record => record.ready)) {
      throw new Error(i18next.t('text.invalid_vas_setting'))
    }
  }

  _updateVasTargets(isFieldChanged) {
    if (!this.vasGrist.dirtyData?.records?.length) return

    if (!this.productGrist.dirtyData?.records?.length) {
      this.vasData = { ...this.vasData, records: [] }
      return
    }

    const standardProductList = isFieldChanged ? this.productGrist.dirtyData.records : this.productData.records
    const batchPackPairs = standardProductList
      .filter(record => record.batchId && record.packingType)
      .map(record => `${record.batchId}-${record.packingType}`)

    const productPackPairs = standardProductList
      .filter(record => record.product && record.product.id)
      .map(record => `${record.product.id}-${record.packingType}`)

    const batchProductPackPairs = standardProductList
      .filter(record => record.batchId && record.product && record.product.id && record.packingType)
      .map(record => `${record.batchId}-${record.product.id}-${record.packingType}`)

    this.vasData = {
      ...this.vasData,
      records: this.vasGrist.dirtyData.records.map(record => {
        if (
          record.targetType === VAS_BATCH_NO_TYPE &&
          batchPackPairs.indexOf(`${record.target}-${record.packingType}`) < 0
        ) {
          return {
            ...record,
            ready: false,
            target: null,
            targetDisplay: null,
            packingType: null,
            qty: 1
          }
        } else if (
          record.targetType === VAS_PRODUCT_TYPE &&
          productPackPairs.indexOf(`${record.target}-${record.packingType}`) < 0
        ) {
          return {
            ...record,
            ready: false,
            target: null,
            targetDisplay: null,
            packingType: null,
            qty: 1
          }
        } else if (
          record.targetType === VAS_BATCH_AND_PRODUCT_TYPE &&
          batchProductPackPairs.indexOf(`${record.target.batchId}-${record.target.productId}-${record.packingType}`) < 0
        ) {
          return {
            ...record,
            ready: false,
            target: null,
            targetDisplay: null,
            packingType: null,
            qty: 1
          }
        } else {
          return record
        }
      })
    }
  }

  _getFormInfo() {
    const formData = this._serializeForm(this.arrivalNoticeForm)
    delete formData.importedOrder
    return formData
  }

  _getOrderProducts() {
    return this.productGrist.dirtyData.records.map(record => {
      if (record.unitPrice) {
        record.unitPrice = parseFloat(record.unitPrice)
      }

      let orderProduct = {
        batchId: record.batchId ? record.batchId.trim() : null,
        batchIdRef: record.batchIdRef ? record.batchIdRef.trim() : null,
        product: { id: record.product.id },
        productDetail: record?.productDetail?.id ? record.productDetail : { id: record.productDetailId },
        packingType: record.packingType,
        packingSize: record.packingSize || record.product.packingSize,
        uomValue: record?.uomValue ? record.uomValue : 0,
        uom: record.uom,
        packQty: Math.round(parseFloat(record.packQty) * 1000) / 1000,
        totalUomValue: record?.totalUomValue ? record?.totalUomValue : 0,
        unitPrice: record?.unitPrice ? record.unitPrice : 0,
        remark: record?.remark ? record.remark : null,
        expDate: record?.expDate ? record.expDate : null,
        manufactureDate: record?.manufactureDate ? record.manufactureDate : null,
        warehouse: record?.warehouse ? record.warehouse : null,
        location: record?.location ? record.location : null,
        palletId: record?.palletId ? record.palletId : null
      }

      if (record.palletQty) orderProduct.palletQty = record.palletQty
      if (this._crossDocking && record.releaseQty !== undefined && record.releaseUomValue !== undefined) {
        orderProduct.releaseQty = record.releaseQty
        orderProduct.releaseUomValue = record.releaseUomValue
      }

      return orderProduct
    })
  }

  _getOrderVass() {
    if (!this.vasGrist.dirtyData?.records?.length) {
      return []
    }

    const records = this.vasGrist.dirtyData.records

    return records
      .map((record, idx) => {
        const orderVass = record.orderVass
        return orderVass.map(orderVas => {
          let result = {
            set: idx + 1,
            vas: { id: orderVas.vas.id },
            remark: orderVas.remark,
            targetType: record.targetType
          }

          if (orderVas.operationGuide && orderVas.operationGuide.data) {
            result.operationGuide = JSON.stringify(orderVas.operationGuide)
          }

          if (record.targetType === VAS_BATCH_NO_TYPE) {
            result.targetBatchId = record.target
            result.packingType = record.packingType
            result.qty = record.qty
          } else if (record.targetType === VAS_PRODUCT_TYPE) {
            result.targetProduct = { id: record.target }
            result.packingType = record.packingType
            result.qty = record.qty
          } else if (record.targetType === VAS_BATCH_AND_PRODUCT_TYPE) {
            result.targetBatchId = record.target.batchId
            result.targetProduct = { id: record.target.productId }
            result.packingType = record.packingType
            result.qty = record.qty
            result.uomValue = record.uomValue
          } else {
            result.otherTarget = record.target
          }

          return result
        })
      })
      .flat()
  }

  _clearView() {
    this.arrivalNoticeForm.reset()
    this.productData = { ...this.productData, records: [] }
    this.vasData = { ...this.vasData, records: [] }
    if (this._document?._files) {
      this._document.reset()
    }
  }

  _serializeForm(form) {
    let obj = {}
    Array.from(form.querySelectorAll('input, select')).forEach(field => {
      if (!field.hasAttribute('hidden') && field.value) {
        obj[field.name] = field.type === 'checkbox' ? field.checked : field.value
      }
    })

    return obj
  }

  openVasCreatePopup(record) {
    try {
      this.checkProductValidity()
      openPopup(
        html`
          <vas-create-popup
            .targetList="${this.productGrist.dirtyData.records
            .map(record => {
              let packQty = record.packQty - (record.releaseQty || 0)

              if (this.vasData.records?.length) {
                const existVasData = this.vasData.records.filter(vasRecord => {
                  const isSamePackingType = !!(record.packingType === vasRecord.packingType)
                  switch (vasRecord.targetType) {
                    case VAS_BATCH_AND_PRODUCT_TYPE:
                      return (
                        record.batchId === vasRecord.target.batchId &&
                        record.product.id === vasRecord.target.productId &&
                        isSamePackingType
                      )
                    case VAS_BATCH_NO_TYPE:
                      return record.batchId === vasRecord.target && isSamePackingType
                    case VAS_PRODUCT_TYPE:
                      return record.product.id === vasRecord.target && isSamePackingType
                  }
                })

                if (existVasData?.length) {
                  let assignedQty = 0

                  existVasData.forEach(dt => {
                    assignedQty += dt.qty
                  })

                  packQty -= assignedQty
                }
              }

              delete record.__dirty__
              delete record.__dirtyfields__
              return {
                ...record,
                packQty,
                unitUomValue: record.uomValue,
                totalUomValue: record.uomValue * packQty
              }
            })
            .filter(record => record.packQty > 0)}"
            .vasList="${this.vasData.records}"
            .record="${record}"
            @completed="${e => {
            const newRecords = e.detail

            if (this.vasGrist.dirtyData.records.length !== this._selectedVasRecordIdx) {
              // update existing vasData records
              this.vasGrist.dirtyData.records.splice(this._selectedVasRecordIdx, 1, ...newRecords)

              this.vasData = {
                ...this.vasData,
                records: [...this.vasGrist.dirtyData.records]
              }
            } else {
              this.vasData = {
                ...this.vasData,
                records: [...this.vasGrist.dirtyData.records, ...newRecords]
              }
            }
          }}"
          ></vas-create-popup>
        `,
        {
          backdrop: true,
          size: 'large',
          title: i18next.t('title.vas_order')
        }
      )
    } catch (e) {
      this._showToast(e)
    }
  }

  checkProductValidity() {
    if (!this.productGrist.dirtyData.records || !this.productGrist.dirtyData.records.length)
      throw new Error(i18next.t('text.there_is_no_product'))

    if (this.productGrist.dirtyData.records.some(record => !record.batchId))
      throw new Error(i18next.t('text.invalid_batch_no'))

    if (this.productGrist.dirtyData.records.some(record => !record.product || !record.product.id))
      throw new Error(i18next.t('text.invalid_product'))

    if (this.productGrist.dirtyData.records.some(record => !record.packingType))
      throw new Error(i18next.t('text.invalid_packing_type'))

    if (this.productGrist.dirtyData.records.some(record => !record.uomValue))
      throw new Error(i18next.t('text.invalid_uom_value'))

    if (this.productGrist.dirtyData.records.some(record => !record.packQty))
      throw new Error(i18next.t('text.invalid_pack_qty'))

    return true
  }

  _openBizplacesSelector() {
    openPopup(
      html`
        <popup-partner-bizplaces-selector
          @selected="${async e => {
          this._bizplaceId = e.detail.id
          this._bizplaceName = e.detail.name
          this.productGrist.reset()
          this.vasGrist.reset()
          this._strictProductSelection = await fetchSettingRule('strict-product-selection', e.detail.domain.id)
          this._configureProductGrist()
          this._clearView()
          this._showAddProducts = true
        }}"
        ></popup-partner-bizplaces-selector>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.select_company')
      }
    )
  }

  excelColumns() {
    const productColumns = [
      {
        type: 'string',
        name: 'batchId',
        header: i18next.t('field.batch_id'),
        imex: { header: i18next.t('field.batch_id'), key: 'batchId', width: 25, type: 'string' },
        width: 100
      },
      {
        type: 'string',
        name: 'productSKU',
        header: i18next.t('field.sku'),
        imex: { header: i18next.t('field.sku'), key: 'productSKU', width: 25, type: 'string' },
        width: 100
      },
      {
        type: 'string',
        name: 'packingType',
        header: i18next.t('field.packing_type'),
        imex: { header: i18next.t('field.packing_type'), key: 'packingType', width: 25, type: 'string' },
        width: 100
      },
      {
        type: 'string',
        name: 'uom',
        header: i18next.t('field.uom'),
        imex: { header: i18next.t('field.uom'), key: 'uom', width: 25, type: 'string' },
        width: 100
      },
      {
        type: 'float',
        name: 'uomValue',
        header: i18next.t('field.uom_value'),
        imex: { header: i18next.t('field.uom_value'), key: 'uomValue', width: 25, type: 'float' },
        width: 100
      },
      {
        type: 'float',
        name: 'packQty',
        header: i18next.t('field.pack_qty'),
        imex: { header: i18next.t('field.pack_qty'), key: 'packQty', width: 25, type: 'float' },
        width: 100
      },
      ...(!this.enableDirectInbound ? [{
        type: 'integer',
        name: 'palletQty',
        header: i18next.t('field.pallet_qty'),
        imex: { header: i18next.t('field.pallet_qty'), key: 'palletQty', width: 25, type: 'float' },
        width: 100
      }] : []),
      ...(this._enableLotIdInput
        ? [
          {
            type: 'string',
            name: 'palletId',
            header: i18next.t('field.lot_id'),
            imex: { header: i18next.t('field.lot_id'), key: 'palletId', width: 25, type: 'string' },
            width: 100
          }
        ]
        : []),
      {
        type: 'date',
        name: 'manufactureDate',
        header: i18next.t('field.manufacture_date'),
        imex: { header: i18next.t('field.manufacture_date'), key: 'manufactureDate', width: 25, type: 'string' },
        width: 120
      },
      {
        type: 'string',
        name: 'batchIdRef',
        header: i18next.t('field.batch_id_ref'),
        imex: { header: i18next.t('field.batch_id_ref'), key: 'batchIdRef', width: 25, type: 'string' },
        width: 100
      },
      ...(this.enableDirectInbound
        ? [
          {
            type: 'object',
            name: 'location',
            header: i18next.t('field.location'),
            imex: {
              header: i18next.t('field.location'),
              key: 'location.name',
              width: 30,
              type: 'array',
              arrData: []
            },
            width: 120
          }
        ]
        : []),
      {
        type: 'float',
        name: 'unitPrice',
        header: i18next.t('field.unit_price'),
        imex: { header: i18next.t('field.unit_price'), key: 'unitPrice', width: 25, type: 'float' },
        width: 100
      },
      {
        type: 'string',
        name: 'remark',
        header: i18next.t('field.remark'),
        imex: { header: i18next.t('field.remark'), key: 'remark', width: 25, type: 'string' },
        width: 100
      },
      ...(this.enableDirectInbound
        ? [
          {
            type: 'string',
            name: 'errMsg',
            header: i18next.t('field.error_message'),
            imex: { header: i18next.t('field.error_message'), key: 'errMsg', width: 25, type: 'string' },
            width: 350
          }
        ]
        : [])
    ]

    return productColumns
  }

  async _exportableData() {
    let location = await this.fetchLocation()
    try {
      var headerSetting = [
        ...this.excelColumns()
          .filter(column => column.type !== 'gutter' && column.imex !== undefined)
          .map(column => {
            if (this.enableDirectInbound && column.header == 'location') {
              location = location.map(item => {
                let newName = `${item.name} (${item.warehouse.name})`
                return {
                  ...item,
                  name: newName
                }
              })

              column.imex.arrData = location
            }
            return column.imex
          })
      ]

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

  async bulkUploadValidateOrderProducts(patches) {
    const response = await client.query({
      query: gql`
        query bulkUploadValidateOrderProducts($orderProducts: [NewOrderProduct!], $partnerBizplaceId: String!) {
          bulkUploadValidateOrderProducts(orderProducts: $orderProducts, partnerBizplaceId: $partnerBizplaceId) {
            id
            packingType
            packingSize
            uom
            uomValue
            totalUomValue
            packQty
            palletQty
            palletId
            manufactureDate
            batchId
            batchIdRef
            unitPrice
            remark
            productSKU
            productName
            productBrand
            isError
            warehouse {
              id
              name
            }
            location {
              id
              name
            }
            product {
              id
              name
              sku
              description
              type
              brand
              primaryUnit
              primaryValue
              costPrice
            }
            productDetail {
              id
              costPrice
            }
          }
        }
      `,
      variables: { orderProducts: patches, partnerBizplaceId: this._bizplaceId }
    })
    let foundErrors = response.data.bulkUploadValidateOrderProducts.find(item => item.isError != 0)
    if (foundErrors) {
      return this._showToast({ message: i18next.t('text.invalid_product_sku') })
    }

    if (this._enableLotIdInput) {
      let palletIds = response.data.bulkUploadValidateOrderProducts
        .map(item => item.palletId)
        .filter(palletId => palletId !== null)

      let uniquePalletIds = new Set(palletIds)
      let isDuplicated = uniquePalletIds.size !== palletIds.length
      if (isDuplicated) {
        return this._showToast({ message: i18next.t('text.duplicated_lot_id') })
      }
    }

    // Validate for location

    if (!foundErrors) {
      if (!response.errors) {
        const data = response.data.bulkUploadValidateOrderProducts.map(item => {
          item.unitPrice =
            item?.unitPrice || item?.unitPrice == 0
              ? item.unitPrice
              : item?.productDetail?.costPrice
                ? item.productDetail.costPrice
                : null

          return {
            ...item,
            productName: item?.productName,
            unitPrice: (item?.unitPrice || 0).toFixed(2),
            packQty: parseFloat(item.packQty).toFixed(3) || '',
            palletQty: parseFloat(item.palletQty) || '',
            productDetail: { id: item?.productDetail?.id || null }
          }
        })
        this.productData = {
          ...this.productData,
          records: data.map((dt, idx) => {
            return {
              ...dt,
              id: idx,
              name: idx,
              __dirty__: '+'
            }
          })
        }
        this.productGrist.fetch()
      } else {
        throw response.errors
      }
    }
  }

  async importHandler(patches) {
    try {
      const orderProducts = patches.map(patch => {
        if (patch?.batchId) patch.batchId = patch.batchId.trim()
        if (patch?.batchIdRef) patch.batchIdRef = patch.batchIdRef.trim()

        if (patch.manufactureDate) {
          const mdt = new Date(patch.manufactureDate)
          patch.manufactureDate = mdt < new Date() ? `${mdt.getFullYear()}-${mdt.getMonth() + 1}-${mdt.getDate()}` : ''
        } else {
          delete patch.manufactureDate
        }
        delete patch.cuFlag
        delete patch.errMsg
        return {
          ...patch,
          uomValue: Math.round(parseFloat(patch.uomValue) * 1000) / 1000 || 0,
          packQty: Math.round(parseFloat(patch.packQty) * 1000) / 1000 || 0,
          palletQty: parseInt(patch.palletQty) || 0,
          unitPrice: patch.unitPrice ? parseFloat(patch.unitPrice) : null
        }
      })
      await this.bulkUploadValidateOrderProducts(orderProducts)
      history.back()
    } catch (e) {
      history.back()
      return
    }
  }

  async _importableData(records) {
    try {
      // Remap raw Excel header text keys to imex.key values
      const headerToKey = {}
      this.excelColumns()
        .filter(col => col.imex)
        .forEach(col => {
          headerToKey[col.imex.header.toLowerCase()] = col.imex.key
        })

      records = records.map(record => {
        const remapped = {}
        Object.entries(record).forEach(([rawKey, value]) => {
          if (!rawKey) return
          const mappedKey = headerToKey[rawKey.toLowerCase()]
          if (mappedKey) {
            const parts = mappedKey.split('.')
            if (parts.length === 2) {
              remapped[parts[0]] = remapped[parts[0]] || {}
              remapped[parts[0]][parts[1]] = value
            } else {
              remapped[mappedKey] = value
            }
          } else {
            remapped[rawKey] = value
          }
        })
        return remapped
      })

      if (!this.enableDirectInbound && records.some(record => record.location)) {
        throw new Error(
          'Location-based assignment is not allowed. Please delete the' +
          ' location ' +
          'column or enable Direct GAN Order in settings.'
        )
      }
      if (this._bizplaceId) {
        records = records
          .filter(record => record.productSKU)
          .map(record => {
            let warehouseName
            if (this.enableDirectInbound) {
              const locationItem = record.location?.name
              const locationName = typeof locationItem === 'object' ? locationItem?.name : locationItem
              const locationId = typeof locationItem === 'object' ? locationItem?.id : undefined
              const match = locationName?.match(/(.*) \((.*)\)/)
              if (match && match.length === 3) {
                warehouseName = match[2].trim()
                record = {
                  ...record,
                  location: { id: locationId, name: match[1].trim() },
                  warehouseName
                }
              }
            }
            return {
              ...record,
              unitPrice:
                record.unitPrice == null && record.unitPrice !== undefined
                  ? '0.00'
                  : record.unitPrice === undefined || record.unitPrice == ""
                    ? null
                    : parseFloat(parseFloat(record.unitPrice).toFixed(2)),
              packQty:
                record.packQty == null && record.packQty !== undefined
                  ? '0'
                  : record.packQty === undefined || record.packQty == ""
                    ? null
                    : parseFloat(parseFloat(record.packQty).toFixed(3)),
              palletQty:
                record.palletQty == null && record.palletQty !== undefined
                  ? parseInt('0')
                  : record.palletQty === undefined || record.palletQty == ""
                    ? null
                    : parseInt(record.palletQty),
              uomValue: record.uomValue !== null && record.uomValue !== undefined ? parseFloat(record.uomValue) : ''
            }
          })

        this._validateProducts(records)
        openPopup(
          html`
            <import-pop-up
              .records=${records}
              .config=${{
              rows: this.productGristConfig.rows,
              columns: [...this.excelColumns().filter(column => column.imex !== undefined)]
            }}
              .importHandler="${await this.importHandler.bind(this)}"
              .enableDirectInbound=${this.enableDirectInbound}
            ></import-pop-up>
          `,
          {
            backdrop: true,
            escapable: false,
            size: 'large',
            title: i18next.t('title.import')
          }
        )
      } else {
        throw new Error(i18next.t('text.please_select_a_company'))
      }
    } catch (e) {
      this._showToast(e)
    }
  }

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

window.customElements.define('create-arrival-notice', CreateArrivalNotice)
