import { Graph, Shape } from '@antv/x6'
import { Selection } from '@antv/x6-plugin-selection'
import { Scroller } from '@antv/x6-plugin-scroller'
import { Keyboard } from '@antv/x6-plugin-keyboard'
import type { PortManager } from '@antv/x6/lib/model/port'
import { customAlphabet } from 'nanoid'

import { Avatar, Share, Finished, CopyDocument } from '@cacp/svg-icons'
import type { FlowDescriptor, NodeDescriptor, ProcessDescriptor } from '@cacp/ui'

export const colors = {
  flowColor: '#A2B1C3',
  nodeColor: '#262626',
  nodeFill: '#E7F7FE',
  nodeStroke: '#69C0FF',
  portStroke: '#5F95FF',
  portFill: '#FFF'
}
const ports: PortManager.Metadata = {
  groups: {
    top: {
      position: 'top',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: colors.portStroke,
          strokeWidth: 1,
          fill: colors.portFill,
          style: {
            visiblility: 'hidden'
          }
        }
      }
    },
    right: {
      position: 'right',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: colors.portStroke,
          strokeWidth: 1,
          fill: colors.portFill,
          style: {
            visiblility: 'hidden'
          }
        }
      }
    },
    bottom: {
      position: 'bottom',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: colors.portStroke,
          strokeWidth: 1,
          fill: colors.portFill,
          style: {
            visiblility: 'hidden'
          }
        }
      }
    },
    left: {
      position: 'left',
      attrs: {
        circle: {
          r: 4,
          magnet: true,
          stroke: colors.portStroke,
          strokeWidth: 1,
          fill: colors.portFill,
          style: {
            visiblility: 'hidden'
          }
        }
      }
    }
  },
  items: [
    {
      group: 'top'
    },
    {
      group: 'right'
    },
    {
      group: 'bottom'
    },
    {
      group: 'left'
    }
  ]
}

function registerGraph() {
  Graph.registerNode(
    'START',
    {
      inherit: 'circle',
      x: 0,
      y: 0,
      width: 60,
      height: 60,
      attrs: {
        body: {
          strokeWidth: 1,
          fill: '#FEF7E7',
          stroke: '#FFC069'
        },
        label: {
          fill: colors.nodeColor,
          fontSize: 12
        }
      },
      ports: ports
    },
    true
  )

  Graph.registerNode(
    'END',
    {
      inherit: 'circle',
      x: 0,
      y: 0,
      width: 60,
      height: 60,
      attrs: {
        body: {
          strokeWidth: 1,
          fill: '#EBF2FF',
          stroke: '#BFCFEE'
        },
        label: {
          fill: colors.nodeColor,
          fontSize: 12
        }
      },
      ports: ports
    },
    true
  )

  Graph.registerNode(
    'APPROVE',
    {
      inherit: 'rect',
      x: 0,
      y: 0,
      width: 100,
      height: 60,
      markup: [
        {
          tagName: 'rect',
          selector: 'body'
        },
        {
          tagName: 'image',
          selector: 'img'
        },
        {
          tagName: 'text',
          selector: 'label'
        }
      ],
      attrs: {
        body: {
          rx: 6,
          ry: 6,
          strokeWidth: 1,
          fill: colors.nodeFill,
          stroke: colors.nodeStroke
        },
        img: {
          x: 6,
          y: 6,
          width: 16,
          height: 16,
          'xlink:href': Avatar
        },
        label: {
          fill: colors.nodeColor,
          fontSize: 12
        }
      },
      ports: ports
    },
    true
  )

  Graph.registerNode(
    'BRANCH',
    {
      inherit: 'rect',
      x: 0,
      y: 0,
      width: 100,
      height: 60,
      markup: [
        {
          tagName: 'rect',
          selector: 'body'
        },
        {
          tagName: 'image',
          selector: 'img'
        },
        {
          tagName: 'text',
          selector: 'label'
        }
      ],
      attrs: {
        body: {
          rx: 6,
          ry: 6,
          strokeWidth: 1,
          fill: colors.nodeFill,
          stroke: colors.nodeStroke
        },
        img: {
          x: 6,
          y: 6,
          width: 16,
          height: 16,
          'xlink:href': Share
        },
        label: {
          fill: colors.nodeColor,
          fontSize: 12
        }
      },
      ports: ports
    },
    true
  )

  Graph.registerNode(
    'VIRTUAL',
    {
      inherit: 'rect',
      x: 0,
      y: 0,
      width: 100,
      height: 60,
      markup: [
        {
          tagName: 'rect',
          selector: 'body'
        },
        {
          tagName: 'image',
          selector: 'img'
        },
        {
          tagName: 'text',
          selector: 'label'
        }
      ],
      attrs: {
        body: {
          rx: 6,
          ry: 6,
          strokeWidth: 1,
          fill: colors.nodeFill,
          stroke: colors.nodeStroke
        },
        img: {
          x: 6,
          y: 6,
          width: 16,
          height: 16,
          'xlink:href': Finished
        },
        label: {
          fill: colors.nodeColor,
          fontSize: 12
        }
      },
      ports: ports
    },
    true
  )

  Graph.registerNode(
    'COPY',
    {
      inherit: 'rect',
      x: 0,
      y: 0,
      width: 100,
      height: 60,
      markup: [
        {
          tagName: 'rect',
          selector: 'body'
        },
        {
          tagName: 'image',
          selector: 'img'
        },
        {
          tagName: 'text',
          selector: 'label'
        }
      ],
      attrs: {
        body: {
          rx: 6,
          ry: 6,
          strokeWidth: 1,
          fill: colors.nodeFill,
          stroke: colors.nodeStroke
        },
        img: {
          x: 6,
          y: 6,
          width: 16,
          height: 16,
          'xlink:href': CopyDocument
        },
        label: {
          fill: colors.nodeColor,
          fontSize: 12
        }
      },
      ports: ports
    },
    true
  )

  Graph.registerEdge(
    'FLOW',
    {
      inherit: 'edge',
      attrs: {
        line: {
          stroke: colors.flowColor,
          strokeWidth: 2
        }
      },
      label: {
        attrs: {
          label: {
            fill: colors.flowColor,
            fontSize: 12
          }
        }
      },
      zIndex: 0
    },
    true
  )
}

export function nanoId() {
  return customAlphabet('123456789abcdefghjkmnpqrstuvwxyz', 20)()
}

export function initGraph(container: HTMLDivElement, postGraphInit: (graph: Graph) => void): Graph {
  const graph: Graph = new Graph({
    container: container,
    grid: {
      size: 10,
      visible: true
    },
    panning: {
      enabled: true
    },
    mousewheel: {
      enabled: true,
      zoomAtMousePosition: true,
      modifiers: 'ctrl',
      minScale: 0.5,
      maxScale: 1.2
    },
    autoResize: true,
    highlighting: {
      magnetAdsorbed: {
        name: 'stroke',
        args: {
          attrs: {
            fill: '#5F95FF',
            stroke: '#5F95FF'
          }
        }
      }
    },
    connecting: {
      router: 'orth',
      snap: {
        radius: 20
      },
      allowBlank: false,
      allowLoop: false,
      allowNode: false,
      allowEdge: false,
      allowPort: true,
      allowMulti: false,
      connector: {
        name: 'rounded',
        args: {
          radius: 8
        }
      },
      anchor: 'center',
      createEdge() {
        const id = `f_${nanoId()}`
        return new Shape.Edge({
          id: id,
          shape: 'FLOW',
          label: '',
          attrs: {
            line: {
              stroke: colors.flowColor,
              strokeWidth: 2,
              targetMarker: {
                name: 'block',
                width: 12,
                height: 8
              }
            }
          },
          data: {
            type: 'NORMAL',
            code: id,
            name: '',
            source: null,
            target: null
          }
        })
      }
    }
  })

  graph
    .use(new Selection({ enabled: true, multiple: false }))
    .use(new Scroller({ pannable: true, enabled: true }))
    .use(new Keyboard({ enabled: true }))

  graph.bindKey('delete', () => {
    const cells = graph.getSelectedCells()
    if (cells) {
      const cell = cells[0]
      if (cell.shape === 'START' || cell.shape === 'END') {
        return
      }
      cell.remove()
    }

    return false
  })

  registerGraph()
  postGraphInit(graph)
  graph.centerContent()

  return graph
}

export function isProcessDescriptor(data: ProcessDescriptor | NodeDescriptor | FlowDescriptor): data is ProcessDescriptor {
  return typeof (data as ProcessDescriptor)['branch'] !== undefined
}

export function isNodeDescriptor(data: ProcessDescriptor | NodeDescriptor | FlowDescriptor): data is NodeDescriptor {
  return typeof (data as NodeDescriptor)['attrs'] !== undefined
}

export function isFlowDescriptor(data: ProcessDescriptor | NodeDescriptor | FlowDescriptor): data is FlowDescriptor {
  return typeof (data as FlowDescriptor)['source'] !== undefined
}
