import '@things-factory/biz-ui'
import '@things-factory/form-ui'
import '@things-factory/grist-ui'
import '@things-factory/import-ui'
import './select-cc-location-popup'

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

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,
  ScrollbarStyles
} from '@things-factory/styles'
import { isMobileDevice } from '@things-factory/utils'

import { fetchLocationSortingRule } from '../../fetch-location-sorting-rule'
import { LOCATION_SORTING_RULE } from '../constants'

class CreateCycleCount extends localize(i18next)(PageView) {
  static get properties() {
    return {
      config: Object,
      data: Object,
      inventoryData: Object,
      locationData: Object,
      _searchField: Array,
      _bizplaceId: String,
      _bizplaceName: String,
      _locationSortingRules: Object,
      _ccStrat: String
    }
  }

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

        .grist {
          display: flex;
          flex-direction: column;
          flex: 1;
          overflow-y: auto;
        }

        .grist-container {
          overflow-y: hidden;
          display: flex;
          flex: 1;
        }

        data-grist {
          overflow-y: hidden;
          flex: 1;
        }

        h2 {
          margin: 0 var(--margin-wide);
          padding: var(--subtitle-padding);
          font: var(--grist-title-font);
          color: var(--subtitle-text-color);
          border-bottom: var(--subtitle-border-bottom);
        }
        .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);
        }
        .button-container button {
          background-color: var(--button-container-button-background-color);
          border-radius: var(--button-container-button-border-radius);
          height: var(--button-container-button-height);
          border: var(--button-container-button-border);
          margin: var(--button-container-button-margin);

          padding: var(--button-padding);
          color: var(--theme-white-color);
          font: var(--button-font);
          text-transform: var(--button-text-transform);
        }
        .button-container button:hover,
        .button-container button:active {
          background-color: var(--button-background-focus-color);
        }
      `
    ]
  }

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

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

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

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

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

  get dataGristConfig() {
    return {
      pagination: { infinite: true },
      rows: {
        selectable: { multiple: false },
        classifier: (record, rowIndex) => {
          return {
            emphasized: record.isError
          }
        }
      },
      list: { fields: ['inventory', 'product', 'location', 'releaseQty'] },
      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.inventoryData = { ...this.inventoryData, records: newData }
                this.dataGrist.dirtyData.records = newData
                this._updateInventoryGrist()
              } catch (e) {
                this._showToast(e)
              }
            }
          }
        },
        {
          type: 'object',
          name: 'inventory',
          header: i18next.t('field.inventory_list'),
          imex: {
            header: i18next.t('field.inventory_list'),
            key: 'inventory.name',
            width: 50,
            type: 'array',
            arrData: []
          },
          record: {
            editable: !!this._bizplaceId,
            options: {
              queryName: 'inventoryProductGroup',
              basicArgs: {
                filters: [
                  { name: 'bizplace_id', operator: 'eq', value: this._bizplaceId },
                  { name: 'hideBundle', operator: 'eq', value: true },
                  { name: 'showAllInventory', operator: 'eq', value: true },
                  { name: 'cycleCount', operator: 'notin', value: true }
                ]
              },
              nameField: 'batchId',
              descriptionField: 'productName',
              select: [
                { name: 'productId', hidden: true },
                { name: 'productDetailId', hidden: true },
                { name: 'remainUomValue', hidden: true },
                { name: 'uom', hidden: true },
                { name: 'batchId', header: i18next.t('field.batch_id') },
                { name: 'batchIdRef', header: i18next.t('field.batch_no_ref') },
                {
                  name: 'productSKU',
                  header: i18next.t('field.sku'),
                  width: 150
                },
                {
                  name: 'productName',
                  header: i18next.t('field.product_info'),
                  width: 280
                },
                {
                  name: 'productBrand',
                  header: i18next.t('field.brand'),
                  width: 150
                },
                { name: 'packingType', header: i18next.t('field.packing_type') },
                { name: 'packingSize', header: i18next.t('field.packing_size') },
                {
                  name: 'remainQty',
                  header: i18next.t('field.remain_qty'),
                  ignoreCondition: true
                },
                {
                  name: 'remainUomValueWithUom',
                  header: i18next.t('field.remain_uom_value'),
                  ignoreCondition: true
                },
                {
                  name: 'lockedQty',
                  header: i18next.t('field.release_qty'),
                  ignoreCondition: true
                },
                {
                  name: 'unassignedQty',
                  header: i18next.t('field.unassigned_qty'),
                  ignoreCondition: true
                },
                {
                  name: 'groupType',
                  header: i18next.t('field.group_type'),
                  ignoreCondition: true
                }
              ],
              list: {
                fields: [
                  'batchId',
                  'batchIdRef',
                  'productName',
                  'packingType',
                  'remainQty',
                  'remainUomValue',
                  'lockedQty',
                  'unassignedQty',
                  'groupType'
                ]
              }
            }
          },
          width: 250
        },
        {
          type: 'string',
          name: 'productSKU',
          header: i18next.t('field.sku'),
          width: 100
        },
        {
          type: 'object',
          name: 'product',
          header: i18next.t('field.product'),
          width: 150
        },
        {
          type: 'string',
          name: 'productBrand',
          header: i18next.t('field.brand'),
          width: 100
        },
        {
          type: 'code',
          name: 'packingType',
          header: i18next.t('field.packing_type'),
          record: {
            codeName: 'PACKING_TYPES'
          },
          width: 150
        },
        {
          type: 'number',
          name: 'packingSize',
          header: i18next.t('field.packing_size'),
          width: 140
        },
        {
          type: 'integer',
          name: 'remainQty',
          header: i18next.t('field.available_qty'),
          width: 140
        },
        {
          type: 'string',
          name: 'remainUomValueWithUom',
          header: i18next.t('field.available_uom_value'),
          width: 140
        },
        {
          type: 'integer',
          name: 'lockedQty',
          header: i18next.t('field.release_qty'),
          width: 140
        },
        {
          type: 'integer',
          name: 'unassignedQty',
          header: i18next.t('field.unassigned_qty'),
          width: 140
        }
      ]
    }
  }

  get locationGristConfig() {
    return {
      pagination: { infinite: true },
      rows: {
        selectable: { multiple: false },
        classifier: (record, rowIndex) => {
          return {
            emphasized: record.isError
          }
        }
      },
      list: { fields: ['locationName', 'locationType'] },
      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.locationData = { ...this.locationData, records: newData }
                this.dataGrist.dirtyData.records = newData
                this._updateInventoryGrist()
              } catch (e) {
                this._showToast(e)
              }
            }
          }
        },
        {
          type: 'string',
          name: 'locationName',
          header: i18next.t('field.location_list'),
          handlers: {
            click: (columns, data, column, record, rowIndex) => {
              this._openSelectLocationPopup(rowIndex, record)
            }
          },
          width: 350
        },
        {
          type: 'string',
          name: 'locationType',
          header: i18next.t('field.type'),
          width: 100
        }
      ]
    }
  }

  get context() {
    return {
      title: i18next.t('title.create_cycle_count'),
      actions: [
        {
          title: i18next.t('button.create'),
          action: this._createCycleCount.bind(this),
          ...CommonButtonStyles.submit
        }
      ],
      help: 'operato-wms/inventory-check/create-cycle-count'
    }
  }

  render() {
    let dynamicFields = html``

    switch (this._ccStrat) {
      case 'random':
        dynamicFields = html`
          <form id="input-form" name="randomStratFrom" class="multi-column-form">
            <fieldset>
              <label>${i18next.t('label.no_of_random_item')}</label>
              <input name="limit" type="number" />
            </fieldset>
          </form>
        `
        break
      case 'product':
        dynamicFields = html`
          <div class="grist-container">
            <div class="grist">
              <data-grist
                id="inventory"
                .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
                .config=${this.inventoryGristConfig}
                .data=${this.inventoryData}
                @field-change="${this._gristFieldChange.bind(this)}"
              ></data-grist>
            </div>
          </div>
        `
        break
      case 'location':
        dynamicFields = html`
          <div class="grist-container">
            <div class="grist">
              <data-grist
                id="location"
                .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
                .config=${this.locGristConfig}
                .data=${this.locationData}
                @field-change="${this._gristFieldChange.bind(this)}"
              ></data-grist>
            </div>
          </div>
        `
        break
      default:
        break
    }

    return html`
      <form id="input-form" name="cycleCount" class="multi-column-form">
        <fieldset>
          <legend>${i18next.t('title.cycle_count_information')}</legend>
          <label>${i18next.t('label.company')}</label>
          <input type="hidden" name="bizplaceId" value="${this._bizplaceId}" />
          <input
            type="text"
            name="bizplaceName"
            value="${this._bizplaceName}"
            readonly
            @click="${this._openBizplacesSelector.bind(this)}"
          />

          <label>${i18next.t('label.execute_date')}</label>
          <input name="executionDate" type="date" min="${this._getStdDate()}" required />

          <input type="checkbox" name="lockedQty">
          <label>${i18next.t('label.locked_qty')}</label>
        </fieldset>
        <fieldset>
          <legend>${i18next.t('title.cycle_count_configuration')}</legend>

          <input
            id="cc-by-random-product"
            name="cc-strat"
            type="radio"
            value="random"
            @change="${e => {
              this._ccStrat = e.currentTarget.value
              this.inventoryGristConfig = this.dataGristConfig
            }}"
            .checked="${this._ccStrat === 'random'}"
            checked
          />
          <label for="cc-by-random-product">Random</label>

          <input
            id="cc-by-product"
            name="cc-strat"
            type="radio"
            value="product"
            @change="${e => {
              this._ccStrat = e.currentTarget.value
              this.inventoryGristConfig = this.dataGristConfig
            }}"
            .checked="${this._ccStrat === 'product'}"
            checked
          />
          <label for="cc-by-product">By Product</label>

          <input
            id="cc-by-location"
            name="cc-strat"
            type="radio"
            value="location"
            @change="${e => {
              this._ccStrat = e.currentTarget.value
              this.locGristConfig = this.locationGristConfig
            }}"
            .checked="${this._ccStrat === 'location'}"
            checked
          />
          <label for="cc-by-location">By Location</label>
        </fieldset>
      </form>

      ${dynamicFields}
    `
  }

  pageUpdated(changes) {
    if (this.active) {
      this._bizplaceName = this._bizplaceId && this._bizplaceName ? this._bizplaceName : ''
      this._clearView()
    }
  }

  constructor() {
    super()
    this.inventoryData = { ...this.inventoryData, records: [] }
    this.locationData = { ...this.locationData, records: [] }
  }

  async pageInitialized() {
    this._ccStrat = 'random'
    this._locationSortingRules = await fetchLocationSortingRule(LOCATION_SORTING_RULE.CREATE_RELEASE_ORDER.value)
    this.inventoryGristConfig = this.dataGristConfig
    this.locGristConfig = this.locationGristConfig
  }

  async _createCycleCount() {
    try {
      this._validate()

      const result = await CustomAlert({
        title: i18next.t('title.are_you_sure'),
        text: i18next.t('text.create_cycle_count_worksheet'),
        confirmButton: { text: i18next.t('button.confirm') },
        cancelButton: { text: i18next.t('button.cancel') }
      })

      if (!result.value) return

      CustomAlert({
        title: i18next.t('text.please_wait'),
        text: i18next.t('text.creating_cycle_count_worksheet'),
        allowOutsideClick: false,
        allowEscapeKey: false
      })

      let limit
      if (this?.limitField?.value) limit = this.limitField.value
      else limit = null

      const worksheetNo = await this.generateCycleCountWorksheet(
        this._bizplaceId,
        this.date.value,
        limit ? parseInt(limit) : null,
        this?.locationData?.records,
        this.lockInventoryField.checked,
      )

      if (worksheetNo) {
        await CustomAlert({
          title: i18next.t('title.completed'),
          text: i18next.t('text.completed_x', { state: { x: i18next.t('text.create_cycle_count_worksheet') } }),
          confirmButton: { text: i18next.t('button.confirm') }
        })

        navigate(`worksheet_cycle_count/${worksheetNo}`)
      } else {
        CustomAlert({
          title: i18next.t('title.error'),
          text: i18next.t('text.x_error', { state: { x: i18next.t('text.create_cycle_count_worksheet') } }),
          confirmButton: { text: i18next.t('button.confirm') }
        })
      }
    } catch (e) {
      this._showToast(e)

      CustomAlert({
        title: i18next.t('title.error'),
        text: i18next.t('text.x_error', { state: { x: i18next.t('text.create_cycle_count_worksheet') } }),
        confirmButton: { text: i18next.t('button.confirm') }
      })
    }
  }

  _openSelectLocationPopup(rowIndex, record) {
    if (!this._bizplaceId || this?._bizplaceId == '') {
      this._showToast({ message: i18next.t('text.bizplace_is_not_selected') })
      return
    }

    openPopup(
      html`
        <select-cc-location-popup
          .bizplaceId="${this._bizplaceId}"
          .selectMultipleLocations="${true}"
          .selectedLocation="${this.dataGrist.dirtyData.records}"
          @location-selected="${e => {
            let newRecords = e.detail.multipleLocations.map(itm => ({
              locationId: itm.location.id,
              locationName: itm.location.name,
              locationType: itm.location.type
            }))

            if (newRecords) {
              this.locationData = {
                ...this.locationData,
                records: [...this.locationData.records, ...newRecords]
              }
            } else {
              // Update existing location data records
              if (this.dataGrist && this.dataGrist.dirtyData) {
                this.dataGrist.dirtyData.records[rowIndex] = {
                  ...this.dataGrist.dirtyData.records[rowIndex],
                  ...newRecords[0]
                }
              }
              this.locationData = {
                ...this.locationData,
                records: [...this.dataGrist.dirtyData.records]
              }
            }
          }}"
        ></select-cc-location-popup>
      `,
      {
        backdrop: true,
        size: 'medium',
        title: i18next.t('title.select_location')
      }
    )
  }

  _validate() {
    if (!this._bizplaceId || this?._bizplaceId == '') {
      throw new Error(i18next.t('text.bizplace_is_not_selected'))
    }

    if (!this.cycleCountForm.checkValidity()) {
      throw new Error(i18next.t('text.cycle_count_date_invalid'))
    }

    switch (this._ccStrat) {
      case 'random':
        if (this.limitField.value && this.limitField.value <= 0) {
          throw new Error(i18next.t('text.invalid_no_of_random_item_value'))
        }
        break

      case 'product':
        if (this?.inventoryData?.records && this?.inventoryData?.records <= 0) {
          throw new Error(i18next.t('text.nothing_selected'))
        }
        break

      case 'location':
        if (this?.locationData?.records && this?.locationData?.records <= 0) {
          throw new Error(i18next.t('text.nothing_selected'))
        }
        break
      default:
        break
    }
  }

  async generateCycleCountWorksheet(customerId, executionDate, limit, selectedLocation, lockInventory) {
    let inventoryCheckItems = []
    // let selectedLocation = null // Initialize selectedLocation

    if (this._ccStrat === 'product' || this._ccStrat === 'random') {
      inventoryCheckItems =
        this?.inventoryData?.records.map(item => {
          return {
            batchId: item.batchId,
            batchIdRef: item.batchIdRef,
            productDetailId: item.productDetailId
          }
        }) || []
        selectedLocation = []
    } else if (this._ccStrat === 'location') {
      selectedLocation = this?.locationData?.records.map(location => location.locationId) || []
    }

    const response = await client.mutate({
      mutation: gql`
        mutation generateCycleCountWorksheet(
          $executionDate: String!
          $customerId: String!
          $inventoryCheckItems: [InventoryCheckItemPatch]
          $limit: Int
          $selectedLocation: [String]!
          $lockInventory: Boolean
        ) {
          generateCycleCountWorksheet(
            executionDate: $executionDate
            customerId: $customerId
            inventoryCheckItems: $inventoryCheckItems
            limit: $limit
            selectedLocation: $selectedLocation
            lockInventory: $lockInventory
          ) {
            name
          }
        }
      `,
      variables: { executionDate, customerId, inventoryCheckItems, limit, selectedLocation, lockInventory }
    })

    if (!response.errors) {
      this._clearView()
      return response.data.generateCycleCountWorksheet.name
    } else {
      return null
    }
  }

  _clearView() {
    this._clearGristConditions()
  }

  _openBizplacesSelector() {
    openPopup(
      html`
        <popup-partner-bizplaces-selector
          @selected="${e => {
            this._bizplaceId = e.detail.id
            this._bizplaceName = e.detail.name
            this.inventoryGristConfig = this.dataGristConfig
            this.locGristConfig = this.locationGristConfig
            this.inventoryData = { records: [] }
            this.locationData = { records: [] }
            this._clearGristConditions()
          }}"
        ></popup-partner-bizplaces-selector>
      `,
      {
        backdrop: true,
        size: 'large',
        title: i18next.t('title.select_company')
      }
    )
  }

  _updateInventoryGrist(e) {
    if (!this._bizplaceId || this?._bizplaceId == '') {
      this._showToast(i18next.t('text.bizplace_is_not_selected'))
      return
    }

    if (this._ccStrat === 'product' || this._ccStrat === 'random') {
      const _selectedInv = (this.dataGrist.dirtyData.records || []).map(record => {
        return {
          batchId: record?.inventory?.batchId,
          packingType: record?.inventory?.packingType,
          packingSize: record?.inventory?.packingSize,
          productId: record?.inventory?.productId,
          groupType: record?.inventory?.groupType
        }
      })

      this.inventoryGristConfig = {
        ...this.inventoryGristConfig,
        columns: this.inventoryGristConfig.columns.map(column => {
          if (column.name === 'inventory') {
            column.record.options.basicArgs = {
              ...column.record.options.basicArgs,
              filters: [...column.record.options.basicArgs.filters.filter(filter => filter.name !== 'batch_product')]
            }

            if (_selectedInv.length) {
              column.record.options.basicArgs.filters.push({
                name: 'batch_product',
                value: _selectedInv,
                operator: 'notin'
              })
            }
          }

          return column
        })
      }
    } else if (this._ccStrat === 'location') {
      const _selectedLocations = (this.dataGrist.dirtyData.records || []).map(record => record?.location?.id)
      this.locGristConfig = {
        ...this.locGristConfig,
        columns: this.locGristConfig.columns.map(column => {
          if (column.name === 'location') {
            column.record.options.basicArgs = {
              ...column.record.options.basicArgs,
              filters: [...column.record.options.basicArgs.filters.filter(filter => filter.name !== 'id')]
            }

            if (_selectedLocations.length)
              column.record.options.basicArgs.filters = [
                ...column.record.options.basicArgs.filters,
                {
                  name: 'id',
                  value: _selectedLocations,
                  operator: 'notin'
                }
              ]
          }

          return column
        })
      }
    }
  }

  _gristFieldChange(e) {
    var { after, before, column, record, row } = e.detail
    if (column?.name == 'inventory') {
      if (this.inventoryData && this.dataGrist && this.dataGrist.dirtyData) {
        this.inventoryData = {
          ...this.inventoryData,
          ...this.dataGrist.dirtyData,
          records: this.dataGrist.dirtyData.records.map((record, idx) => {
            let returnObj = {
              ...record,
              ...record.inventory,
              name: record.name ? record.name : idx,
              id: idx,
              product: {
                id: record?.inventory?.productId,
                name: record?.inventory?.productName
              }
            }

            return returnObj
          })
        }
      }
    }

    if (column?.name == 'locationName') {
      if (this.locationData && this.dataGrist && this.dataGrist.dirtyData) {
        this.locationData = {
          ...this.locationData,
          ...this.dataGrist.dirtyData,
          records: this.dataGrist.dirtyData.records.map((record, idx) => {
            let returnObj = {
              ...record,
              ...record.location,
              name: record.name ? record.name : idx,
              id: idx,
              location: {
                id: record?.location?.locationId,
                name: record?.location?.locationName,
                type: record?.location.locationType
              }
            }

            returnObj
          })
        }
      }
    }

    this._updateInventoryGrist()
  }

  _clearGristConditions() {
    if (this.inventoryGristConfig?.columns?.length) {
      this.inventoryGristConfig = {
        ...this.inventoryGristConfig,
        columns: this.inventoryGristConfig.columns.map(column => {
          if (column.name === 'inventory') {
            column.record.options.basicArgs = {
              filters: column.record.options.basicArgs.filters.filter(
                filter => filter.name !== 'batch_product' && filter.name !== 'id'
              )
            }
          }

          return column
        })
      }
    }
  }

  _getStdDate() {
    let date = new Date()
    date.setDate(date.getDate())
    return date.toISOString().split('T')[0]
  }

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

window.customElements.define('create-cycle-count', CreateCycleCount)
