import '@things-factory/biz-ui'
import '@things-factory/grist-ui'
import './return-order-inventory-popup'

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

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 { ORDER_INVENTORY_STATUS, ORDER_TYPES } from '../../constants'

class CreateReturnOrder extends localize(i18next)(PageView) {
  static get properties() {
    return {
      _searchFields: Array,
      _returnNo: String,
      _refNo: String,
      _etaDate: Date,
      _deliveryOrderNo: String,
      _truckNo: String,
      _checkTransport: Boolean,
      _ownTransport: Boolean,
      _warehouseTransport: Boolean,
      _enableTransportationServiceSetting: Boolean,
      _disableTransport: Boolean,
      inventoryGristConfig: Object,
      inventoryData: Object,
      _bizplaces: Object,
      _bizplaceId: String,
      _bizplaceName: String,
      releaseOrderNo: String,
      _releasegood: Object
    }
  }

  static get styles() {
    return [
      MultiColumnFormStyles,
      css`
        :host {
          display: flex;
          flex-direction: column;
          overflow-x: auto;
        }
        .container {
          flex: 1;
          display: flex;
          overflow-y: auto;
          min-height: 50vh;
        }
        .picking-std-container {
          margin-top: 0px;
        }

        [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;
        }
        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);
        }

        [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() {
    return {
      title: i18next.t('title.create_return_order'),
      actions: [
        {
          title: i18next.t('button.submit'),
          action: this._generateReturnOrder.bind(this),
          ...CommonButtonStyles.submit
        }
      ],
      help: 'operato-wms/return-order/create-return-order'
    }
  }

  render() {
    return html`
      <form name="returnOrder" class="multi-column-form" autocomplete="off">
        <fieldset>
          <legend>${i18next.t('title.return_order')}</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>
          </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')}</label>
          <input name="refNo" ?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._ownTransport}">${i18next.t('label.do_no')}</label>
          <input name="deliveryOrderNo" ?hidden="${!this._ownTransport}" />

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

          <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">
          <h2><mwc-icon>list_alt</mwc-icon>${i18next.t('title.return_product_list')}</h2>

          <data-grist
            id="inventory-grist"
            .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
            .config=${this.inventoryGristConfig}
            .data=${this.inventoryData}
            @record-change="${this._onProductChangeHandler.bind(this)}"
          ></data-grist>
        </div>
      </div>
    `
  }

  constructor() {
    super()
    this.initProperties()
  }

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

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

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

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

  async pageUpdated(changes, lifecycle) {
    if (this.active) {
      this.resetPage()
      this.releaseOrderNo = lifecycle.params.releaseOrderNo || null

      const [bizplaces, enableTransportationServiceSetting] = await Promise.all([
        this.fetchBizplaces(),
        fetchSettingRule('enable-transportation-service')
      ])

      this._bizplaces = bizplaces
      this._enableTransportationServiceSetting = enableTransportationServiceSetting
      this._validateTransport()

      if (this.releaseOrderNo) {
        const { bizplace } = await this.fetchReleaseGood()
        this._bizplaceId = bizplace?.id
        this._bizplaceName = bizplace?.name
      } else {
        this.releaseOrderNo = null
      }
    }
  }

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

  pageInitialized() {
    this._configureInventoryGrist()
  }

  initProperties() {
    this.releaseOrderNo = null
    this._disableTransport = false
    this._ownTransport = false
    this._warehouseTransport = false
    this._checkTransport = false

    // if (this._ownTransportInput) {
    //   this._ownTransportInput.checked = false
    // }

    if (this._etaDateInput) {
      this._etaDateInput.value = ''
    }

    if (this._refNoInput) {
      this._refNoInput.value = ''
    }

    if (this._deliveryOrderNoInput) {
      this._deliveryOrderNoInput.value = ''
    }

    if (this._truckNoInput) {
      this._truckNoInput.value = ''
    }

    this.inventoryData = { ...this.inventoryData, records: [] }
  }

  resetPage() {
    this.returnOrderForm.reset()
    this.initProperties()
    this._clearGristConditions()
  }

  _configureInventoryGrist() {
    this.inventoryGristConfig = {
      pagination: { infinite: true },
      rows: { selectable: { multiple: true } },
      list: { fields: ['inventory', 'product', 'location', 'releaseQty'] },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'close',
          handlers: {
            click: (columns, data, column, record, rowIndex) => {
              try {
                const newData = data.records.filter((_, idx) => idx !== rowIndex)
                this.inventoryData = { ...this.inventoryData, records: newData }
                this.inventoryGrist.dirtyData.records = newData
                this._updateInventoryList()
              } catch (e) {
                this._showToast(e)
              }
            }
          }
        },
        {
          type: 'string',
          name: 'palletId',
          header: i18next.t('field.inventory_list'),
          handlers: {
            click: () => {
              try {
                this._openReturnOrderSelector()
              } catch (e) {
                this._showToast(e)
              }
            }
          },
          record: {
            editable: true,
            options: {
              queryName: 'inventoriesByOrder',
              basicArgs: {
                filters: [
                  { name: 'status', operator: 'eq', value: ORDER_INVENTORY_STATUS.TERMINATED.value },
                  { name: 'type', operator: 'eq', value: ORDER_TYPES.RELEASE_OF_GOODS.value },
                  { name: 'bizplace_id', operator: 'eq', value: this._bizplaceId }
                ]
              },
              nameField: 'batchId',
              descriptionField: 'productName',
              select: [
                { name: 'id', hidden: true },
                { name: 'name', hidden: true },
                { name: 'productId', hidden: true },
                { name: 'releaseOrderId', hidden: true },
                {
                  name: 'releaseGoodName',
                  header: i18next.t('field.ro')
                },
                {
                  name: 'batchId',
                  header: i18next.t('field.batch_id'),
                  ignoreCondition: true
                },
                {
                  name: 'productName',
                  header: i18next.t('field.product_name'),
                  width: 280,
                  ignoreCondition: true
                },
                {
                  name: 'productBrand',
                  header: i18next.t('field.brand'),
                  ignoreCondition: true
                },
                {
                  name: 'packingType',
                  header: i18next.t('field.packing_type'),
                  ignoreCondition: true
                },
                {
                  name: 'packingSize',
                  header: i18next.t('field.packing_size'),
                  ignoreCondition: true
                },
                {
                  name: 'releaseQty',
                  header: i18next.t('field.return_qty'),
                  ignoreCondition: true
                },
                {
                  name: 'releaseUomValue',
                  header: i18next.t('field.return_uom_value'),
                  ignoreCondition: true
                }
              ],
              list: {
                fields: [
                  'releaseGoodName',
                  'batchId',
                  'productName',
                  'productBrand',
                  'packingType',
                  'packingSize',
                  'releaseQty',
                  'releaseUomValue'
                ]
              }
            }
          },
          width: 200
        },
        {
          type: 'string',
          name: 'releaseGoodName',
          header: i18next.t('field.ro'),
          width: 150
        },
        {
          type: 'string',
          name: 'productName',
          header: i18next.t('field.product_name'),
          width: 150
        },
        {
          type: 'string',
          name: 'productBrand',
          header: i18next.t('field.brand'),
          width: 150
        },
        {
          type: 'string',
          name: 'batchId',
          header: i18next.t('field.batch_id'),
          width: 150
        },
        {
          type: 'string',
          name: 'packingType',
          header: i18next.t('field.packing_type'),
          width: 150
        },
        {
          type: 'float',
          name: 'packingSize',
          header: i18next.t('field.packing_size'),
          width: 150
        },
        {
          type: 'float',
          name: 'releaseQty',
          header: i18next.t('field.release_qty'),
          width: 140
        },
        {
          type: 'float',
          name: 'returnQty',
          header: i18next.t('field.return_qty'),
          record: { editable: true, options: { min: 0 } },
          width: 140
        },
        {
          type: 'float',
          name: 'releaseUomValue',
          header: i18next.t('field.release_uom_value'),
          width: 140
        },
        {
          type: 'float',
          name: 'returnUomValue',
          header: i18next.t('field.return_uom_value'),
          record: { editable: true, options: { min: 0 } },
          width: 140
        },
        {
          type: 'string',
          name: 'remark',
          header: i18next.t('field.remark'),
          record: { editable: true },
          width: 300
        }
      ]
    }
  }

  _clearGristConditions() {
    if (this.inventoryGristConfig?.columns?.length) {
      this.inventoryGristConfig = {
        ...this.inventoryGristConfig,
        columns: this.inventoryGristConfig.columns.map(column => {
          if (column.name === 'inventory') {
            column.record.options.basicArgs = {
              filters: column.record.options.basicArgs.filters.filter(
                filter => filter.name !== 'batch_product' && filter.name !== 'id'
              )
            }
          }

          return column
        })
      }
    }
  }

  _updateInventoryList() {
    const _selectedInventories = (this.inventoryGrist.dirtyData.records || []).map(record => record.inventory.id)

    this.inventoryGristConfig = {
      ...this.inventoryGristConfig,
      columns: this.inventoryGristConfig.columns.map(column => {
        if (column.name === 'inventory') {
          column.record.options.basicArgs = {
            ...column.record.options.basicArgs,
            filters: [...column.record.options.basicArgs.filters.filter(filter => filter.name !== 'id')]
          }

          if (_selectedInventories.length) {
            column.record.options.basicArgs.filters = [
              ...column.record.options.basicArgs.filters,
              {
                name: 'inventoryId',
                value: _selectedInventories,
                operator: 'notin'
              }
            ]
          }
        }

        return column
      })
    }
  }

  //Fields Changed Handler
  _onProductChangeHandler(event) {
    const changeRecord = event.detail.after
    const changedColumn = event.detail.column.name

    try {
      if (changedColumn === 'returnQty') {
        // Validate decimal quantities based on isInventoryDecimal
        if (!changeRecord.isInventoryDecimal) {
          // For non-decimal products, check if the quantity is a whole number
          if (changeRecord.returnQty % 1 !== 0) {
            throw new Error(i18next.t('text.decimal_quantities_not_allowed'))
          }
        } else {
          // For decimal products, check if the quantity has more than 3 decimal places
          const decimalPlaces = (changeRecord.returnQty.toString().split('.')[1] || '').length
          if (decimalPlaces > 3) {
            throw new Error(i18next.t('text.decimal_places_exceed_limit'))
          }
        }
        this._validateChangedValue(changeRecord.returnQty, changeRecord.releaseQty, 'field.qty')
      } else if (changedColumn === 'returnUomValue') {
        this._validateChangedValue(changeRecord.returnUomValue, changeRecord.releaseUomValue, 'field.uom_value')
      }
      this._updateReturnQtyAndUomValue(event.detail.row, changedColumn, event.detail.before, event.detail.after)
      this._updateInventoryList()
    } 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)
    }
  }

  _updateReturnQtyAndUomValue(rowIndex, columnName, initialRecord, newRecord) {
    let roundedUomValue = 0
    let returnQty = 0

    if (columnName == 'returnUomValue' || columnName == 'returnQty') {
      if (
        initialRecord.releaseUomValue &&
        initialRecord.releaseQty &&
        initialRecord.releaseUomValue > 0 &&
        initialRecord.releaseQty > 0
      ) {
        let packageUomValue = initialRecord.releaseUomValue / initialRecord.releaseQty

        if (columnName === 'returnQty') {
          returnQty = newRecord.returnQty || 0
        } else {
          returnQty = Math.round(newRecord.returnUomValue / packageUomValue)
        }

        roundedUomValue = returnQty * packageUomValue
        roundedUomValue = parseFloat(roundedUomValue.toFixed(3))
      }
    }

    this.inventoryData = {
      ...this.inventoryGrist.dirtyData,
      records: this.inventoryGrist.dirtyData.records.map((record, idx) => {
        if ((columnName == 'returnUomValue' || columnName == 'returnQty') && idx === rowIndex) {
          if (columnName == 'returnUomValue') record.returnQty = returnQty
          record.returnUomValue = roundedUomValue
        }

        return {
          ...record,
          ...record.inventory
        }
      })
    }
  }

  //Validation
  _validateChangedValue(returnValue, releaseValue, fieldVariable) {
    if (releaseValue === undefined) throw new Error(i18next.t('text.there_is_no_selected_items'))
    if (returnValue > releaseValue) {
      throw new Error(i18next.t('text.available_x_insufficient', { state: { x: i18next.t(fieldVariable) } }))
    } else if (returnValue <= 0 || isNaN(returnValue)) {
      throw new Error(i18next.t('text.invalid_x_input', { state: { x: i18next.t(fieldVariable) } }))
    }
  }

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

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

  _validateForm() {
    if (!this.returnOrderForm.checkValidity()) {
      throw new Error(i18next.t('text.return_order_form_invalid'))
    }

    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'))
    }
  }

  _validateInventories() {
    const inventories = this.inventoryGrist?.dirtyData?.records
    if (!inventories?.length) throw new Error(i18next.t('text.no_products'))
    // required field (batchId, packingType, uomValue, unit, packQty)
    if (!inventories.every(record => record.returnQty && record.batchId && record.packingType)) {
      throw new Error(i18next.t('text.empty_value_in_list'))
    }

    if (inventories.some(record => record.returnQty > record.releaseQty)) {
      throw new Error(i18next.t('text.invalid_quantity_input'))
    }
  }

  async fetchBizplaces() {
    const filters = []

    const response = await client.query({
      query: gql`
        query partnersBizplaces($filters: [Filter!]) {
          partnersBizplaces(filters: $filters) {
            items {
              id
              name
            }
          }
        }
      `,
      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
      } else {
        this._bizplaceId = ''
        this._bizplaceName = ''
      }
      return bizplaces
    }
  }

  async fetchReleaseGood() {
    const response = await client.query({
      query: gql`
        query releaseGood($name: String!) {
          releaseGood(name: $name) {
            id
            name
            bizplace {
              id
              name
            }
          }
        }
      `,
      variables: { name: this.releaseOrderNo }
    })

    if (!response.errors) {
      return response.data.releaseGood
    }
  }

  //Generate Order
  async _generateReturnOrder() {
    try {
      this._validateForm()
      this._validateInventories()

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

      if (!result.value) return

      let returnOrder = this._serializeForm(this.returnOrderForm)
      delete returnOrder.warehouseTransport
      returnOrder.orderInventories = this._getOrderInventories()
      returnOrder.ownTransport = this._ownTransport
      const attachments = this._document?.files ? this._document.files : undefined
      const response = await client.mutate({
        mutation: gql`
          mutation generateReturnOrder($returnOrder: NewReturnOrder!, $attachments: [Upload!]) {
            generateReturnOrder(returnOrder: $returnOrder, file: $attachments) {
              id
              name
            }
          }
        `,
        variables: { returnOrder: { ...returnOrder, customerBizplaceId: this._bizplaceId }, attachments },
        context: { hasUpload: true }
      })

      if (!response.errors) {
        navigate(`return_order_detail/${response.data.generateReturnOrder.name}`)
        this._showToast({ message: i18next.t('text.return_order_created') })
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  _getOrderInventories() {
    return this.inventoryGrist.data.records.map(record => {
      let newRecord = {
        returnQty: record.returnQty,
        returnUomValue: record?.returnUomValue ? record.returnUomValue : 0,
        remark: record?.remark ? '[RETURN]' + record.remark : '[RETURN]',
        batchId: record?.batchId,
        packingType: record?.packingType,
        packingSize: record?.packingSize,
        type: ORDER_TYPES.RETURN_ORDER.value,
        status: ORDER_INVENTORY_STATUS.PENDING.value
      }
      newRecord.releaseOrderId = record?.releaseOrderId
      newRecord.product = { id: record?.productId, name: record?.productName }
      newRecord.productDetail = { id: record?.productDetailId }
      newRecord.inventory = { id: record.id }
      return newRecord
    })
  }

  _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
  }

  _openBizplacesSelector() {
    openPopup(
      html`
        <popup-partner-bizplaces-selector
          @selected="${e => {
            this._bizplaceId = e.detail.id
            this._bizplaceName = e.detail.name
            this.inventoryGrist.reset()
          }}"
        ></popup-partner-bizplaces-selector>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.select_company')
      }
    )
  }

  _openReturnOrderSelector() {
    if (this._bizplaceId) {
      openPopup(
        html`
          <return-order-inventory-popup
            .bizplaceId=${this._bizplaceId}
            .releaseOrderNo=${this.releaseOrderNo}
            .inventoryData=${this.inventoryData}
            @selected="${e => {
              this.inventoryData.records = [...this.inventoryData.records, ...e.detail?.selectedData]
              this.releaseOrderNo = e.detail.releaseOrderNumber || this.releaseOrderNumber
              this.inventoryGrist.reset()
            }}"
          >
          </return-order-inventory-popup>
        `,
        {
          backdrop: true,
          size: 'large',
          title: i18next.t('title.select_item')
        }
      )
    } else {
      this._showToast({ message: i18next.t('text.please_select_a_company') })
    }
  }

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

window.customElements.define('create-return-order', CreateReturnOrder)
