/*
 * 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.
 */

import type { Ref } from 'vue'
import { onMounted, ref } from 'vue'
import type { Node, Graph, Edge, Cell } from '@antv/x6'
import _ from 'lodash'
import {
  X6_PORT_OUT_NAME,
  PORT_HOVER,
  PORT_SELECTED,
  PORT,
  NODE,
  NODE_HOVER,
  NODE_SELECTED,
  EDGE,
  EDGE_SELECTED,
  EDGE_HOVER
} from './dag-config'

interface Options {
  graph: Ref<Graph | undefined>
}

/**
 * Change the style on cell hover and select
 */
export function useCellActive(options: Options) {
  const { graph } = options
  const hoverCell = ref()

  const isStatusIcon = (tagName: string) => {
    if (!tagName) return false
    return (
      tagName.toLocaleLowerCase() === 'em' ||
      tagName.toLocaleLowerCase() === 'body'
    )
  }

  function setEdgeStyle(edge: Edge) {
    const isHover = edge === hoverCell.value
    const isSelected = graph.value?.isSelected(edge)
    let edgeProps = null

    if (isHover) {
      edgeProps = _.merge(_.cloneDeep(EDGE), EDGE_HOVER)
    } else if (isSelected) {
      edgeProps = _.merge(_.cloneDeep(EDGE), EDGE_SELECTED)
    } else {
      edgeProps = _.cloneDeep(EDGE)
    }

    edge.setAttrs(edgeProps.attrs)
    edge.setLabels([
      {
        ..._.merge({
          attrs: _.cloneDeep(edgeProps.defaultLabel.attrs)
        })
      }
    ])
  }

  function setNodeStyle(node: Node) {
    const isHover = node === hoverCell.value
    const isSelected = graph.value?.isSelected(node)
    const portHover = _.cloneDeep(PORT_HOVER.groups[X6_PORT_OUT_NAME].attrs)
    const portSelected = _.cloneDeep(
      PORT_SELECTED.groups[X6_PORT_OUT_NAME].attrs
    )
    const portDefault = _.cloneDeep(PORT.groups[X6_PORT_OUT_NAME].attrs)
    const nodeHover = _.merge(_.cloneDeep(NODE.attrs), NODE_HOVER.attrs)
    const nodeSelected = _.merge(_.cloneDeep(NODE.attrs), NODE_SELECTED.attrs)

    let img = null
    let nodeAttrs = null
    let portAttrs = null

    if (isHover || isSelected) {
      img = `${import.meta.env.BASE_URL}images/task-icons/${(node.data
        .taskType !== 'FLINK_STREAM'
        ? node.data.taskType
        : 'FLINK'
      ).toLocaleLowerCase()}_hover.png`
      if (isHover) {
        nodeAttrs = nodeHover
        portAttrs = _.merge(portDefault, portHover)
      } else {
        nodeAttrs = nodeSelected
        portAttrs = _.merge(portDefault, portSelected)
      }
    } else {
      img = `${import.meta.env.BASE_URL}images/task-icons/${(node.data
        .taskType !== 'FLINK_STREAM'
        ? node.data.taskType
        : 'FLINK'
      ).toLocaleLowerCase()}.png`
      nodeAttrs = NODE.attrs
      portAttrs = portDefault
    }
    node.setAttrByPath('image/xlink:href', img)
    node.setAttrs(nodeAttrs)
    node.setPortProp(X6_PORT_OUT_NAME, 'attrs', portAttrs)
  }

  function updateCellStyle(cell: Cell) {
    if (cell.isEdge()) {
      setEdgeStyle(cell)
    } else if (cell.isNode()) {
      setNodeStyle(cell)
    }
  }

  onMounted(() => {
    if (graph.value) {
      // hover
      graph.value.on('cell:mouseenter', (data) => {
        const { cell, e } = data
        if (!isStatusIcon(e.target.tagName)) {
          hoverCell.value = cell
          updateCellStyle(cell)
        }
      })
      graph.value.on('cell:mouseleave', ({ cell }) => {
        hoverCell.value = undefined
        updateCellStyle(cell)
      })

      // select
      graph.value.on('cell:selected', ({ cell }) => {
        updateCellStyle(cell)
      })
      graph.value.on('cell:unselected', ({ cell }) => {
        updateCellStyle(cell)
      })
    }
  })

  return {
    hoverCell
  }
}
