import Vue, { VNode, PropType } from 'vue'
import { VDataTableA, VIconA } from '../../shims-vuetify'
import VColumnEditor from './VColumnEditor'
import VTableFilter from './VTableFilter'
import { TableHeader, TableHeaderEdition, ColumnEditorResult } from '../VAdvDataTable/utils/AdvTableUtils'
import { ScopedSlot } from 'vue/types/vnode'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const VDataTableProps = ((VDataTableA as any).options as any).props

export default Vue.extend({
  name: 'v-adv-data-table',
  props: {
    headerIcon: {
      type: String,
      default: undefined
    },
    headerIconColor: {
      type: String,
      default: undefined
    },
    filterIcon: {
      type: String,
      default: undefined
    },
    filterActiveIcon: {
      type: String,
      default: undefined
    },
    filterActiveIconColor: {
      type: String,
      default: undefined
    },
    filterOnHover: {
      type: Boolean,
      default: false
    },
    filterSelectionIcon: {
      type: String,
      default: '$unfold'
    },
    upIcon: {
      type: String,
      default: 'expand_more'
    },
    downIcon: {
      type: String,
      default: 'expand_less'
    },
    sortIcon: {
      type: String,
      default: '$sort'
    },
    ...VDataTableProps,
    headers: Array as PropType<TableHeader[]>
  },
  data: () => ({
    isMenuActive: false,
    dataHeaders: [] as TableHeader[],
    valueFilter: new Map(),
    valueFilterUpdateTracker: 1,
    invisibleHeaders: new Set<string>(),
    order: [] as TableHeaderEdition[]
  }),
  computed: {
    computedHeadersTable (): TableHeader[] {
      const headers = this.$props.headers.slice()
      const defaultHeader = { text: '', sortable: false, width: '18px' }
      const index = headers.findIndex((h: TableHeader) => h.value === 'data-table-settings')
      if (index < 0) headers.push({ ...defaultHeader, value: 'data-table-settings' })
      return headers
    },
    visibleHeaders (): TableHeader[] {
      const headers: TableHeader[] = []
      this.order.forEach(val => {
        const current = this.$props.headers.find((v:TableHeader) => v.value === val.value)
        if (current) headers.push(current)
      })
      const defaultSeettings = this.computedHeadersTable.find((v:TableHeader) => v.value === 'data-table-settings')
      if (defaultSeettings) headers.push(defaultSeettings)
      return headers.filter((v: TableHeader) => !this.invisibleHeaders.has(v.value) || v.value === 'data-table-settings')
    },
    editedHeaders (): TableHeader[] {
      const headers: TableHeader[] = []
      this.order.forEach(val => {
        const current = this.$props.headers.find((v:TableHeader) => v.value === val.value)
        if (current) headers.push(current)
      })
      headers.map((v: TableHeader) => {
        if (!this.invisibleHeaders.has(v.value)) v.visible = true
        else v.visible = false
      })
      return headers
    },
    filteredItems (): any[] {
      if (this.valueFilterUpdateTracker || !this.valueFilterUpdateTracker) {
        return this.$props.items.filter((row: any) => {
          for (const [key, value] of this.valueFilter) {
            if (Array.isArray(value) && value.length > 0) {
              if (row[key] && value.indexOf(row[key].toString()) === -1) return false
            }
          }
          return true
        })
      } else {
        return this.$props.items.filter((row: any) => {
          for (const [key, value] of this.valueFilter) {
            if (Array.isArray(value) && value.length > 0) {
              if (row[key] && value.indexOf(row[key].toString()) === -1) return false
            }
          }
          return true
        })
      }
    },
    filteredItemsCount (): number {
      return this.filteredItems.length
    }
  },
  watch: {
    filteredItemsCount: {
      handler (val: number) {
        this.$emit('items-count-change', val)
      }
    }
  },
  created () {
    this.refreshHeaders()
  },
  methods: {
    refreshHeaders () {
      this.order = []
      this.$props.headers.map((v:TableHeader) => {
        this.order.push({
          value: v.value,
          text: v.text,
          visible: true
        })
      })
    },
    genHeaderSettings (): VNode {
      return this.$createElement(VColumnEditor, {
        props: {
          editedHeaders: this.editedHeaders,
          headerIcon: this.$props.headerIcon,
          headerIconColor: this.$props.headerIconColor,
          upIcon: this.$props.upIcon,
          downIcon: this.$props.downIcon,
          dark: this.$props.dark,
          dense: this.$props.dense
        },
        on: {
          'headers-changed': (newSettings: ColumnEditorResult) => {
            this.$nextTick(() => {
              this.invisibleHeaders = newSettings.invisible
              this.order = newSettings.order
            })
          }
        }
      })
    },
    getValues (val: string) {
      return this.$props.items.map((v: any) => v[val])
    },
    applyFilter (vals: any[], header: TableHeader) {
      if (header.value) {
        this.valueFilter.set(header.value, vals)
        this.valueFilterUpdateTracker += 1
      }
    },
    clearFilter (header: TableHeader) {
      if (header.value && this.valueFilter.has(header.value)) {
        this.valueFilter.delete(header.value)
        this.valueFilterUpdateTracker += 1
      }
    },
    genFilter (header: TableHeader): VNode {
      return this.$createElement(VTableFilter, {
        props: {
          header: header,
          getItemValues: this.getValues,
          filterIcon: this.$props.filterIcon,
          filterActiveIcon: this.$props.filterActiveIcon,
          filterIconColor: this.$props.filterIconColor,
          filterActiveIconColor: this.$props.filterActiveIconColor,
          filterSelectionIcon: this.$props.filterSelectionIcon,
          dense: this.$props.dense,
          dark: this.$props.dark
        },
        on: {
          'filter-change': (values: any[]) => this.applyFilter(values, header),
          'clear-filter': (header: TableHeader) => this.clearFilter(header)
        }
      })
    },
    genSortIcon (): VNode {
      return this.$createElement(VIconA, {
        staticClass: 'v-data-table-header__icon',
        props: {
          size: 18
        }
      }, [this.$props.sortIcon])
    },
    genDefaultHeader (header: TableHeader): VNode[] {
      const res = [] as VNode[]
      res.push(
        this.$createElement('span', {
          style: {
            display: 'inline-block',
            'align-items': 'center'
          }
        }, [header.text])
      )
      if (header.filterable) res.push(this.genFilter(header))
      if (header.sortable) res.push(this.genSortIcon())
      return res
    },
    genDefaultHeaderWithSlot (header: TableHeader, slot: any): VNode[] {
      const res = []
      res.push(slot.call(this))
      if (header.filterable) {
        res.push(this.genFilter(header))
      }
      if (header.sortable) {
        res.push(this.genSortIcon())
      }
      return res
    },
    genTableScopedSlots (): {[key: string]: ScopedSlot| undefined}| undefined {
      let slots = {}
      this.visibleHeaders.forEach((element: TableHeader) => {
        const slotName = 'header.' + element.value
        let currentSlot
        if (this.$scopedSlots[slotName]) {
          currentSlot = this.genDefaultHeaderWithSlot.bind(this, element, this.$scopedSlots[slotName])
        }
        const slotnew: any = {} as any
        (slotnew as any)[slotName] = currentSlot || this.genDefaultHeader.bind(this, element)
        slots = Object.assign(slots, slotnew)
        const slotNameItemName = 'item.' + element.value
        let currentSlotItem
        if (this.$scopedSlots[slotNameItemName]) {
          currentSlotItem = this.$scopedSlots[slotNameItemName]
          const slotnewItem: any = {} as any
          (slotnewItem as any)[slotNameItemName] = currentSlotItem
          slots = Object.assign(slots, slotnewItem)
        }
      })
      slots = Object.assign(slots, { 'header.data-table-settings': this.genHeaderSettings })
      return slots
    }
  },
  render (): VNode {
    const currentProps = Object.assign({}, this.$props)
    currentProps.headers = this.visibleHeaders
    currentProps.items = this.filteredItems
    const scopedSlots = this.genTableScopedSlots()
    return this.$createElement(VDataTableA, {
      props: currentProps,
      scopedSlots: scopedSlots,
      on: this.$listeners
    }, this.$slots as any)
  }
})
