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 { ORDER_SOURCES } from '../constants'
import { decodeQR, isValidHttpUrl } from '../../util'

class PackingSerialNumberPopup 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 {
      orderInventory: Object,
      config: Object,
      data: Object,
      orderId: String,
      orderType: String,
      orderSource: String,
      orderPackageId: String,
      _inventoryItems: Object,
      _selectedSerialNumber: Object,
      qrProcessing: Number,
      qrConfig: Object
      // enableRemove: Boolean,
      // enableUpdateBtn: Boolean
    }
  }

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

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

  _focusOnSerialInput() {
    if (this.serialInput) {
      setTimeout(() => this.serialInput.focus(), 50)
    }
  }

  constructor() {
    super()
    this.clearView()
    // this.enableRemove = true
    // this.enableUpdateBtn = true
  }

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

      <form id="input-form" class="single-column-form" @keypress="${this.updateSerialNumber.bind(this)}">
        <input
          name="selectedSerialNumber"
          placeholder="-- Selected Serial Number --"
          disabled
          readonly
          class="mobile-full-width"
          value="${this._selectedSerialNumber.serialNumber}"
        />
        <barcode-scanable-input
          name="serialNumber"
          class="mobile-full-width"
          custom-input
          @keypress="${async e => {
            if (e.keyCode === 13) {
              e.preventDefault()
              await this._extractSerialNumber(e)
            }
          }}"
        ></barcode-scanable-input>
        <mwc-button
          name="setSerialNumber"
          class="mobile-full-width"
          raised
          @click=${this.updateSerialNumber.bind(this)}
          label="${i18next.t('button.update')}"
        ></mwc-button>
      </form>

      <div class="button-container">
        <mwc-button
          danger
          @click=${() => {
            this.dispatchEvent(new CustomEvent('close-serial-number-popup', {}))
            history.back()
          }}
          raised
          label="${i18next.t('button.back')}"
        ></mwc-button>
      </div>
    `
  }

  async firstUpdated() {
    this.clearView()

    this.config = {
      rows: {
        appendable: false,
        classifier: (record, rowIndex) => {
          return {
            emphasized: record.completed == true
          }
        },
        handlers: {
          click: async (columns, data, column, record, rowIndex) => {
            try {
              this.dataGrist.showSpinner()
              if (data.records.length && record) {
                this._selectedSerialNumber = {
                  ...record,
                  product: { id: record.product.id, name: record.product.name }
                }
                delete this._selectedSerialNumber.__seq__
                delete this._selectedSerialNumber.__origin__
              }
            } catch (ex) {
              console.log(ex)
            } finally {
              this.dataGrist.hideSpinner()
            }
          }
        }
      },
      pagination: { infinite: true },
      list: {
        fields: ['serialNumber']
      },
      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._inventoryItems = { ...this._inventoryItems, records: newData }
                this.undoSerialNumberPacking(record.id)
              } catch (e) {}
            }
          }
        },
        {
          type: 'string',
          name: 'serialNumber',
          header: i18next.t('field.serial_number'),
          width: 150
        },
        {
          type: 'string',
          name: 'sku',
          header: i18next.t('field.sku'),
          width: 150
        },
        {
          type: 'string',
          name: 'productName',
          header: i18next.t('field.product'),
          width: 300
        }
      ]
    }
    await this.fetchInventoryItems()
    this._focusOnSerialInput()
  }

  clearView() {
    this._selectedSerialNumber = {}
    if (this.serialInput) this.serialInput.value = ''
  }

  async fetchInventoryItems() {
    let filters = [
      { name: 'orderType', operator: 'eq', value: this.orderType },
      { name: 'orderId', operator: 'eq', value: this.orderId },
      { name: 'orderPackageId', operator: 'eq', value: this.orderPackageId }
    ]

    const sortings = []

    const response = await client.query({
      query: gql`
        query inventoryItemsByOrder($filters: [Filter!]!, $sortings: [Sorting!]!) {
          inventoryItemsByOrder(filters: $filters, sortings: $sortings) {
            items {
              id
              name
              status
              serialNumber
              outboundOrderId
              orderPackageId
              inventory {
                id
                name
              }
              product {
                id
                sku
                name
              }
              productDetail {
                id
                name
              }
            }
            total
          }
        }
      `,
      variables: { filters, sortings }
    })

    if (!response.errors) {
      this._inventoryItems = {
        records: response.data.inventoryItemsByOrder.items.map(item => ({
          ...item,
          sku: item.product.sku,
          productName: item.product.name
        }))
      }
    }
  }

  async _extractSerialNumber() {
    // QR scanning
    if (this.qrProcessing > 0 && isValidHttpUrl(this.serialInput.value)) {
      let input = this.serialInput.value.trim()

      // Get GTIN and serial number from QR code
      let data = await decodeQR(input, this.qrConfig)

      // Fill up form
      if (this.serialInput && data.serialNumber) {
        this.serialInput.value = data.serialNumber
      }
    }
  }

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

        const { id, name, status, serialNumber, outboundOrderId, orderPackageId, inventory, product, productDetail } =
          this._selectedSerialNumber

        const selectedSerialNumber = {
          id,
          name,
          status,
          serialNumber,
          outboundOrderId,
          orderPackageId,
          inventory: { id: inventory.id, name: inventory.name },
          product: { id: product.id, name: product.name },
          productDetail: { id: productDetail.id, name: productDetail.name }
        }

        const response = await client.mutate({
          mutation: gql`
            mutation replacePackedInventoryItem($selectedSerialNumber: InventoryItemPatch!, $newSerialNumber: String!) {
              replacePackedInventoryItem(
                selectedSerialNumber: $selectedSerialNumber
                newSerialNumber: $newSerialNumber
              ) {
                id
                name
                serialNumber
              }
            }
          `,
          variables: {
            selectedSerialNumber,
            newSerialNumber: this.serialInput.value.trim()
          }
        })

        if (!response.errors) {
          this.clearView()
          await this.fetchInventoryItems()
          this._focusOnSerialInput()
        }
      } catch (e) {
        this._showToast(e)
      }
    }
  }

  async undoSerialNumberPacking(id) {
    try {
      let response = await client.mutate({
        mutation: gql`
          mutation undoSerialNumberPacking(
            $releaseGoodId: String!
            $orderPackageId: String!
            $inventoryItemId: String!
          ) {
            undoSerialNumberPacking(
              releaseGoodId: $releaseGoodId
              orderPackageId: $orderPackageId
              inventoryItemId: $inventoryItemId
            )
          }
        `,
        variables: {
          releaseGoodId: this.orderId,
          orderPackageId: this.orderPackageId,
          inventoryItemId: id
        }
      })

      if (!response.errors) {
        this._selectedSerialNumber = {}
        this.dataGrist.fetch()
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  validate() {
    // validate for previous picking execution (multiple picking scanning)
    let foundSerialNumber = this.dataGrist.data.records.filter(itm => itm.serialNumber == this.serialInput.value.trim())
    if (foundSerialNumber.length > 0) {
      throw new Error(i18next.t('text.serial_number_has_been_scanned'))
    }

    if (!this._selectedSerialNumber.id) throw new Error(i18next.t('text.no_serial_number_selected'))

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

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

window.customElements.define('packing-serial-number-popup', PackingSerialNumberPopup)
