import '@things-factory/grist-ui'
import '@things-factory/barcode-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, CustomAlert } from '@things-factory/shell'
import { ScrollbarStyles } from '@things-factory/styles'
import { isMobileDevice } from '@things-factory/utils'
import { TOTE_STATUS } from '../constants'

class TotePopup extends localize(i18next)(LitElement) {
  static get properties() {
    return {
      bizplace: String,
      config: Object,
      grist: Object,
      data: Object,
      orderNo: String,
      toteNo: String,
      minimumSealNumber: Number,
      _totalSeal: Number,
      _selectedToteNo: String,
      _toteNoList: Object,
      _sealNoList: Object,
      _sealed: Boolean
    }
  }

  static get styles() {
    return [
      MultiColumnFormStyles,
      SingleColumnFormStyles,
      ScrollbarStyles,
      css`
        :host {
          display: flex;
          flex-direction: column;
          overflow: hidden;
          background-color: white;
        }

        .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: 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 {
          font: var(--grist-title-font);
        }
        .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);
        }
        .break {
          word-wrap: break-word;
        }
        .mobile-hint {
          padding: 10px;
          background-color: var(--primary-light-color, #f0f0f0);
          border-left: 4px solid var(--primary-color, #2196f3);
          margin: 10px;
          font-size: 0.9em;
        }
        .refresh-button {
          margin: 10px;
          display: flex;
          justify-content: center;
        }
      `
    ]
  }

  render() {
    const shouldShowSealSection = this._shouldShowSealSection()
    const sealInputDisabled = !this.toteNo || this.toteNo === ''

    return html`
      <form class="multi-column-form">
        <fieldset>
          <legend>${i18next.t('title.tote')}</legend>

          <label class="break">${i18next.t('field.now_scanning')}</label>
          <input type="text" .value="${this.toteNo || ''} ${this._sealed ? '(Sealed)' : ''}" disabled />
        </fieldset>
      </form>

      <form id="info-form" class="multi-column-form">
        <fieldset>
          <label>${i18next.t('field.tote')}</label>
          <barcode-scanable-input
            name="toteNo"
            custom-input
            @keypress="${e => {
              if (e.keyCode === 13) {
                e.preventDefault()
                this._checkToteAndOrderTote()
              }
            }}"
          ></barcode-scanable-input>
        </fieldset>

        <fieldset>
          <label>${i18next.t('field.scanned')}</label>
          <select
            name="toteNoList"
            @change="${e => {
              this._selectedToteNo = this._toteNoList.filter(itm => itm.id == e.currentTarget.value)
              if (this._selectedToteNo) this.grist.fetch()
            }}"
          >
            <option value="" selected>-- ${i18next.t('text.tote_number')} --</option>
            ${(this._toteNoList || []).map(list => html` <option value=${list.id}>${list.name}</option> `)}
          </select>
        </fieldset>
      </form>

      ${shouldShowSealSection
        ? html`
            <form class="multi-column-form">
              <fieldset>
                <label>${i18next.t('field.seal_no')}</label>
                <barcode-scanable-input
                  name="sealNo"
                  custom-input
                  ?disabled="${sealInputDisabled}"
                  @keypress="${e => {
                    if (e.keyCode === 13) {
                      e.preventDefault()
                      this.sealNo = this.sealNoInput.value
                      if (this.toteNo) {
                        this._sealTote()
                      } else {
                        this._showToast({ message: i18next.t('text.tote_is_not_scanned') })
                      }
                    }
                  }}"
                ></barcode-scanable-input>
              </fieldset>

              <label>${i18next.t('field.scanned')} (${this._totalSeal || 0})</label>
              <select name="sealNoList">
                <option value="">-- ${i18next.t('text.seal_number')} --</option>
                ${(this._sealNoList?.orderToteSeals || []).map(
                  list => html` <option value="${list.name}">${list.name}</option> `
                )}
              </select>
            </form>
          `
        : ''}
      ${isMobileDevice() && this._selectedToteNo?.length > 0
        ? html`
            <div class="mobile-hint">
              ${i18next.t('text.viewing_items_in_tote')}: <strong>${this._selectedToteNo[0].name}</strong>
            </div>
          `
        : ''}

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

      ${isMobileDevice()
        ? html`
            <div class="refresh-button">
              <mwc-button
                icon="refresh"
                @click=${async () => {
                  if (this.grist) {
                    await this.grist.fetch()
                    this._showToast({ type: 'info', message: i18next.t('text.list_refreshed') })
                  }
                }}
                label="${i18next.t('button.refresh')}"
              ></mwc-button>
            </div>
          `
        : ''}

      <div class="button-container">
        ${this._shouldShowUndoButton()
          ? html`
              <mwc-button
                @click=${this._undoSelectedItems.bind(this)}
                raised
                style="--mdc-theme-primary: var(--status-warning-color, #ff9800); margin-right: 10px;"
                label="${i18next.t('button.undo')}"
              ></mwc-button>
            `
          : ''}
        <mwc-button
          @click=${() => {
            this.dispatchEvent(
              new CustomEvent('completed', {
                detail: this._sealed ? '' : this.toteNo
              })
            )
            history.back()
          }}
          raised
          label="${i18next.t('button.back')}"
        ></mwc-button>
      </div>
    `
  }

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

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

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

  _shouldShowSealSection() {
    // Only show seal section if minimumSealNumber is greater than 0
    return (this.minimumSealNumber || 0) > 0
  }

  _shouldShowUndoButton() {
    // Show undo button only if selected tote is not sealed
    const selectedTote = this._selectedToteNo?.[0]
    return selectedTote && !selectedTote?.closedDate
  }

  _focusOnSealNoInput() {
    const sealInput = this.sealNoInput
    if (sealInput) {
      setTimeout(() => sealInput.focus(), 50)
    }
  }

  _focusOnToteNoInput() {
    setTimeout(() => this.toteNoInput.focus(), 50)
  }

  async firstUpdated() {
    this.config = {
      list: { fields: ['sku', 'packingType', 'pickedQty'] },
      pagination: { pages: [10, 20, 50, 100] },
      rows: {
        appendable: false,
        selectable: {
          multiple: true
        }
      },
      columns: [
        { type: 'gutter', gutterName: 'sequence' },
        { type: 'gutter', gutterName: 'row-selector', multiple: true },
        {
          type: 'string',
          name: 'sku',
          header: i18next.t('field.sku'),
          label: true,
          width: 180
        },
        {
          type: 'string',
          name: 'packingType',
          header: i18next.t('field.packing_type'),
          label: true,
          width: 180
        },
        {
          type: 'string',
          name: 'pickedQty',
          header: i18next.t('field.picked_qty'),
          label: true,
          width: 180
        }
      ]
    }

    await this._fetchToteList()
    this._focusOnToteNoInput()
  }

  async fetchOrderToteItems({ sorters = [] }) {
    if (!this._selectedToteNo || this._selectedToteNo.length < 1) {
      this._totalSeal = 0
      this._sealNoList = null
      return {
        total: 0,
        records: []
      }
    }

    const sortings = sorters

    const response = await client.query({
      query: gql`
        query orderToteItems($toteNo: String!, $orderNo: String!, $sortings: [Sorting!]!) {
          orderToteItems(toteNo: $toteNo, orderNo: $orderNo, sortings: $sortings) {
            items {
              id
              name
              orderProduct {
                id
                product {
                  id
                  sku
                  packingType
                }
              }
              qty
            }
            total
          }
        }
      `,
      variables: {
        toteNo: this._selectedToteNo[0].name,
        sortings,
        orderNo: this.orderNo
      },
      fetchPolicy: 'no-cache'
    })

    if (!response.errors) {
      if (this._selectedToteNo) {
        this._sealNoList = this._toteNoList.find(itm => itm.name == this._selectedToteNo[0].name)
        this._totalSeal = this._sealNoList.orderToteSeals.length
      }

      if (this.toteNo && this.toteNo !== '') {
        // Only focus on seal input if seal section is visible
        if (this._shouldShowSealSection()) {
          this._focusOnSealNoInput()
        }
      } else {
        this._focusOnToteNoInput()
      }

      return {
        total: response.data.orderToteItems.total || 0,
        records:
          response.data.orderToteItems.items.map(item => {
            return {
              ...item,
              sku: item.orderProduct.product.sku,
              packingType: item.orderProduct.product.packingType,
              pickedQty: item.qty
            }
          }) || []
      }
    }
  }

  async _checkToteAndOrderTote() {
    try {
      this.toteNo = this.toteNoInput.value
      let result = await this._checkIsSealed()

      if (result) {
        await this._checkToteAvailability()

        // After successful tote scan, refresh the tote list and auto-select the scanned tote
        if (this.toteNo && this.toteNo !== '') {
          await this._fetchToteList()

          // Auto-select the scanned tote for PDA/mobile users
          const scannedTote = this._toteNoList?.find(tote => tote.name === this.toteNo)
          if (scannedTote) {
            this._selectedToteNo = [scannedTote]
            // Trigger grist to fetch items for the selected tote
            if (this.grist) {
              await this.grist.fetch()
            }
          }
        }
      } else {
        this.toteNoInput.value = ''
        this.toteNo = ''
      }
    } catch (e) {
      this._showToast(e)
    }

    if (this.toteNo && this.toteNo !== '') {
      // Only focus on seal input if seal section is visible
      if (this._shouldShowSealSection()) {
        this._focusOnSealNoInput()
      }
    } else {
      this._focusOnToteNoInput()
    }
  }

  async _sealTote() {
    try {
      const response = await client.mutate({
        mutation: gql`
          mutation sealTote($sealNo: String!, $toteNo: String!, $orderNo: String!) {
            sealTote(sealNo: $sealNo, toteNo: $toteNo, orderNo: $orderNo)
          }
        `,
        variables: {
          sealNo: this.sealNo,
          toteNo: this.toteNo,
          orderNo: this.orderNo
        }
      })

      if (!response.errors) {
        await this._checkIsSealed()
        this._selectedToteNo = ''
        this._toteNoList = null
        this._sealNoList = null
        await this._fetchToteList()
        await this.grist.fetch()
        this.sealNoInput.value = ''
        this.sealNo = ''
        this._focusOnSealNoInput()
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _checkIsSealed() {
    try {
      const response = await client.query({
        query: gql`
          query checkOrderTote($name: String!, $orderNo: String!) {
            checkOrderTote(name: $name, orderNo: $orderNo)
          }
        `,
        variables: { name: this.toteNo, orderNo: this.orderNo }
      })

      if (!response.errors) {
        this._sealed = response.data.checkOrderTote
        return true
      }
    } catch (e) {
      this._showToast(e)
      return false
    }
  }

  async _checkToteAvailability() {
    try {
      const response = await client.query({
        query: gql`
          query checkToteStatus($name: String!, $bizplace: String!) {
            checkToteStatus(name: $name, bizplace: $bizplace)
          }
        `,
        variables: { name: this.toteNo, bizplace: this.bizplace }
      })

      if (!response.errors) {
        const status = response.data?.checkToteStatus
        if (status !== '') {
          if (status == TOTE_STATUS.DAMAGED.value) {
            this.toteNo = ''
            throw new Error(i18next.t('text.tote_is_damaged_please_scan_again'))
          }

          if (status == TOTE_STATUS.DISPATCHED.value) {
            this.toteNo = ''
            throw new Error(i18next.t('text.tote_is_dispatched_please_scan_again'))
          }

          this._showToast({ message: i18next.t('text.tote_carton_(x)_is_scanned', { x: this.toteNoInput?.value }) })
        } else if (this.toteNoInput?.value !== '') {
          this._showToast({ message: i18next.t('text.tote_box_(x)_is_scanned', { x: this.toteNoInput?.value }) })
        }
      }
    } catch (e) {
      this._showToast(e)
    } finally {
      this.toteNoInput.value = ''
    }
  }

  async _fetchToteList() {
    try {
      let filters = [{ name: 'releaseGoodNo', operator: 'eq', value: this.orderNo }]

      const response = await client.query({
        query: gql`
          query orderTotes($filters: [Filter!]!) {
            orderTotes(filters: $filters) {
              items {
                id
                name
                closedDate
                orderToteSeals {
                  id
                  name
                }
              }
              total
            }
          }
        `,
        variables: {
          filters
        }
      })

      if (!response.errors) {
        response.data?.orderTotes.items.map(itm => {
          itm.orderToteSeals.orderToteId = itm.id
        })

        this._toteNoList = response.data?.orderTotes.items
      }
    } catch (e) {
      this._showToast(e)
    }
  }

  async _undoSelectedItems() {
    // Get selected rows from grist
    const selectedRecords = this.grist.selected

    if (!selectedRecords || selectedRecords.length === 0) {
      await CustomAlert({
        type: 'info',
        title: i18next.t('text.nothing_selected'),
        text: i18next.t('text.please_select_items_to_undo')
      })
      return
    }

    // Confirm before deleting
    const itemCount = selectedRecords.length
    const itemText =
      itemCount > 1
        ? `${itemCount} items`
        : `${selectedRecords[0].sku} (${selectedRecords[0].pickedQty} ${selectedRecords[0].packingType})`

    const answer = await CustomAlert({
      type: 'warning',
      title: i18next.t('button.undo'),
      text: i18next.t('text.are_you_sure_to_remove_x_from_tote', { x: itemText }),
      confirmButton: {
        text: i18next.t('button.confirm'),
        color: '#ff9800'
      },
      cancelButton: {
        text: i18next.t('button.cancel'),
        color: '#cfcfcf'
      }
    })

    if (!answer.value) return

    try {
      // Undo all selected items
      for (const record of selectedRecords) {
        await this._unsortItem(record.id, false) // Don't show individual toasts
      }

      // Show single success message for all items
      this._showToast({
        type: 'info',
        message:
          itemCount > 1
            ? i18next.t('text.items_removed_from_tote', { count: itemCount })
            : i18next.t('text.item_removed_from_tote')
      })

      // Refresh once after all items are removed
      await this._fetchToteList()
      await this.grist.fetch()
    } catch (e) {
      this._showToast(e)
    }
  }

  async _unsortItem(orderToteItemId, showToast = true) {
    try {
      const response = await client.mutate({
        mutation: gql`
          mutation unsortItem($orderToteItemId: String!, $reason: String) {
            unsortItem(orderToteItemId: $orderToteItemId, reason: $reason)
          }
        `,
        variables: {
          orderToteItemId,
          reason: 'Removed by user from tote popup'
        }
      })

      if (!response.errors) {
        if (showToast) {
          this._showToast({
            type: 'info',
            message: i18next.t('text.item_removed_from_tote')
          })

          // Refresh the tote list and grist
          await this._fetchToteList()
          await this.grist.fetch()
        }
      }
    } catch (e) {
      this._showToast(e)
      throw e // Re-throw to stop batch processing
    }
  }

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

window.customElements.define('tote-popup', TotePopup)
