import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useHistory } from 'react-router-dom'
import styled, { keyframes } from 'styled-components'
import dayjs from 'dayjs'

import { getCSData } from '../api'
import { cellGap, colors, levelColors, splitLineW, XTipH, YTipW } from '../style-vars'
import { useSearchStr } from '../useSearch'
import { FocusLS, LevelItem } from './common'
import { CustomerList } from './CustomerList'
import { TrackDocModal } from './TrackDocModal'
import { UnknownContent } from './UnknownContent'
import { XAxisTip } from './XAxisTip'
import { YAxisTip } from './YAxisTip'

interface DataSItems {
  s1?: LevelItem
  s2?: LevelItem
  s3?: LevelItem
  s4?: LevelItem
  s5?: LevelItem
  s6?: LevelItem
  [prop: string]: any
}

interface DataInUse {
  unknown: LevelItem[]
  l1: DataSItems
  l2: DataSItems
  l3: DataSItems
  l4: DataSItems
  l5: DataSItems
  [prop: string]: any
}

const levels = [1, 2, 3, 4, 5]
const stages = [1, 2, 3, 4, 5, 6]

const CSMapWrapper = styled.div`
  flex-grow: 1;
  width: 100%;
  box-sizing: border-box;
  padding: 18px 32px;
  overflow: hidden;
`

const Container = styled.div`
  width: 100%;
  height: 100%;
  position: relative;
`

interface DataCellProps {
  l: number
  s: number
  focusLS: FocusLS
  isEmpty: boolean
}
const DataCell = styled.div<DataCellProps>`
  position: absolute;
  border-radius: 3px;
  transition: all 0.24s cubic-bezier(0.19, 0.92, 0.32, 1.11);
  box-shadow: 0 0 12px 12px transparent;
  box-sizing: border-box;
  border: 1px solid transparent;
  ${({ l, s, focusLS, isEmpty }) => {
    let top = `${cellGap}px`
    let bottom = `${XTipH + cellGap}px`
    let left = `${cellGap + YTipW}px`
    let right = `${cellGap}px`
    let zIndex = 10
    let isFocus = true
    if (focusLS[1] !== s || focusLS[0] !== l) {
      isFocus = false
      zIndex = 4
      top = `calc((100% - ${XTipH}px - ${cellGap}px) / ${levels.length} * (${
        l - 1
      }) + ${cellGap}px)`
      bottom = `calc((100% - ${XTipH}px - ${cellGap}px) / ${levels.length} * ${
        levels.length - l
      } + ${XTipH + cellGap}px)`
      left = `calc((100% - ${YTipW}px - ${cellGap}px) / ${stages.length} * (${
        s - 1
      }) + ${cellGap}px + ${YTipW}px)`
      right = `calc((100% - ${YTipW}px - ${cellGap}px) / ${stages.length} * ${
        stages.length - s
      } + ${cellGap}px)`
    }
    const bgColor = isEmpty ? colors.bgEmpty : levelColors[`l${l}`][s]
    const hasOtherFocus = focusLS[0] !== -1 && focusLS[1] !== -1 && !isFocus
    return `
      z-index: ${zIndex};
      top: ${top};
      bottom: ${bottom};
      left: ${left};
      right: ${right};
      background-color: ${bgColor};

      ${
        isFocus
          ? ''
          : `
        &:hover {
          transform: ${isEmpty ? 'translateY(0)' : 'translateY(-6px)'};
          box-shadow: 0 0 6px 1px
            ${isEmpty ? 'transparent' : levelColors[`l${l}`][s - 1]};
          border-color: ${isEmpty ? 'transparent' : 'rgba(255, 255, 255, 0.4)'};
        }
      `
      }

      ${
        !hasOtherFocus
          ? ''
          : `
        .totalNum {
          visibility: hidden;
        }
          `
      }
    `
  }};

  .totalNum {
    display: flex;
    position: absolute;
    right: -8px;
    top: -10px;
    padding: 0 6px;
    height: 28px;
    min-width: 28px;
    border-radius: 28px;
    box-sizing: border-box;
    color: ${colors.fgMain};
    font-size: 18px;
    font-weight: 800;
    justify-content: center;
    align-items: center;
    color: ${colors.fgMain};
    background: ${colors.white};
    border: 2px solid ${({ l, s }) => `${levelColors[`l${l}`][s]}`};
  }
`

interface YCellProps {
  l: number
  focusLS: FocusLS
}
const YCell = styled.div<YCellProps>`
  position: absolute;
  left: 0;
  width: ${YTipW}px;
  border-radius: 3px;
  transition: all 0.24s cubic-bezier(0.19, 0.92, 0.32, 1.11);
  ${({ l, focusLS }) => {
    let top = `${cellGap}px`
    let bottom = `${XTipH + cellGap}px`
    let zIndex = 10
    if (focusLS[0] !== l) {
      zIndex = 4
      top = `calc((100% - ${XTipH}px - ${cellGap}px) / ${levels.length} * (${
        l - 1
      }) + ${cellGap}px)`
      bottom = `calc((100% - ${XTipH}px - ${cellGap}px) / ${levels.length} * ${
        levels.length - l
      } + ${XTipH + cellGap}px)`
    }
    return `
      z-index: ${zIndex};
      top: ${top};
      bottom: ${bottom};
      background-color: ${levelColors[`l${l}`][0]};
    `
  }};
`

interface XCellProps {
  s: number
  focusLS: FocusLS
}
const XCell = styled.div<XCellProps>`
  position: absolute;
  bottom: 0;
  height: ${XTipH}px;
  border-radius: 3px;
  transition: all 0.24s cubic-bezier(0.19, 0.92, 0.32, 1.11);
  background-color: ${colors.white};
  ${({ s, focusLS }) => {
    let left = `${cellGap + YTipW}px`
    let right = `${cellGap}px`
    let zIndex = 10
    if (focusLS[1] !== s) {
      zIndex = 4
      left = `calc((100% - ${YTipW}px - ${cellGap}px) / ${stages.length} * (${
        s - 1
      }) + ${cellGap}px + ${YTipW}px)`
      right = `calc((100% - ${YTipW}px - ${cellGap}px) / ${stages.length} * ${
        stages.length - s
      } + ${cellGap}px)`
    }
    return `
      z-index: ${zIndex};
      left: ${left};
      right: ${right};
    `
  }};
`

interface XSplitProps {
  s: number
}
const XSplitLine = styled.div<XSplitProps>`
  height: 60px;
  width: ${splitLineW}px;
  background-color: ${colors.line};
  border-radius: 2px;
  position: absolute;
  left: ${({ s }) =>
    `calc((100% - ${YTipW}px - ${cellGap}px) / ${stages.length} * (${s - 1}) + ${
      cellGap / 2
    }px + ${YTipW}px - ${splitLineW / 2}px)`};
  bottom: 0px;
`

interface YSplitProps {
  l: number
}
const YSplitLine = styled.div<YSplitProps>`
  width: 60px;
  height: ${splitLineW}px;
  background-color: ${colors.line};
  border-radius: 2px;
  position: absolute;
  left: 0;
  top: ${({ l }) =>
    `calc((100% - ${XTipH}px - ${cellGap}px) / ${levels.length} * (${l - 1}) + ${cellGap / 2}px - ${
      splitLineW / 2
    }px)`};
  z-index: 2;
  ${({ l }) => (l === 1 ? `display: none` : '')};
`

interface CornerCellProps {
  focusLS: FocusLS
}
const CornerCell = styled.div<CornerCellProps>`
  position: absolute;
  left: 0;
  bottom: 0;
  border-radius: 3px;
  background-color: ${colors.bgSecondary};
  transition: all 0.24s cubic-bezier(0.19, 0.92, 0.32, 1.11);
  overflow: hidden;
  cursor: default;
  ${({ focusLS }) => {
    let height = `${XTipH}px`
    let width = `${YTipW}px`
    let zIndex = 4
    if (focusLS[0] === 0 && focusLS[1] === 0) {
      height = '100%'
      width = '100%'
      zIndex = 10
    }
    return `
      z-index: ${zIndex};
      width: ${width};
      height: ${height};
    `
  }};
`

const delayCloseIcon = keyframes`
  from {
    transform: rotate(-90deg) scale(0);
  }

  to {
    transform: rotate(0deg) scale(1);
  }
`

const CloseIcon = styled.img`
  display: block;
  position: fixed;
  height: 32px;
  width: 32px;
  top: 48px;
  right: 48px;
  z-index: 300;
  transition: all 0.24s ease-in-out;
  cursor: pointer;
  transform: rotate(0);
  transform-origin: center;
  animation: ${delayCloseIcon} 0.16s ease-out forwards;

  &:hover {
    transform: rotate(180deg);
  }
`

const getStageStartSec = (stage: any, startSec: number, levels: string[], slf: any): number => {
  const lastTimeUnix = (startSec - 1).toString()
  const reg = new RegExp(`\\[(\\d+),${lastTimeUnix}\\]`)
  for (let i = 0; i < levels.length; i += 1) {
    const lv = levels[i]
    const slvKey = `S${stage} - L${lv}`
    const slStr = slf?.[slvKey]
    if (slStr) {
      const matches = reg.exec(slStr)
      if (matches) {
        const prevStartSec = Number(matches[1])
        const needCheckLevels = levels.filter((it) => it !== lv.toString())
        return getStageStartSec(stage, prevStartSec, needCheckLevels, slf)
      }
    }
  }
  return startSec
}

const convertData2ViewData = (data: any): DataInUse => {
  if (data && data.length) {
    const firstItem = data[0].fields
    const res: DataInUse = {
      unknown: [],
      l1: {},
      l2: {},
      l3: {},
      l4: {},
      l5: {},
    }
    const colNames = Object.keys(firstItem)
    const customerBO = colNames.find((cn) => cn.includes('Customer Project/App - 客户业务'))
    const customerSS = colNames.find((cn) => cn.includes('Success Stage'))
    const customerRL = colNames.find((cn) => cn.includes('Relationship Level'))
    const customerBT = colNames.find((cn) => cn.includes('Business Tag')) || ''
    const customerPM = colNames.find((cn) => cn.includes('Product Manager - "主治医师"')) || ''
    const customerBHR =
      colNames.find((cn) => cn.includes('Business Health Record - 业务健康档案（URL）')) || ''
    if (!customerBO || !customerSS || !customerRL) {
      throw new Error('ColName has changed!')
    }
    data.forEach((it: any) => {
      const r = it.fields
      const slf = it?.slFields?.fields
      const name = r?.[customerBO]?.[0]?.text || 'unknow'
      const stage = r?.[customerSS]?.replace(/^\s*S(\d)[:：].*$/g, '$1')
      const level = r?.[customerRL]?.replace(/^\s*L(\d)[:：].*$/g, '$1')
      const item: any = {
        name,
        stage,
        level,
        bt: r?.[customerBT]?.[0] || '',
        bhr: r?.[customerBHR]?.link || '',
        rid: it?.recordId || '',
        pm: r?.[customerPM]?.map((it: any) => it.name)?.join(','),
        firstPMLarkLink: r?.[customerPM]?.[0]?.id
          ? `https://applink.feishu.cn/client/chat/open?openId=${r?.[customerPM]?.[0]?.id}`
          : '',
      }
      if (stage && level && slf && slf[`S${stage} - L${level}`]) {
        try {
          const slTimeJson = JSON.parse(slf[`S${stage} - L${level}`]) as number[][]
          const startSec = slTimeJson?.[slTimeJson.length - 1]?.[0]
          if (startSec) {
            const needCheckLevels = ['1', '2', '3', '4', '5'].filter(
              (it) => it !== level.toString(),
            )
            const stageStartSec = getStageStartSec(stage, startSec, needCheckLevels, slf)

            const nowSec = dayjs().unix()
            item.curStaySec = nowSec - stageStartSec
          }
        } catch {
          // ignore
        }
      }
      if (!stage || !level) {
        res.unknown.push(item)
      } else {
        if (!res[`l${level}`][`s${stage}`]) {
          res[`l${level}`][`s${stage}`] = []
        }
        res[`l${level}`][`s${stage}`].push(item)
      }
    })

    return res
  }
  return {
    unknown: [],
    l1: {},
    l2: {},
    l3: {},
    l4: {},
    l5: {},
  }
}

export const CSMap: FC<{ viewId: string }> = ({ viewId }) => {
  const his = useHistory()
  const [durl, drecord] = useSearchStr(['durl', 'drecord'])
  // [level, stage], [-1, -1] for no focus, [0, 0] for data["unknown"] focus
  const [focusLS, setFocusLS] = useState<FocusLS>([-1, -1])
  const [dataMap, setDataMap] = useState<any>({})
  const d = useMemo<DataInUse>(() => {
    const data = dataMap[viewId]
    return convertData2ViewData(data)
  }, [dataMap, viewId])
  const curViewId = useRef(viewId)

  const getData = useCallback(async () => {
    const curVId = curViewId.current
    const data = await getCSData(curViewId.current)
    if (!data) {
      return
    }
    setDataMap((pre: any) => ({
      ...pre,
      [curVId]: [...data],
    }))
  }, [])

  useEffect(() => {
    curViewId.current = viewId
    getData()
  }, [viewId]) // eslint-disable-line

  useEffect(() => {
    let tokenTimer: number
    let dataTimer: number
    ;(async () => {
      const curVId = curViewId.current
      const data = await getCSData(curViewId.current)
      if (!data) {
        return
      }
      setDataMap((pre: any) => ({
        ...pre,
        [curVId]: [...data],
      }))

      dataTimer = window.setInterval(async () => {
        const curVId = curViewId.current
        const data = await getCSData(curViewId.current)
        if (!data) {
          return
        }
        setDataMap((pre: any) => ({
          ...pre,
          [curVId]: [...data],
        }))
      }, 1000 * 60 * 10)
    })()

    return () => {
      clearInterval(tokenTimer)
      clearInterval(dataTimer)
    }
  }, []) // eslint-disable-line

  const zoomIn = useCallback(
    (l: number, s: number) => {
      ;(window as any).getSelection().removeAllRanges()
      if (focusLS[0] === l && focusLS[1] === s) {
        setFocusLS([-1, -1])
      } else {
        const canZoomIn =
          (l === 0 && s === 0 && d.unknown.length) ||
          (d[`l${l}`] && d[`l${l}`][`s${s}`] && d[`l${l}`][`s${s}`].length)
        canZoomIn && setFocusLS([l, s])
      }
    },
    [focusLS, d],
  )

  const [trackModalState, setTrackModalState] = useState({
    isOpen: !!durl,
  })
  const openTrackModal = useCallback(
    (url: string, recordId: string) => {
      const durlSeg = url ? `&durl=${encodeURIComponent(url)}` : ''
      setTrackModalState({
        isOpen: true,
      })
      his.push({
        pathname: his.location.pathname,
        search: `?vid=${viewId}${durlSeg}&drecord=${recordId}`,
      })
    },
    [viewId, his],
  )
  const closeModal = useCallback(() => {
    setTrackModalState({
      isOpen: false,
    })
    his.push({
      pathname: his.location.pathname,
      search: `?vid=${viewId}`,
    })
  }, [viewId, his])
  const changeQueryUrl = useCallback(
    (nurl: string, recordId: string) => {
      his.push({
        pathname: his.location.pathname,
        search: `?vid=${viewId}&durl=${encodeURIComponent(nurl)}&drecord=${recordId}`,
      })
    },
    [his, viewId],
  )

  return (
    <>
      <CSMapWrapper>
        <Container>
          {levels.map((l) => (
            <YCell key={l} focusLS={focusLS} l={l}>
              <YAxisTip l={l} />
            </YCell>
          ))}
          {levels.map((l) => (
            <YSplitLine key={l} l={l} />
          ))}
          <YSplitLine l={levels.length + 1} />
          {stages.map((s) => (
            <XCell key={s} focusLS={focusLS} s={s}>
              <XAxisTip s={s} />
            </XCell>
          ))}
          {stages.map((s) => (
            <XSplitLine key={s} s={s} />
          ))}
          <CornerCell focusLS={focusLS} onDoubleClick={() => zoomIn(0, 0)}>
            <UnknownContent focusLS={focusLS} unknowns={d.unknown} onTrackOpen={openTrackModal} />
          </CornerCell>
          {levels.map((l) =>
            stages.map((s) => (
              <DataCell
                key={`${l}-${s}`}
                l={l}
                s={s}
                focusLS={focusLS}
                isEmpty={!d[`l${l}`][`s${s}`] || !d[`l${l}`][`s${s}`].length}
                onDoubleClick={() => zoomIn(l, s)}
              >
                {d[`l${l}`][`s${s}`] && d[`l${l}`][`s${s}`].length ? (
                  <>
                    <div className="totalNum" onClick={() => zoomIn(l, s)}>
                      {d[`l${l}`][`s${s}`].length}
                    </div>
                    <CustomerList
                      list={d[`l${l}`][`s${s}`]}
                      l={l}
                      s={s}
                      focusLS={focusLS}
                      onTrackOpen={openTrackModal}
                    />
                  </>
                ) : null}
              </DataCell>
            )),
          )}
        </Container>
      </CSMapWrapper>

      {trackModalState.isOpen && (
        <CloseIcon
          src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cg fill='%23ffffff'%3E%3Cpath d='M16,0A16,16,0,1,0,32,16,16.047,16.047,0,0,0,16,0Zm8.121,22L22,24.121l-6-6-6,6L7.879,22l6-6-6-6L10,7.879l6,6,6-6L24.121,10l-6,6Z' fill='%23ffffff'%3E%3C/path%3E%3C/g%3E%3C/svg%3E"
          onClick={closeModal}
        />
      )}
      <TrackDocModal
        isOpen={trackModalState.isOpen}
        url={durl}
        recordId={drecord}
        onRequestClose={closeModal}
        onUrlChange={changeQueryUrl}
      />
    </>
  )
}
