import '@things-factory/grist-ui'
import '@things-factory/barcode-ui'
import '../components/popup-note'
import '@things-factory/form-ui'
import '@things-factory/import-ui'

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 { showToast } from '../../util/index'
import {
  ORDER_STATUS,
  ORDER_TYPES,
  REPLENISHMENT_CONFIG
} from '../constants'

class Replenishment extends localize(i18next)(PageView) {
  static get styles() {
    return [
      MultiColumnFormStyles,
      css`
        :host {
          display: flex;
          flex-direction: column;
          overflow-x: auto;
        }
        .form-container {
          display: flex;
        }
        .form-container > form {
          flex: 1;
        }
        .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;
        }
        barcode-tag {
          width: 100px;
          height: 100px;
          margin: 10px;
        }
        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);
        }

        search-form {
          overflow: visible;
        }
      `
    ]
  }

  static get properties() {
    return {
      searchFields: Array,
      config: Object,
      _pageType: String,
      _status: String,
      _orderNo: String
    }
  }

  get context() {
    switch (this._pageType) {
      case 'create':
        return {
          title: i18next.t('title.create_replenishment'),
          actions: [
            {
              title: i18next.t('button.create_replenishment'),
              action: this._createReplenishment.bind(this),
              ...CommonButtonStyles.activate
            }
          ],
          exportable: {
            name: i18next.t('title.create_replenishment'),
            data: this._exportableData.bind(this)
          },
          importable: {
            handler: this.importableData.bind(this)
          }
        }
      case 'list':
        return {
          title: i18next.t('title.replenishment_list'),
          actions: [
            {
              title: i18next.t('button.generate_worksheet'),
              action: this._generateWorksheets.bind(this),
              ...CommonButtonStyles.submit
            }
          ]
        }
    }
  }

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

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

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

  constructor() {
    super()
  }

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

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

  async pageUpdated(changes, lifecycle, before) {
    const replenishmentStatus = await getCodeByName('REPLENISHMENT_STATUS')

    if (this.active) {
      this._pageType = lifecycle.resourceId
    }

    switch (this._pageType) {
      case 'create':
        this.config = REPLENISHMENT_CONFIG.CREATE
        break
      case 'list':
        this.config = REPLENISHMENT_CONFIG.LIST
        break
    }

    const partners = [...(await this._fetchPartners())]

    this.searchFields =
      this._pageType == 'create'
        ? [
            {
              label: i18next.t('field.company'),
              name: 'bizplace',
              type: 'select',
              options: [
                { value: '' },
                ...partners
                  .map(partner => {
                    return {
                      name: `${partner.name} ${partner.description ? `(${partner.description})` : ''}`,
                      value: partner.id
                    }
                  })
                  .sort(this._compareValues('name', 'asc'))
              ],
              props: { searchOper: 'eq' }
            },
            {
              label: i18next.t('field.product_info'),
              name: 'product',
              props: { searchOper: 'eq' }
            }
          ]
        : [
            {
              label: i18next.t('field.order_no'),
              name: 'name',
              props: { searchOper: 'i_like' }
            },
            {
              label: i18next.t('field.status'),
              name: 'status',
              type: 'select',
              options: [
                { value: '' },
                ...replenishmentStatus.map(status => {
                  return { name: status.description, value: status.name }
                })
              ],
              props: { searchOper: 'eq' }
            }
          ]

    this.dataGrist.fetch()
  }

  async pageInitialized() {}

  fetchHandler({ page, limit, sorters = [] }) {
    this._clearView()
    switch (this._pageType) {
      case 'create':
        return this._fetchStorageInventories({ page, limit, sorters })
      case 'list':
        return this._fetchReplenishments({ page, limit, sorters })
    }
  }

  async _fetchStorageInventories({ page, limit, sorters }) {
    const response = await client.query({
      query: gql`
        query replenishmentInventories($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
          replenishmentInventories(filters: $filters, pagination: $pagination, sortings: $sortings) {
            items {
              productDetailId
              productSKU
              packingType
              packingSize
              minQty
              pickingShelfQty
              storageQty
              transferQty
              bizplace {
                id
                name
              }
            }
            total
          }
        }
      `,
      variables: {
        filters: this.searchForm.queryFilters,
        pagination: { page, limit },
        sortings: sorters
      }
    })

    if (!response.errors) {
      this.searchForm.form.reset()
      return {
        total: response.data.replenishmentInventories.total || 0,
        records: response.data.replenishmentInventories.items || []
      }
    }
  }

  async _fetchReplenishments({ page, limit, sorters }) {
    const response = await client.query({
      query: gql`
        query replenishments($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
          replenishments(filters: $filters, pagination: $pagination, sortings: $sortings) {
            items {
              id
              name
              status
              createdAt
              creator {
                id
                name
              }
            }
            total
          }
        }
      `,
      variables: {
        filters: await this.searchForm.getQueryFilters(),
        pagination: { page, limit },
        sortings: sorters
      }
    })

    if (!response.errors) {
      this.searchForm.form.reset()
      return {
        total: response.data.replenishments.total || 0,
        records: response.data.replenishments.items || []
      }
    }
  }

  _validateForm() {
    //check if transfer qty is less than storage qty
    let invalidRow = this.dataGrist.selected.find(itm => itm.transferQty > itm.storageQty)

    if (invalidRow) {
      throw new Error(i18next.t('text.insufficient_storage_qty'))
    }
  }

  async _createReplenishment() {
    try {
      this._validateForm()

      let orderInventories = await this._getOrderInventories()

      if (orderInventories.length < 1) {
        throw new Error(i18next.t('text.please_select_product_to_replenish'))
      }

      let result
      if (this.dataGrist.selected.some( itm => !itm.transferQty || itm.transferQty <= 0 || isNaN(itm.transferQty)) ) {
        result = await CustomAlert({
          title: i18next.t('title.are_you_sure'),
          text: i18next.t('text.only_transfer_quantity_with_greater_than_zero'),
          confirmButton: { text: i18next.t('button.submit') },
          cancelButton: { text: i18next.t('button.cancel') }
        })
      } else {
        result = await CustomAlert({
          title: i18next.t('title.are_you_sure'),
          text: i18next.t('text.create_replenishment'),
          confirmButton: { text: i18next.t('button.submit') },
          cancelButton: { text: i18next.t('button.cancel') }
        })
      }


      if (!result.value) return

      const response = await client.mutate({
        mutation: gql`
          mutation ($replenishInventories: [ReplenishmentInventoryPatch!]!) {
            createReplenishment(replenishInventories: $replenishInventories) {
              id
              name
            }
          }
        `,
        variables: {
          replenishInventories: orderInventories
        }
      })

      if (!response.errors) {
        navigate(`replenishment/list`)
        showToast({ message: i18next.t('text.replenishment_created') })
      }
    } catch (e) {
      showToast(e)
    }
  }

  async _generateWorksheets() {
    try {
      let replenishmentNos = []
      if (this.dataGrist.selected.length <= 0 && this._pageType == 'list')
        throw new Error(i18next.t('text.please_select_one_or_more_order_to_generate_worksheet'))

      let availableReplenishments = this.dataGrist.selected.filter(
        itm => itm.status === ORDER_STATUS.PENDING_WORKSHEET.value
      )

      if (availableReplenishments.length > 0) {
        replenishmentNos = availableReplenishments.map(record => record.name)
      } else {
        if (this._status == ORDER_STATUS.PENDING_WORKSHEET.value) {
          replenishmentNos.push(this._orderNo)
        }
      }

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

      const response = await client.mutate({
        mutation: gql`
          mutation ($replenishmentNos: [String!]) {
            generateReplenishmentWorksheets(replenishmentNos: $replenishmentNos)
          }
        `,
        variables: {
          replenishmentNos
        }
      })

      if (!response.errors) {
        navigate(`replenishment_worksheet_list`)
        showToast({ message: i18next.t('text.worksheet(s)_has_been_generated') })
      }
      this._processing = false
    } catch (e) {
      showToast(e)
    }
  }

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

  async _getOrderInventories() {
    const orderInventories = this.dataGrist.selected
    .filter(record => record.transferQty > 0)
    .map(record => {
      let newRecord = {
        bizplaceId: record.bizplace.id,
        releaseQty: record.transferQty,
        packingType: record.packingType,
        packingSize: record.packingSize,
        type: ORDER_TYPES.REPLENISHMENT.value,
        productDetailId: record.productDetailId
      }

      return newRecord
    })

    return orderInventories
  }

  _fillupForm(form, data) {
    if (form) {
      form.reset()
      for (let key in data) {
        Array.from(form.querySelectorAll('input, textarea')).forEach(field => {
          if (field.name === key && field.type === 'checkbox') {
            field.checked = data[key]
          } else if (field.name === key && field.type === 'datetime-local') {
            const datetime = Number(data[key])
            const timezoneOffset = new Date(datetime).getTimezoneOffset() * 60000
            field.value = new Date(datetime - timezoneOffset).toISOString().slice(0, -1)
          } else if (field.name === key) {
            if (data[key] instanceof Object) {
              const objectData = data[key]
              field.value = `${objectData.name} ${objectData.description ? `(${objectData.description})` : ''}`
            } else {
              field.value = data[key]
            }
          }
        })
      }
    }
  }

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

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

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

  _onInventoryFieldChanged(e) {
    let transferQty = e.detail.after
    if (isNaN(transferQty) || transferQty < 0) {
      let beforeValue = e.detail.before
      if (!beforeValue) {
        beforeValue = 0
      }
      e.detail.after = beforeValue
      this.dataGrist.dirtyData.records[e.detail.row].transferQty = beforeValue

      showToast({ message: i18next.t('text.invalid_transfer_qty') })
    }
  }

  _clearView() {
    this._orderNo = ''
    this._status = ''
  }

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

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

    var data = records.map(item => {
      return {
        id: item.id,
        ...this.config.columns
          .filter(column => column.type !== 'gutter' && column.imex !== undefined)
          .reduce((record, column) => {
            record[column.imex.key] = column.imex.key
              .split('.')
              .reduce((obj, key) => (obj && obj[key] !== 'undefined' ? obj[key] : undefined), item)
            return record
          }, {})
      }
    })

    return { header: headerSetting, data: data }
  }

  async importHandler(patches) {
    try {
      patches.map(itm => {
        const existingRecord = this.dataGrist.dirtyData.records.find(record => record.productSKU === itm.productSKU)
        if (!existingRecord) {
          throw new Error(
            i18next.t('text.transfer_quantity_cannot_be_uploaded_as_it_does_not_meet_the_minimum_required_quantity')
          )
        }
        if (
          !itm.bizplace.name ||
          !itm.productSKU ||
          !itm.packingType ||
          !itm.packingSize ||
          itm.pickingShelfQty == null ||
          !itm.storageQty ||
          !itm.minQty ||
          itm.transferQty == null ||
          ''
        ) {
          throw new Error(i18next.t('text.empty_value_in_list'))
        }
        if (isNaN(itm.pickingShelfQty) || isNaN(itm.storageQty) || isNaN(itm.minQty) || isNaN(itm.transferQty)) {
          throw new Error(i18next.t('text.invalid_value_in_list'))
        }
        if (itm.pickingShelfQty < 0 || itm.storageQty < 1 || itm.minQty < 1 || itm.transferQty < 0) {
          throw new Error(i18next.t('text.invalid_value_in_list'))
        }
      })

      patches.forEach(patch => {
        const record = this.dataGrist.dirtyData.records.find(record => record.productSKU === patch.productSKU && record.packingType === patch.packingType)
        if (record) {
          record.transferQty = parseFloat(patch.transferQty)
        }
      })

      history.back()
      this.dataGrist.records = patches
      this.dataGrist.refresh() 
    } catch (e) {
      showToast(e)
    }
  }

  importableData(records) {
    setTimeout(() => {
      openPopup(
        html`
          <import-pop-up
            .records=${records.map(record => {
              return {
                ...record,
                transferQty: record.transferQty ? record.transferQty : 0,
                pickingShelfQty: record.pickingShelfQty ? record.pickingShelfQty : 0
              }
            })}
            .config=${{
              rows: this.config.rows,
              columns: this.config.columns
            }}
            .importHandler="${this.importHandler.bind(this)}"
          ></import-pop-up>
        `,
        {
          backdrop: true,
          size: 'large',
          title: i18next.t('title.import')
        }
      )
    }, 500)
  }
}

window.customElements.define('replenishment-page', Replenishment)
