import '@things-factory/barcode-ui'
import '@things-factory/grist-ui'

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

import { MultiColumnFormStyles, SingleColumnFormStyles } from '@things-factory/form-ui'
import { i18next, localize } from '@things-factory/i18n-base'
import { client } from '@things-factory/shell'
import { isMobileDevice } from '@things-factory/utils'

import { INVENTORY_ITEM_CHANGE_TRANSACTION_TYPE, INVENTORY_ITEM_CHANGE_TYPE } from '../constants'

class SerialNumberListPopup extends localize(i18next)(LitElement) {
  static get styles() {
    return [
      MultiColumnFormStyles,
      SingleColumnFormStyles,
      css`
        :host {
          padding: 10px;
          display: flex;
          flex-direction: column;
          overflow-x: overlay;
          background-color: var(--main-section-background-color);
        }
        .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;
        }
        .button-container {
          padding: 10px 0 12px 0;
          text-align: center;
          display: flex;
          flex-direction: column;
        }

        @media (max-width: 460px) {
          .single-column-form {
            max-width: 100%;
          }
          .mobile-full-width {
            grid-column: span 12 / auto;
          }
        }
      `
    ]
  }

  static get properties() {
    return {
      inventory: Object,
      config: Object,
      data: Array,
      _selectedInventoryItem: Object,
      _currentSerialNumber: String,
      adjustmentType: String,
      enableRemove: Boolean,
      enableUpdateBtn: Boolean,
      checkDuplicate: Boolean
    }
  }

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

  get serialNumberInput() {
    return this.shadowRoot
      ?.querySelector('barcode-scanable-input[name=serialNumber]')
      ?.shadowRoot?.querySelector('input')
  }

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

  constructor() {
    super()
    this.enableRemove = true
    this.enableUpdateBtn = true
    this.checkDuplicate = false
  }

  render() {
    return html`
      <form class="multi-column-form">
        <fieldset>
          <legend>
            ${this.adjustmentType == 'APPROVAL'
              ? `${
                  this.inventory?.cartonId
                    ? this.inventory.cartonId
                    : this.inventory?.palletId
                    ? this.inventory.palletId
                    : ''
                } (${this.inventory.transactionType})`
              : `${this.inventory.product.sku} - ${`${this.inventory.product.name}`} ( ${this.data?.total || 0} / ${
                  this.inventory.qty
                } )`}
          </legend>
        </fieldset>
      </form>

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

      <form id="input-form" class="single-column-form">
        <input
          name="selectedSerialNumber"
          placeholder="-- Selected Serial Number --"
          disabled
          readonly
          class="mobile-full-width"
          value="${this._currentSerialNumber}"
        />

        ${this.enableUpdateBtn
          ? html`<barcode-scanable-input
                name="serialNumber"
                class="mobile-full-width"
                custom-input
              ></barcode-scanable-input>
              <mwc-button
                name="addSerialNumber"
                class="mobile-full-width"
                raised
                @click=${async e => {
                  this.createInventoryItemChange(INVENTORY_ITEM_CHANGE_TYPE.NEW.value)
                }}
                label="${i18next.t('button.add')}"
              ></mwc-button>

              <mwc-button
                name="setSerialNumber"
                class="mobile-full-width"
                raised
                @click=${async e => {
                  this.createInventoryItemChange(INVENTORY_ITEM_CHANGE_TYPE.MODIFIED.value)
                }}
                label="${i18next.t('button.update')}"
              ></mwc-button>`
          : ''}
      </form>

      ${this.enableUpdateBtn && this.enableRemove
        ? html`<div class="button-container">
            <mwc-button
              danger
              @click=${async e => {
                await this._confirmInventoryItemChange(e)
              }}
              raised
              label="${i18next.t('button.confirm')}"
            ></mwc-button>
          </div>`
        : ''}
    `
  }

  async firstUpdated() {
    this.clearView()

    this.config = {
      pagination: { infinite: true },
      rows: {
        appendable: false,
        handlers: {
          click: async (columns, data, column, record, rowIndex) => {
            try {
              this.dataGrist.showSpinner()
              if (record.type == '') {
                this._selectedInventoryItem = record
                this._currentSerialNumber = record.serialNumber
              }
            } catch (ex) {
              console.log(ex)
            } finally {
              this.dataGrist.hideSpinner()
            }
          }
        }
      },
      list: {
        fields: ['serialNumber', 'palletId', 'cartonId', 'remark']
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'gutter',
          gutterName: 'button',
          icon: this.enableRemove ? 'close' : '',
          handlers: {
            click: async (columns, data, column, record, rowIndex) => {
              if (this.enableRemove) {
                if (record.transactionType == INVENTORY_ITEM_CHANGE_TRANSACTION_TYPE.CHANGES.value) {
                  this.data = {
                    ...this.data,
                    records: data.records.filter((_, idx) => idx !== rowIndex)
                  }

                  if (record.type == INVENTORY_ITEM_CHANGE_TYPE.NEW.value) {
                    this.data.total -= 1
                  } else if (record.type == INVENTORY_ITEM_CHANGE_TYPE.REMOVED.value) {
                    this.data.total += 1
                  }
                } else {
                  this._selectedInventoryItem = record

                  let dt = this.data
                  dt.records[rowIndex].type =
                    record.type == INVENTORY_ITEM_CHANGE_TYPE.REMOVED.value
                      ? ''
                      : INVENTORY_ITEM_CHANGE_TYPE.REMOVED.value

                  this.data = { ...dt }

                  this.checkDuplicate === false
                    ? (this.data.total = dt.records.filter(
                        x => x.type != INVENTORY_ITEM_CHANGE_TYPE.REMOVED.value
                      ).length)
                    : ''
                }

                this.clearView()
              }
            }
          }
        },
        {
          type: 'string',
          name: 'serialNumber',
          header: i18next.t('field.serial_number'),
          label: true,
          width: 140
        },
        {
          type: 'string',
          name: 'palletId',
          header: i18next.t('field.lot_id'),
          label: true,
          width: 140
        },
        {
          type: 'string',
          name: 'cartonId',
          header: i18next.t('field.carton_id'),
          label: true,
          width: 140
        },
        {
          type: 'string',
          name: 'type',
          header: i18next.t('field.remark'),
          label: true,
          width: 400
        }
      ]
    }

    await this.fetchInventoryItems()
  }

  clearView() {
    this._selectedInventoryItem = null
    if (this.serialNumberInput) this.serialNumberInput.value = ''
    this._currentSerialNumber = ''
    this._checkDuplicated = false
  }

  async fetchInventoryItems() {
    if (this.adjustmentType == 'ADJUSTMENT') {
      let inventoryId = this.inventory.id
      let filters = []
      if (inventoryId) filters.push({ name: 'inventoryId', operator: 'eq', value: this.inventory.id })

      const sortings = [{ name: 'serialNumber' }]

      const response = await client.query({
        query: gql`
          query inventoryItemsWithChanges($filters: [Filter!]!, $pagination: Pagination, $sortings: [Sorting!]!) {
            inventoryItemsWithChanges(filters: $filters, pagination: $pagination, sortings: $sortings) {
              items {
                id
                name
                serialNumber
                type
                transactionType
                inventory {
                  id
                  name
                  palletId
                  cartonId
                }
                product {
                  id
                  name
                  sku
                }
              }
              total
            }
          }
        `,
        variables: { filters, sortings }
      })

      if (!response.errors) {
        let total = response.data.inventoryItemsWithChanges.total || 0
        let records =
          response.data.inventoryItemsWithChanges.items.map(item => {
            return {
              ...item,
              palletId: item.inventory.palletId,
              cartonId: item.inventory.cartonId
            }
          }) || []

        if (this.inventory?.inventoryItemChanges) {
          let existingInventoryItemChanges = JSON.parse(this.inventory.inventoryItemChanges)

          records.push(...existingInventoryItemChanges)

          total = records.length
        }

        this.data = {
          total,
          records
        }
      }
    } else if (this.adjustmentType == 'APPROVAL') {
      let inventoryChange = this.inventory
      let filters = []
      if (inventoryChange) {
        filters.push({ name: 'inventoryChange', operator: 'eq', value: inventoryChange.id })
      }

      const sortings = [{ name: 'serialNumber' }]

      const response = await client.query({
        query: gql`
          query inventoryItemChanges($filters: [Filter!]!, $pagination: Pagination, $sortings: [Sorting!]!) {
            inventoryItemChanges(filters: $filters, pagination: $pagination, sortings: $sortings) {
              items {
                id
                name
                serialNumber
                type
                status
              }
              total
            }
          }
        `,
        variables: { filters, sortings }
      })

      if (!response.errors) {
        let total = response.data.inventoryItemChanges.total || 0
        let records = response.data.inventoryItemChanges.items || []

        this.data = {
          total,
          records
        }
      }
    }
  }

  createInventoryItemChange(type) {
    try {
      if (type != INVENTORY_ITEM_CHANGE_TYPE.REMOVED.value) {
        if (this.serialNumberInput.value == '') throw new Error(i18next.t('text.please_key_in_new_serial_number'))
      }

      let inventoryItemChange = {
        serialNumber:
          type == INVENTORY_ITEM_CHANGE_TYPE.REMOVED.value
            ? this._selectedInventoryItem.serialNumber
            : this.serialNumberInput.value,
        type,
        transactionType: INVENTORY_ITEM_CHANGE_TRANSACTION_TYPE.CHANGES.value,
        inventory: {
          id: this.inventory.id,
          name: this.inventory.name
        },
        product: {
          id: this.inventory.product.id,
          name: this.inventory.product.name
        }
      }

      if (type == INVENTORY_ITEM_CHANGE_TYPE.MODIFIED.value || type == INVENTORY_ITEM_CHANGE_TYPE.REMOVED.value) {
        inventoryItemChange = {
          ...inventoryItemChange,
          palletId: this._selectedInventoryItem.palletId,
          cartonId: this._selectedInventoryItem.cartonId,
          inventoryItem: {
            id: this._selectedInventoryItem.id,
            name: this._selectedInventoryItem.name
          }
        }
      }

      const duplicated = this.data.records.findIndex(
        res =>
          res.transactionType == 'CHANGES' &&
          res.type == 'REMOVED' &&
          res.serialNumber == inventoryItemChange.serialNumber
      )

      if (duplicated > 0) {
        this.checkDuplicate = true
        throw new Error(i18next.t('text.duplicated_romoval_on_same_sn'))
      }

      this.checkDuplicate = false

      if (type == INVENTORY_ITEM_CHANGE_TYPE.NEW.value) this.data.total += 1
      this.data = {
        ...this.data,
        records: [...this.dataGrist.dirtyData.records, inventoryItemChange]
      }

      this.clearView()
    } catch (e) {
      this._showToast(e)
    }
  }

  deleteInventoryItemChange(rowIndex) {
    try {
      this.data = {
        ...this.data,
        records: data.records.filter((_, idx) => idx !== rowIndex)
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _confirmInventoryItemChange(e) {
    if (e.keyCode === 13 || e.type == 'click') {
      e.preventDefault()
      try {
        this.validateInventoryItemChange()

        this.dispatchEvent(
          new CustomEvent('confirm-serial-number-list', {
            detail: {
              inventoryItemChanges:
                this.data.records.filter(
                  itm =>
                    itm.transactionType == INVENTORY_ITEM_CHANGE_TRANSACTION_TYPE.CHANGES.value ||
                    itm.type == INVENTORY_ITEM_CHANGE_TYPE.REMOVED.value
                ) || []
            }
          })
        )
        history.back()
      } catch (e) {
        this._showToast(e)
      }
    }
  }

  validate() {
    // validate for previous picking execution (multiple picking scanning)
    if (this.selectedSerialInput.value == '') throw new Error(i18next.t('text.no_serial_number_selected'))

    if (this.serialNumberInput.value == '') throw new Error(i18next.t('text.please_key_in_new_serial_number'))
  }

  validateInventoryItemChange() {
    if (this.data?.total != this.inventory.qty) throw new Error(i18next.t('text.serial_number_not_tally_with_qty'))
  }

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

window.customElements.define('serial-number-list-popup', SerialNumberListPopup)
