import '@things-factory/barcode-ui'
import '@things-factory/grist-ui'
import './dispatcher-verification-popup'

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

import { GhostPrint } from '@operato/ghost-print'
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 { MANIFEST_STATUS, ORDER_STATUS } from '../constants'

class LoadingManifestDetail 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);
        }

        search-form {
          bottom: 10px;
        }

        h2 + data-grist {
          padding-top: var(--grist-title-with-grid-padding);
        }

        .add[collapsable] {
          height: 10%;
          margin-top: -5px;
        }
        .add[collapsable][collapsed] {
          display: none;
        }
        h2.add-title {
          padding-bottom: 0px;
        }

        .refresh-btn {
          font-size: 26px !important;
          float: right !important;
          margin-right: 10px !important;
        }

        [hidden] {
          display: none;
        }
      `
    ]
  }

  static get properties() {
    return {
      _searchFields: Array,
      shippingData: Object,
      shippingGristConfig: Object,
      _manifestNo: String,
      _status: String,
      _removePackages: Array,
      _selectedStatus: String,
      _addExpanded: Boolean,
      _companyList: Array
    }
  }

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

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

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

  get companyList() {
    return this.searchForm.shadowRoot.querySelector('select[name=companyList]')
  }

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

  _focusOnTrackingNoInput() {
    setTimeout(() => this.trackingNoInput.focus(), 50)
  }

  get context() {
    var actions = [
      {
        title: i18next.t('button.back'),
        action: () => navigate('loading_manifest_list'),
        ...CommonButtonStyles.back
      },
      {
        title: i18next.t('button.print'),
        action: this._printManifest.bind(this),
        ...CommonButtonStyles.label
      }
    ]

    if (this._status == MANIFEST_STATUS.OPEN.value || this._status == MANIFEST_STATUS.IN_QUEUE.value) {
      actions.push(
        {
          title: i18next.t('button.update'),
          action: this._updateManifestDetails.bind(this),
          ...CommonButtonStyles.add
        },
        {
          title: i18next.t('button.dispatch'),
          action: this._openDispatcherVerificationPopup.bind(this),
          ...CommonButtonStyles.load
        }
      )
    }

    return {
      title: i18next.t('title.manifest'),
      actions
    }
  }

  render() {
    return html`
      <div class="form-container">
        <form name="manifest" class="multi-column-form">
          <fieldset>
            <legend>${i18next.t('title.manifest_no')}: ${this._manifestNo}</legend>
            <label>${i18next.t('label.shipping_provider')}</label>
            <input name="shippingProvider" readonly />

            <label>${i18next.t('label.dispatcher_name')}</label>
            <input name="dispatcherName" readonly />

            <label>${i18next.t('label.dispatcher_identity')}</label>
            <input name="dispatcherIdentity" readonly />

            <label>${i18next.t('label.dispatch_at')}</label>
            <input name="dispatchedAt" type="datetime-local" readonly />

            <label>${i18next.t('label.total_tracking_no')}</label>
            <input name="totalTrackingNo" readonly />

            <label>${i18next.t('label.status')}</label>
            <select
              name="status"
              ?disabled=${this._status == MANIFEST_STATUS.CLOSED.value}
              @change="${e => {
                this._selectedStatus = e.currentTarget.value
              }}"
            >
              ${Object.keys(MANIFEST_STATUS).map(key => {
                const status = MANIFEST_STATUS[key]
                if (this._status == MANIFEST_STATUS.OPEN.value || this._status == MANIFEST_STATUS.IN_QUEUE.value) {
                  if (status.name !== 'closed') {
                    return html` <option value="${status.value}">${i18next.t(`label.${status.name}`)}</option> `
                  }
                } else {
                  if (status.name == 'closed') {
                    return html` <option value="${status.value}">${i18next.t(`label.${status.name}`)}</option> `
                  }
                }
              })}
            </select>

            
          </fieldset>
          <fieldset>
          <legend>${i18next.t('title.information filter')}</legend>
          </fieldset>

        </form>
      </div>

      <search-form
              id="search-form"
              .fields=${this._searchFields}
              @submit=${e => this.dataGrist.fetch()}
            ></search-form

      <div class="container">
        <div class="grist">
          <h2>
            <mwc-icon>list_alt</mwc-icon>${i18next.t('title.manifest_items')}
            ${
              this._showReset
                ? html`<mwc-icon class="refresh-btn" @click=${e => this.dataGrist.fetch()}>sync</mwc-icon>`
                : ''
            }
          </h2>
          
          <data-grist
            .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
            .config=${this.shippingGristConfig}
            .data=${this.shippingData}
            .fetchHandler=${this._fetchManifestDetail.bind(this)}
          ></data-grist>

          ${
            this._status !== MANIFEST_STATUS.CLOSED.value
              ? html`
                  <h2
                    @click="${e => {
                      this._addExpanded = !this._addExpanded
                      this._focusOnTrackingNoInput()
                    }}"
                  >
                    <mwc-icon>list_alt</mwc-icon>${i18next.t('title.add_tracking_no')}<mwc-icon
                      >${this._addExpanded ? 'expand_less' : 'expand_more'}</mwc-icon
                    >
                  </h2>

                  <div class="add" collapsable ?collapsed=${!this._addExpanded}>
                    <form name="addTrackingNo" class="multi-column-form">
                      <fieldset>
                        <label>${i18next.t('label.scan_tracking_no')}</label>
                        <barcode-scanable-input
                          name="trackingNo"
                          custom-input
                          @keypress="${e => {
                            if (e.keyCode === 13) {
                              e.preventDefault()
                              if (this.trackingNoInput.value) {
                                this._addTrackingNo(this.trackingNoInput.value)
                              }
                            }
                          }}"
                        ></barcode-scanable-input>
                      </fieldset>
                    </form>
                  </div>
                `
              : ''
          }
        </div>
      </div>
    `
  }

  constructor() {
    super()
    this._removePackages = []
    this._addExpanded = false
    this._companyList = []
  }

  pageInitialized() {
    this._searchFields = [
      {
        label: i18next.t('field.company'),
        name: 'companyList',
        type: 'select',
        options: [
          { value: '' },
          ...this._companyList.map(sp => {
            return {
              name: sp,
              value: sp
            }
          })
        ],
        props: { searchOper: 'eq' }
      }
    ]
    this.shippingGristConfig = {
      list: { fields: ['refNo', 'trackingNo', 'dispatchmentStatus'] },
      pagination: { infinite: true },
      rows: {
        appendable: false
      },
      columns: [
        {
          type: 'gutter',
          gutterName: 'button',
          icon: 'close',
          handlers: {
            click: (columns, data, column, record, rowIndex) => {
              try {
                if (this._status && this._status == MANIFEST_STATUS.CLOSED.value) return
                const newData = data.records.filter((_, idx) => idx !== rowIndex)
                this.shippingData = { ...this.shippingData, records: newData }
                this._removePackages.push(record.packageName)
                this._showReset = true
              } catch (e) {}
            }
          }
        },
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'string',
          name: 'bizplaceName',
          label: true,
          header: i18next.t('field.company'),
          sortable: true,
          width: 200
        },
        {
          type: 'string',
          name: 'trackingNo',
          label: true,
          header: i18next.t('field.tracking_no'),
          sortable: true,
          width: 200
        },
        {
          type: 'string',
          name: 'refNo',
          label: true,
          header: i18next.t('field.order_no'),
          sortable: true,
          width: 200
        },
        {
          type: 'string',
          name: 'attentionTo',
          label: true,
          header: i18next.t('field.delivery_to'),
          sortable: true,
          width: 150
        },
        {
          type: 'string',
          name: 'phone',
          label: true,
          header: i18next.t('field.phone'),
          sortable: true,
          width: 150
        },
        {
          type: 'string',
          name: 'deliveryAddress',
          label: true,
          header: i18next.t('field.delivery_address'),
          sortable: true,
          width: 250
        },
        {
          type: 'string',
          name: 'orderStatus',
          label: true,
          header: i18next.t('field.order_status'),
          sortable: true,
          width: 150
        },
        {
          type: 'string',
          name: 'orderRemark',
          label: true,
          header: i18next.t('field.order_remark'),
          sortable: true,
          width: 150
        },
        {
          type: 'datetime',
          name: 'manifestedAt',
          label: true,
          header: i18next.t('field.manifested_at'),
          sortable: true,
          width: 150
        }
      ]
    }
  }

  async pageUpdated(changes, lifecycle, before) {
    if (this.active) {
      this._manifestNo = changes.resourceId || this._manifestNo || ''
      await this.dataGrist.fetch()
      this.updateContext()
    }
  }

  async _fetchManifestDetail({ page, limit, sorters = [] }) {
    if (!this._manifestNo) return

    const response = await client.query({
      query: gql`
        query manifest($filters: [Filter!], $name: String!, $sortings: [Sorting!]) {
          manifest(filters: $filters, name: $name, sortings: $sortings) {
            id
            name
            dispatchedAt
            dispatcherName
            dispatcherIdentity
            status
            shippingProvider
            trackingNos {
              trackingNo
              deliveryAddress
              attentionTo
              phone
              postalCode
              city
              packageName
              refNo
              dispatchmentStatus
              orderStatus
              orderRemark
              manifestedAt
              bizplaceId
              bizplaceName
            }
          }
        }
      `,
      variables: { name: this._manifestNo, sortings: sorters, filters: this.searchForm.queryFilters }
    })

    //const manifest = response.data.manifest

    const manifest = response.data.manifest
    if (manifest.shippingProvider === 'EMS') {
      manifest.shippingProvider = 'EFMX'
    }

    if (!response.errors) {
      let uniqueBizplaceNames = Array.from(new Set(manifest.trackingNos.map(item => item.bizplaceName)))

      this._status = manifest.status
      this._fillupForm(this.manifestForm, { ...manifest, totalTrackingNo: manifest.trackingNos?.length })
      this.shippingData = {
        manifestId: manifest.id,
        shippingProvider: manifest.shippingProvider == 'EMS' ? 'EFMX' : manifest.shippingProvider,
        records: manifest.trackingNos
      }
      this._removePackages = []
      this._showReset = false
      let companyList = uniqueBizplaceNames
      if (this._companyList.length < companyList.length) {
        this._companyList = companyList
        this._searchFields.filter(x => x.name == 'companyList')[0].options = [
          { value: '' },
          ...this._companyList.map(sp => {
            return {
              name: sp,
              value: sp
            }
          })
        ]
        this._searchFields = [...this._searchFields]
      }
      this.updateContext()
    }
  }

  async _updateManifestDetails() {
    if ((!this._selectedStatus || this._selectedStatus == this._status) && this._removePackages?.length == 0) {
      this._showToast({ message: i18next.t('text.manifest_has_been_updated') })
      return
    }
    const response = await client.mutate({
      mutation: gql`
        mutation updateManifest(
          $manifestId: String
          $status: String
          $shippingProvider: String
          $removePackages: [String!]
        ) {
          updateManifest(
            manifestId: $manifestId
            status: $status
            shippingProvider: $shippingProvider
            removePackages: $removePackages
          )
        }
      `,
      variables: {
        manifestId: this.shippingData.manifestId,
        status: this._selectedStatus,
        shippingProvider: this.shippingData.shippingProvider,
        removePackages: this._removePackages
      }
    })
    if (!response.errors) {
      if (!response.data.updateManifest) {
        navigate('loading_manifest_list')
        this._showToast({ message: i18next.t('text.manifest_has_been_deleted') })
      } else {
        await this.dataGrist.fetch()
        this._showToast({ message: i18next.t('text.manifest_has_been_updated') })
      }
    }
  }

  async _openDispatcherVerificationPopup() {
    let answer = await this._validateOrderStatus()

    if (answer) {
      openPopup(
        html`
          <dispatcher-verification-popup
            @dispatch="${e => {
              this._dispatchManifest(e.detail)
            }}"
          ></dispatcher-verification-popup>
        `,
        {
          size: 'small',
          title: `${i18next.t('title.dispatcher_verification')}`
        }
      )
    }
  }

  async _addTrackingNo() {
    let answer = await this._checkReleaseOrderStatus()

    if (answer) {
      const response = await client.mutate({
        mutation: gql`
          mutation addTrackingNo($manifestId: String, $trackingNo: String, $shippingProvider: String) {
            addTrackingNo(manifestId: $manifestId, trackingNo: $trackingNo, shippingProvider: $shippingProvider)
          }
        `,
        variables: {
          manifestId: this.shippingData.manifestId,
          trackingNo: this.trackingNoInput.value.trim(),
          shippingProvider: this.shippingData.shippingProvider
        }
      })

      if (!response.errors) {
        await this.dataGrist.fetch()
        this.trackingNoInput.value = ''
        this._focusOnTrackingNoInput()
        this._showToast({ message: i18next.t('text.tracking_no_added_successfully') })
      } else {
        let errorMessage = response.errors[0].message
        if (errorMessage.includes('amp;')) {
          response.errors[0].message = errorMessage.replace('amp;', '')
          this._showToast({ message: response.errors[0].message })
        }
      }
    }
  }

  async _dispatchManifest(patch) {
    const response = await client.mutate({
      mutation: gql`
        mutation dispatchManifest($name: String!, $patch: ManifestPatch!) {
          dispatchManifest(name: $name, patch: $patch) {
            id
            name
          }
        }
      `,
      variables: { name: this._manifestNo, patch }
    })

    if (!response.errors) {
      await this.dataGrist.fetch()
      this._showToast({ message: i18next.t('text.manifest_has_been_dispatched_and_closed') })
    }
  }

  async _validateOrderStatus() {
    let foundCancelOrder = this.shippingData.records.some(
      itm => itm.orderStatus == ORDER_STATUS.PENDING_CANCEL.value || itm.orderStatus == ORDER_STATUS.CANCELLED.value
    )
    if (foundCancelOrder) {
      const answer = await CustomAlert({
        title: i18next.t('title.do_you_wish_to_proceed'),
        text: i18next.t('text.parcels_are_found_with_cancelled_or_pending_cancelled_status'),
        confirmButton: { text: i18next.t('button.proceed') },
        cancelButton: { text: i18next.t('button.cancel') }
      })

      if (answer) {
        if (!answer?.value) {
          return false
        } else {
          return true
        }
      }
    }

    return true
  }

  async _checkReleaseOrderStatus() {
    const response = await client.query({
      query: gql`
        query orderPackage($trackingNo: String) {
          orderPackage(trackingNo: $trackingNo) {
            id
            name
            trackingNo
            transporter
            releaseGood {
              id
              name
              refNo
              status
            }
          }
        }
      `,
      variables: { trackingNo: this.trackingNoInput.value.trim() }
    })

    if (!response.error) {
      let orderPackage = response.data.orderPackage
      let releaseGood = orderPackage.releaseGood

      if (
        releaseGood?.status === ORDER_STATUS.CANCELLED.value ||
        releaseGood?.status === ORDER_STATUS.PENDING_CANCEL.value
      ) {
        let answer = await CustomAlert({
          type: 'warning',
          title: i18next.t('title.order(s)_is_at_pending_cancelled_or_cancelled'),
          text: i18next.t('text.do_you_want_to_ignore'),
          confirmButton: { text: i18next.t('button.ignore') },
          cancelButton: { text: i18next.t('button.cancel') }
        })

        if (!answer?.value) {
          this._focusOnTrackingNoInput()
          return false
        } else {
          return true
        }
      } else {
        return true
      }
    }
  }

  _printManifest() {
    const filterValue = this.searchForm?.queryFilters?.[0]?.value || null
    const encodedFilterValue = encodeURIComponent(filterValue)

    GhostPrint.print({
      src: `/view_manifest/${this._manifestNo}?biz=${encodedFilterValue}`,
      pending: 1000,
      onfinish: () => {
        this._showToast({ message: i18next.t('text.document_has_been_generated') })
      }
    })
  }

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

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

customElements.define('loading-manifest-detail', LoadingManifestDetail)
