import '@things-factory/biz-ui'
import '../../components/vas-templates'
import './popup/vas-create-popup'
import '@things-factory/grist-ui'

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 { VAS_BATCH_AND_PRODUCT_TYPE, VAS_BATCH_NO_TYPE, VAS_PRODUCT_TYPE } from '../../constants'

//vas ---inbound. keep it for inbound.
class CreateVasOrder extends localize(i18next)(PageView) {
  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;
        }
        .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;
        }
      `
    ]
  }

  static get properties() {
    return {
      inventoryGristConfig: Object,
      inventoryData: Object,
      vasGristConfig: Object,
      vasData: Object,
      _bizplaces: Object,
      _bizplaceId: String,
      _bizplaceName: String,
      _template: Object
    }
  }

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

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

  render() {
    return html`
      <form name="vasOrder" class="multi-column-form" autocomplete="off">
        <fieldset>
          <label>${i18next.t('label.company')}</label>
          <input type="hidden" name="bizplaceId" value="${this._bizplaceId}" />
          <input
            readonly
            type="text"
            name="bizplaceName"
            value="${this._bizplaceName}"
            @click="${this._openBizplacesSelector.bind(this)}"
          />
        </fieldset>
      </form>
      <div class="container">
        <div class="grist">
          <h2><mwc-icon>list_alt</mwc-icon>${i18next.t('title.target_products')}</h2>

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

          <h2><mwc-icon>list_alt</mwc-icon>${i18next.t('title.vas')}</h2>

          <data-grist
            id="vas-grist"
            .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
            .config=${this.vasGristConfig}
            .data="${this.vasData}"
          ></data-grist>
        </div>
      </div>
    `
  }

  constructor() {
    super()
    this.inventoryData = { records: [] }
    this.vasData = { records: [] }
  }

  get context() {
    return {
      title: i18next.t('title.create_vas_order'),
      actions: [
        {
          title: i18next.t('button.create'),
          action: this._generateVasOrder.bind(this),
          ...CommonButtonStyles.add
        }
      ],
      help: 'operato-wms/vas-order/create-vas-order'
    }
  }

  async pageInitialized() {
    await this.fetchBizplaces()
    this._configureInventoryGrist()

    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'),
          record: { align: 'center' },
          width: 150
        },
        {
          type: 'string',
          name: 'targetDisplay',
          header: i18next.t('field.target'),
          record: { align: 'center' },
          width: 250
        },
        {
          type: 'string',
          name: 'packingType',
          header: i18next.t('field.packing_type'),
          record: { align: 'center' },
          width: 250
        },
        {
          type: 'integer',
          name: 'qty',
          header: i18next.t('field.qty'),
          record: { align: 'center' },
          width: 100
        },
        {
          type: 'float',
          name: 'uomValue',
          header: i18next.t('field.uom_value'),
          record: { align: 'center' },
          width: 100
        },
        {
          type: 'integer',
          name: 'vasCount',
          header: i18next.t('field.vas_count'),
          record: { align: 'center' },
          width: 100
        }
      ]
    }
  }

  _configureInventoryGrist() {
    let editable = this._bizplaceId ? true : false

    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) => {
              const newData = data.records.filter((_, idx) => idx !== rowIndex)
              this.inventoryData = { ...this.inventoryData, records: newData }
              this.inventoryGrist.dirtyData.records = newData
              this._updateInventoryList()
            }
          }
        },
        {
          type: 'object',
          name: 'inventory',
          header: i18next.t('field.inventory_list'),
          record: {
            editable: editable,
            options: {
              queryName: 'inventoryProductGroup',
              basicArgs: { filters: [{ name: 'bizplace_id', operator: 'eq', value: this._bizplaceId }] },
              nameField: 'batchId',
              descriptionField: 'productName',
              select: [
                { name: 'productId', hidden: true },
                { name: 'uom', hidden: true },
                { name: 'batchId', header: i18next.t('field.batch_id') },
                { name: 'productName', header: i18next.t('field.product_info'), width: 280 },
                { name: 'packingType', header: i18next.t('field.packing_type') },
                { name: 'packingSize', header: i18next.t('field.packing_size') },
                {
                  name: 'remainQty',
                  header: i18next.t('field.remain_qty'),
                  ignoreCondition: true
                },
                {
                  name: 'remainUomValue',
                  header: i18next.t('field.remain_uom_value'),
                  ignoreCondition: true
                }
              ],
              list: { fields: ['batchId', 'productName', 'packingType', 'remainQty', 'remainUomValue'] }
            }
          },
          width: 300
        },
        {
          type: 'code',
          name: 'packingType',
          header: i18next.t('field.packing_type'),
          record: { codeName: 'PACKING_TYPES' },
          width: 150
        },
        {
          type: 'integer',
          name: 'remainQty',
          header: i18next.t('field.available_qty'),
          width: 140
        },
        {
          type: 'float',
          name: 'remainUomValue',
          header: i18next.t('field.available_uom_value'),
          width: 140
        }
      ]
    }
  }

  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
      } else {
        this._bizplaceId = ''
        this._bizplaceName = ''
      }

      return bizplaces
    }
  }

  _updateInventoryList() {
    const _selectedInv = (this.inventoryGrist.dirtyData.records || []).map(record => {
      return {
        batchId: record.inventory.batchId,
        packingType: record.inventory.packingType,
        packingSize: record.inventory.packingSize,
        productId: record.inventory.productId
      }
    })

    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 !== 'batch_product')]
          }

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

        return column
      })
    }

    this.inventoryData = {
      ...this.inventoryGrist.dirtyData,
      records: this.inventoryGrist.dirtyData.records.map(record => {
        return {
          ...record,
          ...record.inventory,
          product: {
            id: record.inventory.productId,
            name: record.inventory.productName
          }
        }
      })
    }

    this._updateVasTargets()
  }

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

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

    const standardProductList = isFieldChanged ? this.inventoryGrist.dirtyData.records : this.inventoryData.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
        }
      })
    }
  }

  async _generateVasOrder() {
    try {
      this._validate()
      const result = await CustomAlert({
        title: i18next.t('title.are_you_sure'),
        text: i18next.t('text.create_vas_order'),
        confirmButton: { text: i18next.t('button.confirm') },
        cancelButton: { text: i18next.t('button.cancel') }
      })

      if (!result.value) return

      await this._executeRelatedTrxs()

      const response = await client.mutate({
        mutation: gql`
          mutation generateVasOrder($vasOrder: NewVasOrder!) {
            generateVasOrder(vasOrder: $vasOrder) {
              id
              name
            }
          }
        `,
        variables: {
          vasOrder: this._getVasOrder()
        }
      })

      if (!response.errors) {
        this.inventoryData = { records: [] }
        this.vasData = { records: [] }
        this._template = null
        this._selectedVasRecord = null
        this._selectedVasRecordIdx = null
        navigate(`vas_order_detail/${response.data.generateVasOrder.name}`)
        this._showToast({ message: i18next.t('text.vas_order_created') })
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  _validate() {
    if (!this.vasGrist.dirtyData || !this.vasGrist.dirtyData.records || !this.vasGrist.dirtyData.records.length)
      throw new Error(i18next.t('text.invalid_data_in_list'))
    if (!this.vasData.records.every(record => record.ready)) throw new Error(i18next.t('text.there_is_not_ready_vas'))
  }

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

  _getVasOrder() {
    return {
      orderVass: this._getOrderVass(),
      customerBizplaceId: this._bizplaceId
    }
  }

  _getOrderVass() {
    if (!this.vasGrist.dirtyData || !this.vasGrist.dirtyData.records || !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()
  }

  openVasCreatePopup(record) {
    try {
      this.checkInventoryValidity()

      openPopup(
        html`
          <vas-create-popup
            .targetList="${this.inventoryGrist.dirtyData.records
              .map(record => {
                let packQty = record.remainQty
                const unitUomValue = record.remainUomValue / record.remainQty

                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,
                  totalUomValue: unitUomValue * packQty
                }
              })
              .filter(record => record.packQty > 0)}"
            .vasList="${this.vasData.records}"
            .bizplaceId="${this._bizplaceId}""
            .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)
    }
  }

  checkInventoryValidity() {
    if (!this.inventoryGrist.dirtyData.records || !this.inventoryGrist.dirtyData.records.length) {
      throw new Error(i18next.t('text.there_is_no_inventory'))
    }

    if (this.inventoryGrist.dirtyData.records.some(record => !record.inventory && !record.inventory.id))
      throw new Error(i18next.t('text.invalid_inventory'))

    return true
  }

  _openBizplacesSelector() {
    openPopup(
      html`
        <popup-partner-bizplaces-selector
          @selected="${e => {
            this._bizplaceId = e.detail.id
            this._bizplaceName = e.detail.name
            const inventoryRecords = this.inventoryGrist.dirtyData.records
            if (inventoryRecords && inventoryRecords.length > 0) {
              // clear all data in grist
              this.inventoryGrist.dirtyData.records.length = 0
              this.vasGrist.reset()
            }
            this._configureInventoryGrist()
          }}"
        ></popup-partner-bizplaces-selector>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.select_company')
      }
    )
  }

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

window.customElements.define('create-vas-order', CreateVasOrder)
