<script>
import Icon from "../icon"
import TabNav from "./tab-nav"
import config from "../../managers/config"

config.defaults({
  "Tabs.type": "",
  "Tabs.closable": false,
  "Tabs.addable": false,
  "Tabs.editable": false,
  "Tabs.tabPosition": "top",
  "Tabs.keepTabsAlive": true,
  "Tabs.align": "left",
  "Tabs.queryKey": "mm_tab_active",
})

export default {
  name: "mm-tabs",
  components: {
    Icon,
    TabNav,
  },
  model: {
    prop: "value",
    event: "change",
  },
  props: {
    type: {
      type: String,
      default: () => config.get("Tabs.type"),
      validator(value) {
        return ["", "card", "border-card"].includes(value)
      },
    },
    activeName: String,
    closable: {
      type: Boolean,
      default: () => config.get("Tabs.closable"),
    },
    addable: {
      type: Boolean,
      default: () => config.get("Tabs.addable"),
    },
    value: {
      type: null,
    },
    editable: {
      type: Boolean,
      default: () => config.get("Tabs.editable"),
    },
    tabPosition: {
      type: String,
      default: () => config.get("Tabs.tabPosition"),
    },
    keepTabsAlive: {
      type: Boolean,
      default: () => config.get("Tabs.keepTabsAlive"),
    },
    align: {
      type: String,
      default: () => config.get("Tabs.align"),
      validator(val) {
        return ["left", "right", "center"].includes(val)
      },
    },
    router: {
      type: Boolean,
      default: false,
    },
    query: {
      type: Boolean,
      default: false,
    },
    queryKey: {
      type: String,
      default: () => config.get("Tabs.queryKey"),
    },
  },
  provide() {
    return {
      "mm-tabs": this,
    }
  },
  data() {
    return {
      currentName: this.value || this.activeName,
      panes: [],
    }
  },
  watch: {
    activeName(val) {
      this.currentName = val
    },
    value(val) {
      this.currentName = val
    },
    currentName() {
      if (this.$refs.nav) {
        this.$nextTick(_ => {
          this.$refs.nav.scrollToActiveTab()
        })
      }
    },
  },
  updated() {
    this.calcPaneInstances(false)
  },
  created() {
    if (!this.currentName) {
      this.setCurrentName("0")
    }

    const {
      query,
      router,
      $route,
      $router,
    } = this

    if ((query || router) && !($route && $router)) {
      throw new Error("\"Query\" and \"router\" modes can only be used when using \"vue-router\"")
    }

    this.$on("tab-nav-update", this.calcPaneInstances.bind(null, true))
  },
  methods: {
    handleTabClick(tab, tabName, event) {
      if (tab.disabled) {
        return
      }
      this.setCurrentName(tabName)
      this.$emit("tab-click", tab, event)
      this.handleRouterAndQuery(tab, tabName)
    },
    handleTabRemove(pane, ev) {
      if (pane.disabled) {
        return
      }
      ev.stopPropagation()
      this.$emit("edit", pane.name, "remove")
      this.$emit("tab-remove", pane.name)
    },
    handleTabAdd() {
      this.$emit("edit", null, "add")
      this.$emit("tab-add")
    },
    handleRouterAndQuery(tab, tabName) {
      const {
        router,
        query,
        queryKey,
        routeToItem,
        $route,
      } = this

      let routerObj = null
      if (router) {
        routerObj = { ...tab.paneResolvedPath }
      } else if (query) {
        routerObj = {
          query: {
            ...$route.query,
            [queryKey]: tabName,
          },
        }
      }
      if (routerObj) {
        routeToItem(routerObj)
      }
    },
    routeToItem(route) {
      try {
        this.$router.push(route, () => {}, error => {
          if (error) {
            console.error(error)
          }
        })
      } catch (e) {
        console.error(e)
      }
    },
    setCurrentName(value) {
      this.currentName = value
      this.$emit("change", value)
    },
    addPanes(item) {
      this.panes.push(item)
      this.panes.forEach((pane, index) => {
        pane.index = `${index}`
      })
    },
    removePanes(item) {
      const panes = this.panes
      const index = panes.indexOf(item)
      if (index > -1) {
        panes.splice(index, 1)
      }
    },
    calcPaneInstances(isForceUpdate = false) {
      if (this.$slots.default) {
        const paneSlots = this.$slots.default.filter(vnode => vnode.tag &&
            vnode.componentOptions && vnode.componentOptions.Ctor.options.name === "mm-tab")
        const panes = paneSlots.map(({ componentInstance }) => componentInstance)
        const panesChanged = !(panes.length === this.panes.length && panes.every((pane, index) => pane === this.panes[index]))
        if (isForceUpdate || panesChanged) {
          this.panes = panes
        }
      } else if (this.panes.length !== 0) {
        this.panes = []
      }
    },
  },
  render(h) {
    let {
      type,
      handleTabClick,
      handleTabRemove,
      handleTabAdd,
      currentName,
      panes,
      editable,
      addable,
      tabPosition,
      align,
    } = this
    const newButton =
      editable || addable ? (
        <icon
          tabindex="0"
          class="mm-tabs-new-tab"
          name="add"
          size="0.75rem"
          fill="#909399"
          on-click={handleTabAdd}
        />
      ) : null
    const navData = {
      props: {
        currentName,
        onTabClick: handleTabClick,
        onTabRemove: handleTabRemove,
        editable,
        type,
        panes,
      },
      ref: "nav",
    }
    const header = (
      <div class={["mm-tabs-header", `mm-tabs-header__${tabPosition}`]}>
        {newButton}
        <tab-nav {...navData} />
      </div>
    )
    const panels = <div class="mm-tabs-content">{this.$slots.default}</div>
    return (
      <div
        class={{
          "mm-tabs": true,
          "mm-tabs__card": type === "card",
          [`mm-tabs__${tabPosition}`]: true,
          "mm-tabs__border-card": type === "border-card",
          [`mm-tabs__align-${align}`]: align,
        }}
      >
        {tabPosition !== "bottom" ? [header, panels] : [panels, header]}
      </div>
    )
  },
}
</script>
