// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

<template>
  <resource-layout>
    <template #left>
      <a-spin :spinning="loading">
        <a-card :bordered="false">
          <a-input-search
            size="default"
            :placeholder="$t('label.search')"
            v-model:value="searchQuery"
            @search="onSearch"
          >
            <template #prefix><search-outlined /></template>
          </a-input-search>
          <a-spin :spinning="loadingSearch">
            <a-tree
              showLine
              v-if="treeViewData.length > 0"
              class="list-tree-view"
              :treeData="treeViewData"
              :loadData="onLoadData"
              :expandAction="false"
              :showIcon="true"
              :selectedKeys="defaultSelected"
              :checkStrictly="true"
              @select="onSelect"
              @expand="onExpand"
              :expandedKeys="arrExpand">
              <template #icon="{data}">
                <block-outlined v-if="data.isLeaf" />
              </template>
            </a-tree>
          </a-spin>
        </a-card>
      </a-spin>
    </template>
    <template #right>
      <a-spin :spinning="detailLoading">
        <a-card
          class="spin-content"
          :bordered="true"
          style="width:100%">
          <a-tabs
            style="width: 100%"
            :animated="false"
            :defaultActiveKey="tabs[0].name"
            @change="onTabChange" >
            <template v-for="tab in tabs" :tab="$t('label.' + tab.name)" :key="tab.name">
              <a-tab-pane :tab="$t('label.' + tab.name)" :key="tab.name" v-if="checkShowTabDetail(tab)">
                <keep-alive>
                  <component
                    v-if="tab.resourceType"
                    :is="tab.component"
                    :resource="resource"
                    :resourceType="tab.resourceType"
                    :loading="loading"
                    :tab="tabActive"
                    :bordered="false" />
                  <component
                    v-else
                    :is="tab.component"
                    :resource="resource"
                    :items="items"
                    :tab="tabActive"
                    :loading="loading"
                    :bordered="false" />
                </keep-alive>
              </a-tab-pane>
            </template>
          </a-tabs>
        </a-card>
      </a-spin>
    </template>
  </resource-layout>
</template>

<script>
import store from '@/store'
import { callAPI } from '@/api'
import DetailsTab from '@/components/view/DetailsTab'
import ResourceView from '@/components/view/ResourceView'
import ResourceLayout from '@/layouts/ResourceLayout'
import eventBus from '@/config/eventBus'

export default {
  name: 'TreeView',
  components: {
    ResourceLayout,
    ResourceView
  },
  props: {
    treeData: {
      type: Array,
      required: true
    },
    treeSelected: {
      type: Object,
      required: true
    },
    tabs: {
      type: Array,
      default () {
        return [{
          name: 'details',
          component: DetailsTab
        }]
      }
    },
    loadedKeys: {
      type: Array,
      default () {
        return []
      }
    },
    loading: {
      type: Boolean,
      default: false
    },
    treeStore: {
      type: Object,
      default () {
        return {}
      }
    },
    treeDeletedKey: {
      type: String,
      default: null
    }
  },
  provide: function () {
    return {
      parentFetchData: null,
      parentToggleLoading: null
    }
  },
  data () {
    return {
      detailLoading: false,
      loadingSearch: false,
      tabActive: 'details',
      selectedTreeKey: '',
      resource: {},
      defaultSelected: [],
      treeVerticalData: [],
      treeViewData: [],
      oldTreeViewData: [],
      apiList: '',
      apiChildren: '',
      apiDetail: '',
      metaName: '',
      page: 1,
      pageSize: 20,
      items: [],
      showSetting: false,
      oldSearchQuery: '',
      searchQuery: '',
      arrExpand: [],
      rootKey: ''
    }
  },
  created: function () {
    this.metaName = this.$route.meta.name
    this.apiList = this.$route.meta.permission[0] ? this.$route.meta.permission[0] : ''
    this.apiChildren = this.$route.meta.permission[1] ? this.$route.meta.permission[1] : ''
    eventBus.on('refresh-domain-icon', () => {
      this.getDetailResource(this.selectedTreeKey)
    })
  },
  watch: {
    loading () {
      this.detailLoading = this.loading
      this.treeViewData = []
      this.arrExpand = []
      if (!this.loading) {
        this.treeViewData = this.treeData.slice()
        this.treeVerticalData = this.treeData.slice()

        if (this.treeViewData.length > 0) {
          this.oldTreeViewData = this.treeViewData.slice()
          this.rootKey = this.treeViewData[0].key
        }
      }
    },
    treeSelected: {
      deep: true,
      handler () {
        if (Object.keys(this.treeSelected).length === 0) {
          return
        }

        const resourceKeys = Object.keys(this.resource)
        if (resourceKeys.length > 0) {
          this.selectedTreeKey = this.resource.key
          this.$emit('change-resource', this.resource)
          if (resourceKeys.filter(x => x.endsWith('limit')).length === 0) {
            setTimeout(() => { this.getDetailResource(this.resource.id) })
          }

          // set default expand
          if (this.defaultSelected.length > 1) {
            const arrSelected = this.defaultSelected
            this.defaultSelected = []
            this.defaultSelected.push(arrSelected[0])
          }

          return
        }

        this.resource = this.treeSelected
        this.resource = this.createResourceData(this.resource)
        this.selectedTreeKey = this.treeSelected.key
        this.defaultSelected.push(this.selectedTreeKey)

        // set default expand
        if (this.defaultSelected.length > 1) {
          const arrSelected = this.defaultSelected
          this.defaultSelected = []
          this.defaultSelected.push(arrSelected[0])
        }
      }
    },
    treeVerticalData: {
      deep: true,
      handler () {
        if (!this.treeStore.isExpand) {
          return
        }
        if (this.treeStore.expands && this.treeStore.expands.length > 0) {
          for (const expandKey of this.treeStore.expands) {
            if (this.arrExpand.includes(expandKey)) {
              continue
            }
            const keyVisible = this.treeVerticalData.findIndex(item => item.key === expandKey)
            if (keyVisible > -1) this.arrExpand.push(expandKey)
          }
        }
      }
    }
  },
  methods: {
    onLoadData (treeNode) {
      if (this.searchQuery !== '' && treeNode.eventKey !== this.rootKey) {
        return new Promise(resolve => {
          resolve()
        })
      }

      const params = {
        listAll: true,
        id: treeNode.eventKey,
        showicon: true
      }

      return new Promise(resolve => {
        callAPI(this.apiChildren, params).then(json => {
          const dataResponse = this.getResponseJsonData(json)
          const dataGenerate = this.generateTreeData(dataResponse)
          treeNode.dataRef.children = dataGenerate

          if (this.treeVerticalData.length === 0) {
            this.treeVerticalData = this.treeViewData
          }

          this.treeViewData = [...this.treeViewData]
          this.oldTreeViewData = this.treeViewData

          for (let i = 0; i < dataGenerate.length; i++) {
            const resource = this.treeVerticalData.filter(item => item.id === dataGenerate[i].id)

            if (!resource || resource.length === 0) {
              this.treeVerticalData.push(dataGenerate[i])
            } else {
              this.treeVerticalData.filter((item, index) => {
                if (item.id === dataGenerate[i].id) {
                  // replace all value of tree data
                  Object.keys(dataGenerate[i]).forEach((value, idx) => {
                    this.treeVerticalData[index][value] = dataGenerate[i][value]
                  })
                }
              })
            }
          }

          this.handleSelectResource(treeNode.eventKey)
          resolve()
        })
      })
    },
    async handleSelectResource (treeKey) {
      if (this.treeDeletedKey && this.treeDeletedKey === this.treeStore?.treeSelected?.key) {
        const treeSelectedKey = this.treeStore.treeSelected.parentdomainid
        if (treeSelectedKey === this.rootKey) {
          this.resource = this.treeVerticalData[0]

          this.selectedTreeKey = treeSelectedKey
          this.defaultSelected = [treeSelectedKey]
          this.$emit('change-resource', this.resource)
          await this.setTreeStore(false, false, this.resource)
          return
        }
        const resourceIndex = await this.treeVerticalData.findIndex(item => item.id === treeSelectedKey)
        if (resourceIndex > -1) {
          const resource = await this.getDetailResource(treeSelectedKey)
          this.resource = await this.createResourceData(resource)

          this.selectedTreeKey = treeSelectedKey
          this.defaultSelected = [treeSelectedKey]
          this.$emit('change-resource', this.resource)
          await this.setTreeStore(false, false, this.resource)
          return
        }
      }
      const treeSelectedKey = this.treeStore.treeSelected.key
      const resourceIndex = await this.treeVerticalData.findIndex(item => item.id === treeSelectedKey)
      if (resourceIndex > -1) {
        this.selectedTreeKey = treeSelectedKey
        this.defaultSelected = [treeSelectedKey]

        this.resource = this.treeStore.treeSelected
        this.$emit('change-resource', this.resource)
      }
    },
    async onSelect (selectedKeys, event) {
      if (!event.selected) {
        return
      }

      // check item tree selected, set selectedTreeKey
      if (selectedKeys && selectedKeys[0]) {
        this.selectedTreeKey = selectedKeys[0]
      }

      this.defaultSelected = []
      this.defaultSelected.push(this.selectedTreeKey)
      const resource = await this.getDetailResource(this.selectedTreeKey)
      this.resource = await this.createResourceData(resource)
      const index = this.treeVerticalData.findIndex(item => item.key === this.selectedTreeKey)
      this.treeVerticalData[index] = this.resource

      this.$emit('change-resource', this.resource)
      await this.setTreeStore(false, false, this.resource)
    },
    onExpand (treeExpand) {
      const treeStore = this.treeStore
      this.arrExpand = treeExpand
      treeStore.isExpand = true
      treeStore.expands = this.arrExpand
      treeStore.treeSelected = this.resource
      this.$emit('change-tree-store', treeStore)
    },
    onSearch (value) {
      if (this.searchQuery === '' && this.oldSearchQuery === '') {
        return
      }

      this.searchQuery = value
      this.newTreeData = this.treeViewData
      this.treeVerticalData = this.newTreeData

      // set parameter for the request
      const params = {}
      params.listall = true

      // Check the search query to set params and variables using reset data
      if (this.searchQuery !== '') {
        this.oldSearchQuery = this.searchQuery
        params.keyword = this.searchQuery
      } else if (this.metaName === 'domain') {
        this.oldSearchQuery = ''
        params.id = this.$store.getters.userInfo.domainid
      }

      this.arrExpand = []
      this.treeViewData = []
      this.loadingSearch = true
      this.$emit('change-tree-store', {})
      callAPI(this.apiList, params).then(json => {
        const listDomains = this.getResponseJsonData(json)
        this.treeVerticalData = this.treeVerticalData.concat(listDomains)

        if (!listDomains || listDomains.length === 0) {
          return
        }

        if (listDomains[0].id === this.rootKey) {
          const rootDomain = this.generateTreeData(listDomains)
          this.treeViewData = rootDomain
          this.defaultSelected = []
          this.defaultSelected.push(this.treeViewData[0].key)
          this.resource = this.treeViewData[0]
          this.$emit('change-resource', this.resource)

          return
        }

        this.recursiveTreeData(listDomains)

        if (this.treeViewData && this.treeViewData[0]) {
          this.defaultSelected = []
          this.defaultSelected.push(this.treeViewData[0].key)
          this.resource = this.treeViewData[0]
          this.$emit('change-resource', this.resource)
        }

        // check treeViewData, set to expand first children
        if (this.treeViewData &&
            this.treeViewData[0] &&
            this.treeViewData[0].children &&
            this.treeViewData[0].children.length > 0
        ) {
          this.arrExpand.push(this.treeViewData[0].children[0].key)
        }
      }).finally(() => {
        this.loadingSearch = false
      })
    },
    onTabChange (key) {
      this.tabActive = key
    },
    setTreeStore (arrExpand, isExpand, resource) {
      const treeStore = this.treeStore
      if (arrExpand) treeStore.expands = arrExpand
      if (isExpand) treeStore.isExpand = true
      if (resource) treeStore.treeSelected = resource
      this.$emit('change-tree-store', treeStore)
    },
    getDetailResource (selectedKey) {
      return new Promise(resolve => {
        // set api name and parameter
        const apiName = this.$route.meta.permission[0]
        const params = {}

        // set id to parameter
        params.id = selectedKey
        params.listAll = true
        params.showicon = true
        params.page = 1
        params.pageSize = 1

        this.detailLoading = true
        callAPI(apiName, params).then(json => {
          const jsonResponse = this.getResponseJsonData(json)
          resolve(jsonResponse[0])
        }).catch(() => {
          resolve()
        }).finally(() => {
          this.detailLoading = false
        })
      })
    },
    getResponseJsonData (json) {
      let responseName
      let objectName
      let hasJobId = false
      for (const key in json) {
        if (key.includes('response')) {
          for (const res in json[key]) {
            if (res === 'jobid') {
              hasJobId = true
              break
            }
          }
          responseName = key
          break
        }
      }

      for (const key in json[responseName]) {
        if (key === 'count') {
          continue
        }

        objectName = key
        break
      }
      if (hasJobId) {
        return {}
      }
      return json[responseName][objectName]
    },
    checkShowTabDetail (tab) {
      if ('show' in tab) {
        return tab.show(this.resource, this.$route, store.getters.userInfo)
      }
      // get permission from the route
      const permission = tab.permission ? tab.permission[0] : ''

      // check permission not exists
      if (!permission || permission === '') {
        return true
      }

      // Check the permissions to see the tab for a user
      if (!Object.prototype.hasOwnProperty.call(store.getters.apis, permission)) {
        return false
      }

      return true
    },
    generateTreeData (jsonData) {
      if (!jsonData || jsonData.length === 0) {
        return []
      }

      for (let i = 0; i < jsonData.length; i++) {
        jsonData[i] = this.createResourceData(jsonData[i])
      }

      return jsonData
    },
    createResourceData (resource) {
      if (!resource || Object.keys(resource) === 0) {
        return {}
      }

      Object.keys(resource).forEach((value, idx) => {
        if (resource[value] === 'Unlimited') {
          resource.value = '-1'
        }
      })
      resource.title = resource.name
      resource.key = resource.id
      if (resource?.icon) {
        resource.resourceIcon = resource.icon
        delete resource.icon
      }

      if (!resource.haschild) {
        resource.isLeaf = true
      }

      return resource
    },
    recursiveTreeData (treeData) {
      const maxLevel = Math.max.apply(Math, treeData.map((o) => { return o.level }))
      const items = treeData.filter(item => item.level <= maxLevel)
      this.treeViewData = this.getNestedChildren(items, 0, maxLevel)
      this.oldTreeViewData = this.treeViewData
    },
    getNestedChildren (dataItems, level, maxLevel, id) {
      if (level > maxLevel) {
        return
      }

      let items = []

      if (!id || id === '') {
        items = dataItems.filter(item => item.level === level)
      } else {
        items = dataItems.filter(item => {
          let parentKey = ''
          const arrKeys = Object.keys(item)
          for (let i = 0; i < arrKeys.length; i++) {
            if (arrKeys[i].indexOf('parent') > -1 && arrKeys[i].indexOf('id') > -1) {
              parentKey = arrKeys[i]
              break
            }
          }

          return parentKey ? item[parentKey] === id : item.level === level
        })
      }

      if (items.length === 0) {
        return this.getNestedChildren(dataItems, (level + 1), maxLevel)
      }

      for (let i = 0; i < items.length; i++) {
        items[i] = this.createResourceData(items[i])

        if (items[i].haschild) {
          items[i].children = this.getNestedChildren(dataItems, (level + 1), maxLevel, items[i].key)
        }
      }

      return items
    }
  }
}
</script>

<style lang="less" scoped>
:deep(.ant-tree).list-tree-view {
  overflow-y: hidden;
  margin-top: 10px;
}
:deep(.ant-tree).ant-tree-directory {
  li.ant-tree-treenode-selected {
    span.ant-tree-switcher {
      color: rgba(0, 0, 0, 0.65);
    }
    span.ant-tree-node-content-wrapper.ant-tree-node-selected > span {
      color: rgba(0, 0, 0, 0.65);
      background-color: #bae7ff;
    }
    span.ant-tree-node-content-wrapper::before {
      background: #ffffff;
    }
  }

  .ant-tree-child-tree {
    li.ant-tree-treenode-selected {
      span.ant-tree-switcher {
        color: rgba(0, 0, 0, 0.65);
      }
      span.ant-tree-node-content-wrapper::before {
        background: #ffffff;
      }
    }
  }
}

:deep(.ant-tree-show-line) .ant-tree-switcher.ant-tree-switcher-noop {
  display: none
}

:deep(.ant-tree-node-content-wrapper-open) > span:first-child,
:deep(.ant-tree-node-content-wrapper-close) > span:first-child {
  display: none;
}

:deep(.ant-tree-icon__customize) {
  padding-right: 5px;
  background: transparent;
}

:deep(.ant-tree) {
  .ant-tree-treenode {
    .ant-tree-node-content-wrapper {
      margin-left: 0;
      margin-bottom: 2px;

      &:before {
        border-left: 1px solid #d9d9d9;
        content: " ";
        height: 100%;
        height: calc(100% - 15px);
        left: 12px;
        margin: 22px 0 0;
        position: absolute;
        width: 1px;
        top: 0;
      }
    }

    &.ant-tree-treenode-switcher-close, &.ant-tree-treenode-switcher-open, &.ant-tree-treenode-leaf-last {
      .ant-tree-node-content-wrapper:before {
        display: none;
      }
    }
  }
}

:deep(.ant-tree-show-line) .ant-tree-indent-unit:before {
  bottom: -2px;
  top: -5px;
}
</style>
