import '@things-factory/barcode-ui'
import '@things-factory/grist-ui'
import '../../components/attachment-viewer'
import '../../components/select-product-popup'

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

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

import { PACKING_UNIT_QTY, PACKING_UNIT_UOM, PACKING_UNITS } from '../../constants'
import { VasTemplate } from './vas-template'

//vas --inbound
class VasRepackRelabel extends localize(i18next)(VasTemplate) {
  static get styles() {
    return [
      SingleColumnFormStyles,
      css`
        :host {
          display: flex;
          flex: 1;
          flex-direction: column;
        }
        .container {
          display: flex;
          flex-direction: column;
          flex: 1;
          margin: 0px 20px;
        }
        data-grist {
          flex: 1;
        }
        [options-wrap] *,
        [attachment-wrap] a * {
          vertical-align: middle;
        }
        [options-wrap] input[type='checkbox'] {
          left: 0;
        }
        [options-wrap] input[type='checkbox'] + label {
          margin-right: 20px;
          padding-left: 0;
        }
        [attachment-wrap] {
          grid-column: span 9 / auto;
        }
        [attachment-wrap] a {
          display: block;
          text-overflow: ellipsis;
          overflow: hidden;
          white-space: nowrap;
          color: var(--primary-color);
          font-size: var(--fontsize-default);
        }
        [attachment-wrap] {
          margin: 0;
          padding: 0;
          list-style: none;
        }
        [attachment-wrap] a mwc-icon {
          font-size: var(--fontsize-default);
        }
      `
    ]
  }

  static get properties() {
    return {
      bizplaceId: String,
      record: Object,
      targetInfo: Object,
      packingTypes: Array,
      isExecuting: Boolean,
      config: Object,
      palletData: Object,
      toBatchId: String,
      toProduct: Object,
      toPackingType: String,
      toPackingSize: Number,
      _selectedPackingUnit: String
    }
  }

  render() {
    return html`
      <div class="container">
        <form id="repack-relabel-form" class="single-column-form" @submit="${e => e.preventDefault()}">
          <fieldset>
            <legend>${i18next.t('title.repack_relabel')}</legend>

            <label>${i18next.t('label.packing_unit')}</label>
            <select
              id="packing-unit-selector"
              required
              ?disabled="${!this._isEditable}"
              @change="${this._onPackingUnitChange}"
            >
              <option value=""></option>
              ${PACKING_UNITS.map(
                unit =>
                  html`<option
                    value="${unit.value}"
                    ?selected="${this._getOperationGuideData('packingUnit') === unit.value}"
                  >
                    ${unit.display}
                  </option>`
              )}
            </select>

            <label>${i18next.t('label.to_batch_id')}</label>
            <input
              name="batch-id"
              required
              ?disabled="${!this._isEditable}"
              value="${this.toBatchId}"
              @change="${e => (this.toBatchId = e.currentTarget.value)}"
            />

            <label>${i18next.t('label.to_product')}</label>
            <input
              readonly
              required
              name="product"
              ?disabled="${!this._isEditable}"
              @click="${this._openProductPopup.bind(this)}"
              value="${this.toProduct?.productName || ''}"
            />

            <label>${i18next.t('label.required_package_qty')}</label>
            <input
              readonly
              type="number"
              name="required-package-qty"
              value="${this._getOperationGuideData('requiredPackageQty') || 0}"
            />

            <label>${i18next.t('label.remainder')}</label>
            <input readonly type="number" name="remainder" value="${this._getOperationGuideData('remainder') || 0}" />

            <label>${i18next.t('label.new_label')}</label>
            ${(!this._isEditable && this.toProduct) || this._isEditable
              ? html`
                  <file-uploader custom-input required name="newLabel" ._files="${this.newLabelFile}"></file-uploader>
                `
              : html`
                  <ul attachment-wrap>
                    <li>
                      <a href="${location.origin}/attachment/${this.newLabel.path}" download
                        ><mwc-icon>attach_file</mwc-icon> ${this.newLabel.name}</a
                      >
                    </li>
                    <li></li>
                  </ul>
                `}
          </fieldset>
        </form>

        ${this.isExecuting
          ? html`
              <form
                id="input-form"
                class="single-column-form"
                @submit="${e => e.preventDefault()}"
                @keypress="${e => {
                  if (e.keyCode === 13) this.repack()
                }}"
              >
                <fieldset>
                  <label>${i18next.t('label.package_qty')}</label>
                  <input
                    name="package-qty"
                    type="number"
                    max="${this._getOperationGuideData('requiredPackageQty')}"
                    value="1"
                  />

                  <label>${i18next.t('label.from_pallet')}</label>
                  <barcode-scanable-input name="from-pallet-id" custom-input></barcode-scanable-input>

                  <label>${i18next.t('label.to_pallet')}</label>
                  <barcode-scanable-input name="to-pallet-id" custom-input></barcode-scanable-input>

                  <label>${i18next.t('label.location')}</label>
                  <barcode-scanable-input name="location-name" custom-input></barcode-scanable-input>
                </fieldset>
              </form>
            `
          : ''}
        ${this.isExecuting || this.record.completed
          ? html`
              <data-grist
                .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
                .config="${this.config}"
                .data="${this.palletData}"
              >
              </data-grist>
            `
          : ''}
      </div>
    `
  }

  constructor() {
    super()
    this.config = {}
    this.palletData = { records: [] }
  }

  get contextButtons() {
    return []
  }

  get form() {
    return this.shadowRoot.querySelector('form#repack-relabel-form')
  }

  get packingUnitSelector() {
    return this.shadowRoot.querySelector('select#packing-unit-selector')
  }

  get batchIdInput() {
    return this.shadowRoot.querySelector('input[name=batch-id]')
  }

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

  get newLabelInput() {
    return this.shadowRoot.querySelector('file-uploader[name=newLabel]')
  }

  get requiredPackageQtyInput() {
    return this.shadowRoot.querySelector('input[name=required-package-qty]')
  }

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

  get transactions() {
    return [this.createNewLabel.bind(this)]
  }

  get revertTransactions() {
    return [this.deleteNewLabel.bind(this)]
  }

  get data() {
    return {
      packingUnit: this._selectedPackingUnit,
      toBatchId: this.toBatchId,
      toProduct: this.toProduct,
      toPackingType: this.toPackingType,
      stdAmount: Number(this.toPackingSize) || '',
      requiredPackageQty: Number(this.requiredPackageQtyInput.value) || '',
      remainder: Number(this.remainderInput.value) || 0,
      newLabel: {
        files: this.newLabelInput.files
      }
    }
  }

  get newLabelFile() {
    if (
      this.record &&
      this.record.operationGuide &&
      this.record.operationGuide.data &&
      this.record.operationGuide.data.newLabel &&
      this.record.operationGuide.data.newLabel.files &&
      this.record.operationGuide.data.newLabel.files.length
    ) {
      return this.record.operationGuide.data.newLabel.files
    }
  }

  get newLabel() {
    if (
      this.record &&
      this.record.operationGuide &&
      this.record.operationGuide.data &&
      this.record.operationGuide.data.newLabel
    ) {
      return this.record.operationGuide.data.newLabel
    }
  }

  get packageQtyInput() {
    return this.shadowRoot.querySelector('input[name=package-qty]')
  }

  get toPalletIdInput() {
    return this.shadowRoot.querySelector('barcode-scanable-input[name=to-pallet-id]').shadowRoot.querySelector('input')
  }

  get locationInput() {
    return this.shadowRoot.querySelector('barcode-scanable-input[name=location-name]').shadowRoot.querySelector('input')
  }

  async firstUpdated() {
    if (this.record && this.record.operationGuide && this.record.operationGuide.data) {
      this.productInput.value = this.record.operationGuide.data.toProduct.productName || ''
      this.batchIdInput.value = this.record.operationGuide.data.toBatchId || ''
    }

    this.toBatchId = this.targetInfo && this.targetInfo?.length ? this.targetInfo[0].target?.batchId : ''

    if (this.isExecuting || this.record.completed) {
      await this.updateComplete
      this._initExecutingConfig(this.record.completed)
    }
  }

  _initExecutingConfig(isCompleted) {
    let gutters = [{ type: 'gutter', gutterName: 'sequence' }]
    if (!isCompleted) {
      this.locationInput.addEventListener('change', this._checkLocationValidity.bind(this))
      gutters.push({
        type: 'gutter',
        gutterName: 'button',
        icon: 'close',
        handlers: {
          click: async (columns, data, column, record, rowIndex) => {
            const result = await CustomAlert({
              title: i18next.t('title.are_you_sure'),
              text: i18next.t('text.undo_repalletizing'),
              confirmButton: { text: i18next.t('button.confirm') },
              cancelButton: { text: i18next.t('button.cancel') }
            })

            if (!result.value) {
              return
            }

            this.undoRepack(this.record.name, record.palletId)
          }
        }
      })
    }

    this.config = {
      rows: { appendable: false },
      pagination: { infinite: true },
      list: { fields: ['fromPalletId', 'palletId', 'locationName', 'qty', 'uomValue'] },
      columns: [
        ...gutters,
        {
          type: 'string',
          name: 'fromPalletId',
          header: i18next.t('field.from_pallet'),
          width: 180
        },
        {
          type: 'string',
          name: 'palletId',
          header: i18next.t('field.repacked_pallet'),
          width: 180
        },
        {
          type: 'string',
          name: 'locationName',
          header: i18next.t('field.location'),
          width: 180
        },
        {
          type: 'integer',
          name: 'qty',
          header: i18next.t('field.qty'),
          width: 60
        },
        {
          type: 'integer',
          name: 'uomValue',
          header: i18next.t('field.uom_value'),
          width: 60
        }
      ]
    }
  }

  async _checkLocationValidity() {
    try {
      const locationName = this.locationInput.value
      if (!locationName) return

      const response = await client.query({
        query: gql`
          query locationByName($name: String!) {
            locationByName(name: $name) {
              id
            }
          }
        `,
        variables: {
          name: locationName
        }
      })

      if (!response.errors) {
        if (!response.data.locationByName) throw new Error(i18next.t('text.there_is_no_location'))
      }
    } catch (e) {
      this.locationInput.value = ''
      this.locationInput.focus()
      this._showToast(e)
    }
  }

  _openProductPopup() {
    openPopup(
      html` <select-product-popup
        .bizplaceId="${this.bizplaceId}"
        @selected="${e => {
          this.toProduct = e.detail
          this._updatePackingDetail(this.toProduct)
        }}"
      ></select-product-popup>`,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.select_item')
      }
    )
  }

  async init() {
    await this.updateComplete
    this.isExecuting = !this.record.completed
    if (this.isExecuting) {
      this.inputForm.reset()
      if (this.record.palletId) {
        this.fromPalletIdInput.value = this.record.palletId
        this.fromPalletIdInput.setAttribute('readonly', true)
        this.toPalletIdInput.focus()
      } else {
        this.fromPalletIdInput.value = ''
        this.fromPalletIdInput.focus()
      }
      this.toPalletIdInput.value = ''
      this.locationInput.value = ''
    }

    if (this.isExecuting || this.record.completed) {
      this.palletData = { records: this._formatPalletData(this.record.operationGuide.data) }
    }
  }

  _formatPalletData(operationGuideData = {}) {
    if (!this.record.palletId) return []
    const stdAmount = operationGuideData?.stdAmount
    const packingUnit = operationGuideData?.packingUnit

    return operationGuideData?.repackedInvs?.length
      ? operationGuideData?.repackedInvs.map(ri => {
          return {
            ...ri,
            fromPalletId: this.record.palletId,
            ...ri.repackedFrom.reduce(
              (amount, rf) => {
                if (packingUnit == 'QTY') {
                  amount.qty += rf.reducedUnitQty / stdAmount
                  amount.uomValue += Math.round((rf.reducedUomValue + Number.EPSILON) * 100) / 100
                } else if (packingUnit == 'UOM') {
                  amount.qty = ri.repackedPkgQty
                  amount.uomValue += Math.round((rf.reducedUomValue + Number.EPSILON) * 100) / 100
                }

                return amount
              },
              { qty: 0, uomValue: 0 }
            )
          }
        })
      : []
  }

  _onPackingUnitChange() {
    this._selectedPackingUnit = this.packingUnitSelector.value

    if (this._selectedPackingUnit == '') {
      this.requiredPackageQtyInput.value = 0
      this.remainderInput.value = 0
    }

    if (this.toProduct) {
      this._updatePackingDetail(this.toProduct)
    }
  }

  _updatePackingDetail(toProduct) {
    this.toPackingType = toProduct.packingType
    this.toPackingSize =
      this.packingUnitSelector.value === PACKING_UNIT_UOM.value
        ? toProduct.product.primaryValue
        : toProduct.product.packingSize
    this._calcPackageQty()
  }

  _calcPackageQty() {
    try {
      const packingUnit = this._selectedPackingUnit
      const stdAmount = parseFloat(this.toPackingSize)
      const target = this.targetInfo[0]

      if (packingUnit === PACKING_UNIT_QTY.value) {
        const requiredPackageQty = Math.floor((target.qty * target.packingSize) / stdAmount)
        this.requiredPackageQtyInput.value = requiredPackageQty
        this.remainderInput.value =
          Math.round((target.qty * target.packingSize - requiredPackageQty * stdAmount) * 100) / 100
      } else if (packingUnit === PACKING_UNIT_UOM.value) {
        const requiredPackageQty = Math.floor(target.uomValue / stdAmount)
        this.requiredPackageQtyInput.value = requiredPackageQty
        this.remainderInput.value = Math.round((target.uomValue - stdAmount * requiredPackageQty) * 100) / 100
      }
    } catch (e) {
      this.requiredPackageQtyInput.value = ''
      this._showToast(e)
    }
  }

  async validateAdjust() {
    if (!this.form.checkValidity() || !this.toProduct || !this.newLabelInput.files)
      throw new Error(i18next.t('text.invalid_form'))

    const stdAmount = Number(this.toPackingSize)
    const packageQty = Number(this.requiredPackageQtyInput.value)
    if (packageQty <= 0) throw new Error(i18next.t('text.package_qty_should_be_positive'))

    const packingUnit = this._selectedPackingUnit
    const target = this.targetInfo[0]

    if (packingUnit === PACKING_UNIT_QTY.value) {
      if (target.qty && stdAmount * packageQty > target.qty * target.packingSize)
        throw new Error(i18next.t('text.qty_exceed_limit'))
    } else if (packingUnit === PACKING_UNIT_UOM.value) {
      if (target.uomValue && stdAmount * packageQty > target.uomValue) {
        throw new Error(i18next.t('text.uom_value_exceed_limit'))
      }
    }
  }

  async createNewLabel(operationGuide) {
    try {
      if (!operationGuide.data.newLabel.files || !operationGuide.data.newLabel.files.length) return operationGuide
      const attachment = {
        file: operationGuide.data.newLabel.files[0],
        category: 'LABEL',
        refBy: `VAS-REPACK-RELABEL-${String(Date.now())}`
      }
      const response = await client.mutate({
        mutation: gql`
          mutation ($attachment: NewAttachment!) {
            createAttachment(attachment: $attachment) {
              id
              name
              path
            }
          }
        `,
        variables: {
          attachment
        },
        context: {
          hasUpload: true
        }
      })

      if (response.errors) throw response.errors[0]

      return {
        ...operationGuide,
        data: {
          ...operationGuide.data,
          newLabel: response.data.createAttachment
        }
      }
    } catch (e) {
      throw e
    }
  }

  async deleteNewLabel(record) {
    try {
      const operationGuide = JSON.parse(record.operationGuide)
      const response = await client.mutate({
        mutation: gql`
          mutation deleteAttachment($id: String!) {
            deleteAttachment(id: $id) {
              name
            }
          }
        `,
        variables: {
          id: operationGuide.data.newLabel.id
        }
      })

      if (response.errors) throw response.errors[0]
    } catch (e) {
      throw e
    }
  }

  async repack() {
    try {
      this._checkRepackable()
      await client.mutate({
        mutation: gql`
          mutation repackaging(
            $worksheetDetailName: String!
            $fromPalletId: String!
            $toPalletId: String!
            $locationName: String!
            $packageQty: Int!
          ) {
            repackaging(
              worksheetDetailName: $worksheetDetailName
              fromPalletId: $fromPalletId
              toPalletId: $toPalletId
              locationName: $locationName
              packageQty: $packageQty
            )
          }
        `,
        variables: {
          worksheetDetailName: this.record.name,
          fromPalletId: this.fromPalletIdInput.value,
          toPalletId: this.toPalletIdInput.value,
          locationName: this.locationInput.value,
          packageQty: parseInt(this.packageQtyInput.value)
        }
      })

      this.dispatchEvent(
        new CustomEvent('completed', {
          bubbles: true,
          composed: true
        })
      )
    } catch (e) {
      this._showToast(e)
    }
  }

  async undoRepack(worksheetDetailName, toPalletId) {
    try {
      await client.mutate({
        mutation: gql`
          mutation undoRepackaging($worksheetDetailName: String!, $fromPalletId: String!, $toPalletId: String!) {
            undoRepackaging(
              worksheetDetailName: $worksheetDetailName
              fromPalletId: $fromPalletId
              toPalletId: $toPalletId
            )
          }
        `,
        variables: {
          worksheetDetailName,
          fromPalletId: this.fromPalletIdInput.value,
          toPalletId
        }
      })
      this.dispatchEvent(
        new CustomEvent('completed', {
          bubbles: true,
          composed: true
        })
      )
    } catch (e) {
      this._showToast(e)
    }
  }

  _checkRepackable() {
    if (
      isNaN(Number(this.packageQtyInput.value)) ||
      Number(this.packageQtyInput.value) < 1 ||
      Number(this.packageQtyInput.value) > this._getOperationGuideData('requiredPackageQty')
    ) {
      this.packageQtyInput.value = 1
      this.packageQtyInput.select()
      throw new Error(i18next.t('text.invalid_quantity_input'))
    }

    if (!this.fromPalletIdInput.value) {
      this.fromPalletIdInput.focus()
      throw new Error(i18next.t('text.from_lot_id_is_emplty'))
    }

    if (!this.toPalletIdInput.value) {
      this.toPalletIdInput.focus()
      throw new Error(i18next.t('text.to_lot_id_is_empty'))
    }

    if (!this.locationInput.value) {
      // location에 값이 없을 경우 기존에 추가된 팔렛에 현재 추가하려는 팔렛이 있는지 확인하고
      // 만약 있다면 기존 로케이션을 유지하는 것으로 간주하여 valid
      // 없다면 로케이션을 필수로 입력해야 하기 때문에 invalid

      if (
        this.record.operationGuide.data?.repackedInvs &&
        this.record.operationGuide.data.repackedInvs.find(ri => ri.palletId === this.toPalletIdInput.value)
      ) {
        const samePallet = this.record.operationGuide.data.repackedInvs.find(
          ri => ri.palletId === this.toPalletIdInput.value
        )
        this.locationInput.value = samePallet.locationName
      } else {
        this.locationInput.select()
        throw new Error(i18next.t('text.location_code_is_empty'))
      }
    }
  }

  checkExecutionValidity() {
    if (!this.palletData?.records?.length) {
      throw new Error(i18next.t('text.vas_is_not_completed_yet'))
    }

    if (this.record.qty !== this.palletData.records.reduce((repackedQty, r) => (repackedQty += r.qty), 0)) {
      throw new Error(i18next.t('text.vas_is_not_completed_yet'))
    }
  }
}

window.customElements.define('vas-repack-relabel', VasRepackRelabel)
