import React, { useEffect, useMemo, useState } from 'react'

import {
  ArrowLeftOutlined,
  ArrowRightOutlined,
  QuestionCircleOutlined
} from '@ant-design/icons'
import { Card } from '@mantine/core'
import { useDebounceFn } from 'ahooks'
import { Popover, Tag, Tooltip, Typography, Carousel } from 'antd'
import dayjs from 'dayjs'
import { useParams } from 'react-router-dom'
import useSWR from 'swr'

import {
  ClusterCluster,
  ClusterDataInfo,
  ClusterSloganResp
} from '@clinic/client'
import { getClusterInfo, getClusterDataInfo, getClusterSlogan } from '~/api'
import DatePicker from '~/components/DatePicker'
import { EmptyBoxIcon } from '~/components/Icons'
import { useQueryParams } from '~/hooks/use-query-params'
import { ReportTypeName } from '~/pages/reports/entry/useReportType'
import { ReportType } from '~/pages/reports/shared/types'
import { clusterDetailTelemetry } from '~/telemetry'
import { getStatusColor } from '~/utils/cluster-status'
import { formatDateTime } from '~/utils/date-time'
import { CloudUIProvider, OnPremUIProvider } from '~/utils/tenant-type'

import {
  OPMetricsModal,
  InstanceLogsModal,
  SlowQueryModal,
  DownloadModal
} from './cards'
import { HealthReportLink } from './cards/HealthReportLink'
import { DetailItemCard } from './components/DetailItemCard'
import TimeWindowSelect, { TimeRange } from './components/TimeWindowSelect'

const { Text, Title } = Typography

const ONE_DAY_SECONDS = 24 * 60 * 60

const defQueryParams = {
  from: 0,
  to: 0
}

const Page: React.FC = () => {
  const { orgId, cid } = useParams<'orgId' | 'cid'>()

  const { data: clusterInfoRes } = useSWR(
    `/orgs/${orgId}/clusters/${cid}`,
    () => getClusterInfo(orgId!, cid!),
    { refreshInterval: 10 * 1000 }
  )

  // 设置页面 title
  useEffect(() => {
    if (clusterInfoRes?.data?.name) {
      document.title = `TiDB Clinic - ${clusterInfoRes.data.name}`
    }
  }, [clusterInfoRes?.data?.name])

  const statusColor = useMemo(
    () => getStatusColor(clusterInfoRes?.data.status),
    [clusterInfoRes?.data.status]
  )

  const { queryParams, setQueryParams } = useQueryParams(
    defQueryParams,
    undefined,
    { replace: true }
  )
  const [selectedTimeWindow, setSelectedTimeWindow] =
    useState<TimeRange | null>(() => {
      if (queryParams.from === 0) {
        return null
      }
      return { start: queryParams.from, end: queryParams.to }
    })
  // use debounce to improve performance
  // reduce lag when dragging time window
  const { run: debounceChangeTimeWindow } = useDebounceFn(changeTimeWindow, {
    wait: 500
  })
  function changeTimeWindow(t: TimeRange, moveTimeline: boolean = false) {
    setQueryParams({ from: t.start, to: t.end })
    setSelectedTimeWindow(t)
    clusterDetailTelemetry.selectTimeRange(t)
    if (moveTimeline) {
      setDay(dayjs.unix(t.end).startOf('d').unix())
    }
  }

  const isDM = clusterInfoRes?.data.clusterType?.startsWith('dm')
  const topology = useMemo(() => {
    const topo = clusterInfoRes?.data.topology ?? {}
    return Object.entries(topo)
      .filter(([k, _]) => {
        if (
          !isDM &&
          (k === 'dmMaster' || k === 'dmWorker' || k === 'dmWoker')
        ) {
          return false
        }
        if (isDM && k !== 'dmMaster' && k !== 'dmWorker' && k !== 'dmWoker') {
          return false
        }
        return true
      })
      .map(([k, v]) => `${k}-${v}`)
      .join(' ')
  }, [clusterInfoRes, isDM])

  const [day, setDay] = useState<number | undefined>(undefined)

  function preDay() {
    setDay(day! - ONE_DAY_SECONDS)
  }
  function nextDay() {
    setDay(day! + ONE_DAY_SECONDS)
  }

  const { data: clusterAllDataInfoRes } = useSWR(
    `/orgs/${orgId}/clusters/${cid}/data`,
    () => getClusterDataInfo(orgId!, cid!)
  )

  const { data: clusterSloganRes } = useSWR(
    `/orgs/${orgId}/clusters/${cid}/slogans`,
    () => getClusterSlogan(orgId!, cid!, 5, 1)
  )

  const { data: clusterDataInfoRes } = useSWR(
    day ? `/orgs/${orgId}/clusters/${cid}/data/${day}` : null,
    () => {
      // TODO: avoid call it too frequently,
      // we can prefetch some extra data,
      // so if day changes and day doesn't beyond the prefetched range,
      // then we can avoid request.
      const startTime = day!
      const endTime = day! + 3 * ONE_DAY_SECONDS

      let needReq = true
      if (clusterInfoRes) {
        needReq = !(
          startTime > clusterInfoRes.data.endTime! ||
          endTime < clusterInfoRes.data.startTime!
        )
      }

      if (needReq) {
        return getClusterDataInfo(orgId!, cid!, startTime, endTime)
      }
    }
  )

  useEffect(() => {
    if (day) {
      return
    }
    if (selectedTimeWindow) {
      setDay(dayjs.unix(selectedTimeWindow.end).startOf('d').unix())
      return
    }
    if (clusterInfoRes) {
      setDay(dayjs.unix(clusterInfoRes.data.endTime!).startOf('d').unix())
    }
  }, [day, selectedTimeWindow, clusterInfoRes])

  const timeWindowStr = useMemo(() => {
    if (selectedTimeWindow) {
      const { start, end } = selectedTimeWindow
      return `Cluster Data from ${formatDateTime(start)} to ${formatDateTime(
        end
      )}`
    }
    return ''
  }, [selectedTimeWindow])

  interface TimeSpan {
    filename: string
    collectors: string
    start: number
    end: number
  }

  const [timeSpansCache, setTimeSpansCache] = useState<
    Record<string, TimeSpan>
  >({})

  useEffect(() => {
    setTimeSpansCache((pre) => {
      const newCache = { ...pre }
      const items = (clusterAllDataInfoRes?.data.dataInfos || []).concat(
        clusterDataInfoRes?.data.dataInfos || []
      )
      items.forEach((item) => {
        if (!newCache[item.itemID!]) {
          newCache[item.itemID!] = {
            filename: item.filename!,
            collectors:
              Object.entries({
                Config: item.haveConfig,
                Log: item.haveLog,
                Metric: item.haveMetric
              })
                .filter(([k, v]) => v)
                .map(([k]) => k)
                .join('/') || 'Unknown',
            start: item.startTime!,
            end: item.endTime!
          }
        }
      })
      return newCache
    })
  }, [clusterDataInfoRes, clusterAllDataInfoRes])
  const reverseOrderTimeSpans = useMemo(
    () => Object.values(timeSpansCache).sort((a, b) => b.end - a.end),
    [timeSpansCache]
  )

  const timeSpans = useMemo(
    () => Object.values(timeSpansCache),
    [timeSpansCache]
  )

  const hasSelectTimeSpan = useMemo(() => {
    if (!selectedTimeWindow) {
      return false
    }
    return (
      timeSpans.find(
        (r: any) =>
          selectedTimeWindow.end > r.start && selectedTimeWindow.start < r.end
      ) !== undefined
    )
  }, [selectedTimeWindow, timeSpans])

  const { run: debounceChangeTimeline } = useDebounceFn(
    (t: TimeRange) => {
      setDay(t.start)
    },
    { wait: 500 }
  )

  const canJumpPreTimeSpan = useMemo(() => {
    if (clusterInfoRes && selectedTimeWindow) {
      return selectedTimeWindow.start > clusterInfoRes.data.startTime!
    }
    return false
  }, [clusterInfoRes, selectedTimeWindow])

  const canJumpNextTimeSpan = useMemo(() => {
    if (clusterInfoRes && selectedTimeWindow) {
      return selectedTimeWindow.end < clusterInfoRes.data.endTime!
    }
    return false
  }, [clusterInfoRes, selectedTimeWindow])

  function jumpPreTimeSpan(event: any) {
    event.preventDefault() // prevent selection

    for (let i = 0; i < reverseOrderTimeSpans.length; i++) {
      if (reverseOrderTimeSpans[i].start < selectedTimeWindow!.start) {
        changeTimeWindow(reverseOrderTimeSpans[i], true)
        break
      }
    }
  }

  function jumpNextTimeSpan(event: any) {
    event.preventDefault() // prevent selection

    for (let i = reverseOrderTimeSpans.length - 1; i >= 0; i--) {
      if (reverseOrderTimeSpans[i].end > selectedTimeWindow!.end) {
        changeTimeWindow(reverseOrderTimeSpans[i], true)
        break
      }
    }
  }

  const TimeSpanList = (
    <div className='flex flex-col items-center'>
      {reverseOrderTimeSpans.map((span, idx) => (
        <div className='mb-1'>
          <p className='mb-0'>
            <a key={idx} onClick={() => changeTimeWindow(span, true)}>
              {formatDateTime(span.start)} - {formatDateTime(span.end)}
            </a>
          </p>
          <div className='flex gap-4 justify-center'>
            <span>{span.collectors}</span>
            <span>
              <Typography.Text
                style={{ width: 150 }}
                ellipsis={{ tooltip: span.filename }}
              >
                {span.filename}
              </Typography.Text>
            </span>
          </div>
        </div>
      ))}
    </div>
  )

  return (
    <div className='flex flex-col'>
      {clusterInfoRes?.data && (
        <Card withBorder>
          <Title level={4}>{clusterInfoRes?.data.name}</Title>
          <div className='grid grid-cols-3'>
            <Text type='secondary'>Cluster ID: {clusterInfoRes?.data.id}</Text>
            <OnPremUIProvider>
              <Text type='secondary' className='col-span-2'>
                Data Range: {formatDateTime(clusterInfoRes.data.startTime!)} -{' '}
                {formatDateTime(clusterInfoRes.data.endTime!)}
              </Text>
            </OnPremUIProvider>
            <Text type='secondary'>Version: {clusterInfoRes.data.version}</Text>
            <Text type='secondary'>Topology: {topology}</Text>
            <OnPremUIProvider>
              <Text type='secondary'>
                Last Updated:{' '}
                {formatDateTime(clusterInfoRes.data.lastestUpdated!)}
              </Text>
            </OnPremUIProvider>
            <CloudUIProvider>
              <Text type='secondary'>
                Created: {formatDateTime(clusterInfoRes.data.created!)}
              </Text>
              <Text type='secondary'>
                Provider: {clusterInfoRes.data.provider}
              </Text>
              <Text type='secondary'>Region: {clusterInfoRes.data.region}</Text>
            </CloudUIProvider>
          </div>
          <CloudUIProvider>
            <div className='absolute right-4 top-4'>
              <Tag color='#0ca6f2'>
                {clusterInfoRes.data.deployType + ' tier'}
              </Tag>
              <Tag color={statusColor}>{clusterInfoRes.data.status}</Tag>
            </div>
          </CloudUIProvider>
        </Card>
      )}

      {day && (
        <OnPremUIProvider>
          <section>
            <div className='py-2 time-window-select-header relative flex items-center'>
              <DatePicker
                bordered={false}
                value={dayjs.unix(day)}
                onChange={(val) => setDay(val!.startOf('d').unix())}
                allowClear={false}
              />

              <a className='absolute my-auto h-[22px] top-0 bottom-0 left-[100px]'>
                <Tooltip title='You can move the timeline by pressing CTRL and dragging the mouse, and zoom in or out the timeline by pressing CTRL and scrolling mouse wheel.'>
                  <QuestionCircleOutlined className='text-gray-500' />
                </Tooltip>
              </a>

              <div className='ml-auto flex'>
                {canJumpPreTimeSpan ? (
                  <a className='px-1' onClick={jumpPreTimeSpan}>
                    <ArrowLeftOutlined />
                  </a>
                ) : (
                  <div className='px-1 text-gray-500'>
                    <ArrowLeftOutlined />
                  </div>
                )}
                <Popover content={TimeSpanList} placement='bottomRight'>
                  <a className='px-1'>Jump to</a>
                </Popover>
                {canJumpNextTimeSpan ? (
                  <a className='px-1' onClick={jumpNextTimeSpan}>
                    <ArrowRightOutlined />
                  </a>
                ) : (
                  <div className='px-1 text-gray-500'>
                    <ArrowRightOutlined />
                  </div>
                )}
              </div>
            </div>

            <TimeWindowSelect
              startTime={day}
              timeSpans={timeSpans}
              timeWindow={selectedTimeWindow}
              onPre={preDay}
              onNext={nextDay}
              onTimeWindowChange={debounceChangeTimeWindow}
              onTimelineChange={debounceChangeTimeline}
            />
          </section>
        </OnPremUIProvider>
      )}

      {clusterSloganRes?.data && (
        <section>
          <ClusterSlogans
            clusterSloganRes={clusterSloganRes?.data.items || []}
          />
        </section>
      )}

      <section className='mb-4'>
        <OnPremUIProvider>
          {hasSelectTimeSpan && clusterInfoRes?.data ? (
            <OPDetailActions
              cluster={clusterInfoRes.data}
              packages={clusterDataInfoRes?.data?.dataInfos!}
              timeRange={selectedTimeWindow!}
              timeWindowStr={timeWindowStr}
            />
          ) : (
            <div className='flex flex-col items-center pt-12'>
              <Text className='text-xl'>No Cluster Data</Text>
              <Text type='secondary'>Try another time range</Text>
              <div className='h-12'></div>
              <EmptyBoxIcon />
              <Text type='secondary'>No Data</Text>
            </div>
          )}
        </OnPremUIProvider>
      </section>
    </div>
  )
}

const OPDetailActions: React.FC<{
  cluster: ClusterCluster
  timeRange: TimeRange
  packages: ClusterDataInfo[]
  timeWindowStr: string
}> = ({ cluster, timeRange }) => {
  const { orgId, cid } = useParams<'orgId' | 'cid'>()
  const isDM = cluster.clusterType?.startsWith('dm')

  return (
    <section>
      <div className='py-4 pl-3 flex flex-row'>
        <Text strong>Details</Text>
      </div>
      <div className='grid grid-cols-3 gap-4'>
        <OPMetricsModal
          oid={orgId!}
          cid={cid!}
          timeRange={timeRange}
          cluster={cluster}
          visible={true}
        />
        <InstanceLogsModal oid={orgId!} cid={cid!} timeRange={timeRange} />
        <SlowQueryModal oid={orgId!} cid={cid!} timeRange={timeRange} />
        <DownloadModal oid={orgId!} cid={cid!} timeRange={timeRange} />
      </div>
      <div className='py-4 pl-3 flex flex-row'>
        <Text strong>Reports</Text>
      </div>
      <div className='grid grid-cols-3 gap-4'>
        <DetailItemCard
          name={ReportTypeName[ReportType.Benchmark]}
          desc='Dig performance bottleneck, for POC benchmark'
          to={`report/${ReportType.Benchmark}`}
          onClick={() => clusterDetailTelemetry.clickBenchmarkReportCard()}
        />
        <DetailItemCard
          name={ReportTypeName[ReportType.Comparison]}
          desc='Explore the change of key metrics, you may get the diagnostic clues'
          to={`report/${ReportType.Comparison}`}
          onClick={() => clusterDetailTelemetry.clickComparisonReportCard()}
        />
        {!isDM && (
          <HealthReportLink
            orgId={orgId!}
            clusterId={cid!}
            timeRange={timeRange!}
          />
        )}
      </div>
    </section>
  )
}

const ClusterSlogans: React.FC<{ clusterSloganRes: ClusterSloganResp[] }> = ({
  clusterSloganRes
}) => {
  return (
    <div style={{ background: 'rgb(12,166,242, 0.2)' }}>
      <Carousel
        autoplay
        className='my-4'
        speed={2000}
        autoplaySpeed={4000}
        waitForAnimate={true}
      >
        {clusterSloganRes.map((md) => (
          <div key={md.id!}>
            <div
              className='p-4'
              dangerouslySetInnerHTML={{ __html: md.text! }}
            ></div>
          </div>
        ))}
      </Carousel>
    </div>
  )
}

export default Page
