import { i18next, localize } from '@things-factory/i18n-base'
import { css, html, LitElement } from 'lit-element'
import { SingleColumnFormStyles, MultiColumnFormStyles } from '@things-factory/form-ui'
import { client, navigate } from '@things-factory/shell'
import gql from 'graphql-tag'
import { isMobileDevice } from '@things-factory/utils'
import { fetchSettingRule } from '../../fetch-setting-value'
import { ORDER_STATUS } from '../constants'
import { sleep } from '@things-factory/utils'

class GenerateWorksheetPopup extends localize(i18next)(LitElement) {
  static get properties() {
    return {
      _generateStd: String,
      gristData: Object,
      _partnerBizplaceIds: Array,
      _releaseGoods: Array,
      _batchLimit: String,
      _groupedReleaseGoods: Array,
      _processing: Boolean,
      _enableZonePicking: Boolean
    }
  }

  static get styles() {
    return [
      MultiColumnFormStyles,
      SingleColumnFormStyles,
      css`
        :host {
          display: flex;
          flex-direction: column;
          overflow-x: overlay;
          background-color: var(--main-section-background-color);
        }
        .container {
          display: flex;
          flex-direction: column;
        }
        .button-container {
          padding: var(--button-container-padding);
          margin: var(--button-container-margin);
          text-align: var(--button-container-align);
          background-color: var(--button-container-background);
          height: var(--button-container-height);
        }
        .picking-div {
          width: max-content;
          margin: 10px auto auto -10px;
          display: flex;
          gap: 5em;
        }
        .radio-div {
          margin: auto;
        }
        input[type='radio'] {
          top: 2px;
        }
        .multi-column-form {
          display: table !important;
        }
        .grist {
          display: flex;
          flex-direction: column;
          flex: 1;
          overflow-y: auto;
        }
        .grist-container {
          overflow-y: hidden;
          display: flex;
          flex: 1;
          padding: 10px;
        }
        data-grist {
          overflow-y: auto;
          flex: 1;
        }
      `
    ]
  }

  get gristColumn() {
    if (this._generateStd == 'zone') {
      return html`
        <div class="grist-container">
          <div class="grist">
            <data-grist
              id="zoneGrist"
              .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
              .config=${this.zoneConfig}
              .fetchHandler=${this._calculateZoneWoksheets.bind(this)}
            ></data-grist>
          </div>
        </div>
      `
    } else {
      return html`
        <div class="grist-container">
          <div class="grist">
            <data-grist
              id="batchGrist"
              .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
              .config=${this.batchConfig}
              .fetchHandler=${this._calculatePossibleWorksheets.bind(this)}
            ></data-grist>
          </div>
        </div>
      `
    }
  }

  render() {
    return html`
      <div class="container">
        <form id="input-form" class="multi-column-form">
          <fieldset>
            <legend>${i18next.t('title.please_select_generate_type')}</legend>
            <div class="picking-div">
              <div class="radio-div">
                <input
                  id="order-picking"
                  name="generate-std"
                  type="radio"
                  value="single"
                  @change="${e => {this._handleGenerateStdChange(e)}}"
                  .checked="true"
                  checked
                />
                <label for="order-picking">${i18next.t('text.single_picking')}</label>
              </div>
              <div class="radio-div">
                <input
                  id="batch-picking"
                  name="generate-std"
                  type="radio"
                  value="batch"
                  @change="${e => {this._handleGenerateStdChange(e)}}"
                />
                <label for="batch-picking">${i18next.t('text.batch_picking')}</label>
              </div>
              ${this._enableZonePicking
                ? html`
                    <div class="radio-div">
                      <input
                        id="zone-picking"
                        name="generate-std"
                        type="radio"
                        value="zone"
                        @change="${e => {this._handleGenerateStdChange(e)}}"
                      />
                      <label for="zone-picking">${i18next.t('text.zone_picking')}</label>
                    </div>
                  `
                : ''}
            </div>
          </fieldset>
        </form>
      </div>

      ${this.gristColumn}

      <div class="button-container">
        <mwc-button
          raised
          @click="${async e => {
            const button = e.currentTarget
            this.disableButton(button)
            try {
              switch (this._generateStd) {
                case 'single':
                  await this._generatePickingWorksheet()
                  break
                case 'batch':
                  await this._generateBatchPickingWorksheet()
                  break
                case 'zone':
                  await this._generateZonePickingWorksheet()
                  break
              }
            } finally {
              await sleep(200)
              this.enableButton(button)
            }
          }}"
          label="${i18next.t('button.confirm')}"
          ?disabled=${this._processing}
        ></mwc-button>
      </div>
    `
  }

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

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

  constructor() {
    super()
    this._generateStd = 'single'
    this._partnerBizplaceIds = []
    this._releaseGoods = []
    this._groupedReleaseGoods = []
    this._processing = false
  }

  async firstUpdated() {
    this.batchConfig = {
      pagination: { infinite: true },
      rows: {
        appendable: false
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'string',
          name: 'company',
          header: i18next.t('field.company'),
          width: 180
        },
        {
          type: 'int',
          name: 'totalReleaseOrder',
          header: i18next.t('field.release_order'),
          width: 180
        },
        {
          type: 'string',
          name: 'totalBatchWorksheet',
          header: i18next.t('field.merged_worksheet'),
          width: 180
        }
      ]
    }

    this.zoneConfig = {
      pagination: { infinite: true },
      rows: {
        appendable: false
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        {
          type: 'string',
          name: 'company',
          header: i18next.t('field.company'),
          width: 180
        },
        {
          type: 'int',
          name: 'noOfSku',
          header: i18next.t('field.no_of_sku'),
          width: 110
        },
        {
          type: 'int',
          name: 'noOfRo',
          header: i18next.t('field.no_of_ro'),
          width: 110
        },
        {
          type: 'string',
          name: 'zone',
          header: i18next.t('field.zone'),
          width: 180
        }
      ]
    }

    this._batchLimit = await fetchSettingRule('batch-picking-limit')
    this._enableZonePicking = await fetchSettingRule('enable-zone-picking')
  }

  _handleGenerateStdChange(e) {
    this._generateStd = e.currentTarget.value

    this.updateComplete.then(() => {
      if (this._generateStd === 'zone') {
        this.zoneGrist.fetch()
      } else {
        this.batchGrist.fetch()
      }
    })
  }

  async _generatePickingWorksheet() {
    this._processing = true
    try {
      let releaseGoods = this.gristData.selected.filter(itm => itm.status === ORDER_STATUS.PENDING_WORKSHEET.value)

      releaseGoods = releaseGoods.map(ro => {
        return {
          name: ro.name,
          status: ro.status
        }
      })

      const response = await client.mutate({
        mutation: gql`
          mutation generateMultipleReleaseGoodWorksheet($releaseGoods: [MultipleReleaseGoodWorksheet!]) {
            generateMultipleReleaseGoodWorksheet(releaseGoods: $releaseGoods)
          }
        `,
        variables: {
          releaseGoods
        }
      })

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

  async _generateBatchPickingWorksheet() {
    this._processing = true
    try {
      await this._validateWorksheets()
      const response = await client.mutate({
        mutation: gql`
          mutation generateBatchPickingWorksheet($releaseGoods: [[GenerateBatchPickInfo!]]) {
            generateBatchPickingWorksheet(releaseGoodWithBiz: $releaseGoods) {
              name
            }
          }
        `,
        variables: {
          releaseGoods: this._groupedReleaseGoods
        }
      })

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

  async _generateZonePickingWorksheet() {
    this._processing = true
    try {

      let releaseGoodNames = this.gristData.selected
        .filter(itm => itm.status === ORDER_STATUS.PENDING_WORKSHEET.value)
        .map(itm => itm.name)

      const response = await client.mutate({
        mutation: gql`
          mutation generateWorksheetZonePicking($releaseGoods: [String!]) {
            generateWorksheetZonePicking(releaseGoods: $releaseGoods) {
              name
            }
          }
        `,
        variables: {
          releaseGoods: releaseGoodNames
        }
      })
      if (!response.errors) {
        navigate('zone_worksheets')
        this._showToast({ message: i18next.t('text.worksheet(s)_has_been_generated')})
      }
      this._processing = false
    } catch (e) {
      this._showToast(e)
      this._processing = false
    }
  }

  async _splitOrdersForGenerateBatchWorksheet() {
    this._releaseGoods = this.gristData.selected
      .filter(itm => itm.status === ORDER_STATUS.PENDING_WORKSHEET.value)
      .map(itm => {
        return {
          releaseGoodNo: itm.name,
          bizplaceId: itm.bizplace.id,
          bizplaceName: itm.bizplace.name
        }
      })

    this._partnerBizplaceIds = [...new Set(this._releaseGoods.map(item => item.bizplaceId))]

    //separate orders by bizplace
    let array = []
    this._releaseGoods.forEach(rg => {
      const { bizplaceId } = rg
      array[bizplaceId] = array[bizplaceId] ?? []

      array[bizplaceId].push(rg)
      return array
    })

    //check if there is at least 2 orders, separate orders based on batch picking limit
    this._partnerBizplaceIds.forEach(itm => {
      let insufficientOrders = this._releaseGoods.filter(rg => rg.bizplaceId == itm)

      if (insufficientOrders?.length > 1) {
        while (array[itm].length > 0) {
          array.push(array[itm].splice(0, parseInt(this._batchLimit) ? parseInt(this._batchLimit) : 20))
        }

        delete array[itm]
      }
    })

    return array
  }

  async _calculatePossibleWorksheets() {
    //get grouped release goods
    this._groupedReleaseGoods = await this._splitOrdersForGenerateBatchWorksheet()
    let batchLimit = parseInt(this._batchLimit) ? parseInt(this._batchLimit) : 20
    //for each bizplace, generate data of possible worksheets
    let data = this._partnerBizplaceIds.map(id => {
      let selectedOrders = this._releaseGoods.filter(rg => rg.bizplaceId == id)
      let possibleBatchWorksheet = Math.ceil(selectedOrders.length / batchLimit)

      return {
        company: selectedOrders[0].bizplaceName,
        totalReleaseOrder: selectedOrders.length,
        totalBatchWorksheet: possibleBatchWorksheet
      }
    })

    return { records: data, total: data.length }
  }

  async _calculateZoneWoksheets() {
    //fetch orders and estimated worksheet count
    const orderZones = await this.fetchOrderZone()

    let data = orderZones.items.map(item => {
      return {
        company: item.company,
        noOfSku: item.noOfSku,
        noOfRo: item.noOfRo,
        zone: item.zone
      }
    })

    return { records: data, total: data.length }
  }

  async fetchOrderZone() {
    let releaseGoodNames = this.gristData.selected
      .filter(itm => itm.status === ORDER_STATUS.PENDING_WORKSHEET.value)
      .map(itm => itm.name)

    const response = await client.query({
      query: gql`
        query zoneWorksheetOverview($releaseGoods: [String!]!) {
          zoneWorksheetOverview(releaseGoods: $releaseGoods) {
            items {
              company
              noOfSku
              noOfRo
              zone
            }
          }
        }
      `,
      variables: {
        releaseGoods: releaseGoodNames
      }
    })

    return response.data.zoneWorksheetOverview
  }

  _validateWorksheets() {
    //check if there is at least 2 orders, separate orders based on batch picking limit
    this._partnerBizplaceIds.forEach(itm => {
      let insufficientOrders = this._releaseGoods.filter(rg => rg.bizplaceId == itm)

      if (insufficientOrders?.length < 2) {
        throw new Error(i18next.t('text.minimum_of_2_orders_required_for_batch_picking'))
      }
    })

    //check recall and normal orders are selected together
    let uniqueWS = this.gristData.selected.filter(
      res => res.recall !== this.gristData.selected[0].recall && res.status === ORDER_STATUS.PENDING_WORKSHEET.value
    )

    if (uniqueWS.length > 0) {
      throw new Error(i18next.t('text.recall_release_order_cant_be_generated_together_with_normal_release_order'))
    }

    //check route and without route orders are selected together
    let withRouteLabel = this.gristData.selected.find(
      itm => itm.routeId && itm.status === ORDER_STATUS.PENDING_WORKSHEET.value
    )
    let withoutRouteLabel = this.gristData.selected.find(
      itm => itm.routeId == null && itm.status === ORDER_STATUS.PENDING_WORKSHEET.value
    )

    if (withRouteLabel && withoutRouteLabel) {
      throw new Error(i18next.t('text.release_order_without_route_labelling_is_included_in_the_batch_picking'))
    }
  }

  disableButton(button) {
    button.icon = 'rotate_right'
    button.setAttribute('working', true)
    button.disabled = true
  }

  enableButton(button) {
    button.disabled = false
    button.icon = ''
    button.removeAttribute('working')
  }

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

window.customElements.define('generate-worksheet-popup', GenerateWorksheetPopup)
