import React from 'react'
import {
  ComponentClass,
  ComponentPropsWithoutRef,
  PropsWithChildren,
  forwardRef,
  useMemo
} from 'react'

import { getValueFormat } from '@baurine/grafana-value-formats'
import {
  Axis,
  BarSeries,
  BrushEndListener,
  Chart,
  Position,
  ScaleType,
  Settings,
  Tooltip
} from '@elastic/charts'
import { Box, Flex, Prism, Skeleton, Stack, Typography } from '@tidbcloud/uikit'

import { useChartThemeConfig } from './_shared/chartTheme'
import { compactSql } from './_shared/HighlightSQL'
import { useTimeFormatter } from './_shared/useTimeFormatter'
import { withAlpha } from './colors'
import { isTopSQLOthersLabel } from './display'
import { useTopSQLTooltipTimeHeaderFormatter } from './time'
import { TopSQLTrendSeries } from './types'

interface TrendSeriesWithColor extends TopSQLTrendSeries {
  color: string
}

function TooltipSQL({ sql }: { sql: string }) {
  const formattedSql = useMemo(() => compactSql(sql), [sql])

  return (
    <Prism
      noCopy
      language='sql'
      styles={{
        code: {
          backgroundColor: 'transparent !important',
          padding: 0,
          fontSize: 11,
          lineHeight: 1.35
        },
        scrollArea: {
          '& > div > div': {
            display: 'block !important'
          }
        },
        line: {
          padding: 0
        },
        lineContent: {
          overflow: 'hidden',
          whiteSpace: 'nowrap',
          textOverflow: 'ellipsis'
        }
      }}
    >
      {formattedSql}
    </Prism>
  )
}

const TrendChartTooltip = ({
  header,
  values,
  headerFormatter,
  backgroundColor
}: any) => (
  <Box
    px='sm'
    py='xs'
    sx={(theme) => ({
      minWidth: 220,
      backgroundColor,
      border: `1px solid ${theme.colors.carbon[3]}`,
      borderRadius: theme.radius.sm,
      boxShadow: theme.shadows.sm
    })}
  >
    {header && (
      <Typography variant='label-md' mb='xs'>
        {headerFormatter ? headerFormatter(header) : header.formattedValue}
      </Typography>
    )}
    <Stack gap={6}>
      {(() => {
        const sortedValues = [...values].sort((left: any, right: any) => {
          const leftIsOthers = isTopSQLOthersLabel(left.label)
          const rightIsOthers = isTopSQLOthersLabel(right.label)

          if (leftIsOthers !== rightIsOthers) {
            return leftIsOthers ? 1 : -1
          }

          return Number(right.value ?? 0) - Number(left.value ?? 0)
        })
        const hasHighlightedItem = sortedValues.some(
          (item: any) => item.isHighlighted
        )

        return sortedValues.map((item: any, index: number) => (
          <Flex
            key={`${item.label}-${index}`}
            align='center'
            gap='xs'
            justify='space-between'
            sx={{
              opacity: hasHighlightedItem && !item.isHighlighted ? 0.35 : 1,
              transition: 'opacity 120ms ease'
            }}
          >
            <Flex align='center' gap={6} sx={{ minWidth: 0, flex: 1 }}>
              <Box
                w={4}
                h={16}
                sx={{ backgroundColor: String(item.color), flexShrink: 0 }}
              />
              {isTopSQLOthersLabel(item.label) ? (
                <Typography
                  variant='body-sm'
                  fs='italic'
                  sx={{
                    minWidth: 0,
                    overflow: 'hidden',
                    textOverflow: 'ellipsis',
                    whiteSpace: 'nowrap'
                  }}
                >
                  {item.label}
                </Typography>
              ) : (
                <Box sx={{ minWidth: 0, flex: 1, '& pre': { margin: 0 } }}>
                  <TooltipSQL sql={item.label} />
                </Box>
              )}
            </Flex>
            <Typography variant='body-sm' fw={500} sx={{ flexShrink: 0 }}>
              {item.formattedValue}
            </Typography>
          </Flex>
        ))
      })()}
    </Stack>
  </Box>
)

interface TrendChartProps {
  data: TrendSeriesWithColor[]
  loading?: boolean
  timeRangeValue: [number, number]
  stepSeconds: number
  valueFormatter?: (value: number) => string
  highlightedKey?: string
  onBrushEnd: BrushEndListener
}

type ElasticChartInstance = InstanceType<typeof Chart>
type ElasticChartProps = PropsWithChildren<
  ComponentPropsWithoutRef<typeof Chart>
>

const ChartWithRef = Chart as unknown as ComponentClass<ElasticChartProps>

const TrendChart = forwardRef<
  ElasticChartInstance,
  PropsWithChildren<TrendChartProps>
>(
  (
    {
      data,
      loading = false,
      timeRangeValue,
      stepSeconds,
      valueFormatter,
      highlightedKey,
      onBrushEnd
    },
    ref
  ) => {
    const { themeConfig } = useChartThemeConfig()
    const sameDay = useMemo(() => {
      const dayFormatter = new Intl.DateTimeFormat('en-US', {
        year: 'numeric',
        month: '2-digit',
        day: '2-digit'
      })

      return (
        dayFormatter.format(timeRangeValue[0] * 1000) ===
        dayFormatter.format(timeRangeValue[1] * 1000)
      )
    }, [timeRangeValue])
    const xAxisFormatter = useTimeFormatter(sameDay ? 'HH:mm' : 'MM-DD HH:mm')
    const tooltipTimeHeaderFormatter = useTopSQLTooltipTimeHeaderFormatter()
    const yAxisFormatter =
      valueFormatter ||
      ((value: number) => getValueFormat('short')(value, 1, 1))

    return (
      <Box h='100%' w='100%' data-testid='top-sql-trend-chart'>
        {loading ? (
          <Stack gap='md' justify='center' h='100%' px='md'>
            <Skeleton h={28} w='100%' />
            <Skeleton h={28} w='94%' />
            <Skeleton h={28} w='88%' />
            <Skeleton h={28} w='76%' />
            <Skeleton h={28} w='68%' />
          </Stack>
        ) : data.length ? (
          <ChartWithRef ref={ref}>
            <Settings
              baseTheme={themeConfig}
              showLegend={false}
              onBrushEnd={onBrushEnd}
              xDomain={{
                minInterval: stepSeconds * 1000,
                min: timeRangeValue[0] * 1000,
                max: timeRangeValue[1] * 1000
              }}
            />
            <Tooltip
              headerFormatter={({ value }) =>
                tooltipTimeHeaderFormatter(Number(value) / 1000)
              }
              customTooltip={TrendChartTooltip}
            />
            <Axis
              id='top-sql-bottom'
              position={Position.Bottom}
              tickFormat={(value) => xAxisFormatter(value / 1000)}
            />
            <Axis
              id='top-sql-left'
              position={Position.Left}
              ticks={5}
              tickFormat={(value) => yAxisFormatter(Number(value))}
            />

            {data.map((series) => {
              const activeKey = highlightedKey
              const color = !activeKey
                ? series.color
                : activeKey === series.key
                ? series.color
                : withAlpha(series.color, 0.18)

              return (
                <BarSeries
                  key={series.key}
                  id={series.key}
                  name={series.label}
                  color={color}
                  xScaleType={ScaleType.Time}
                  yScaleType={ScaleType.Linear}
                  xAccessor='timestamp'
                  yAccessors={['value']}
                  stackAccessors={['timestamp']}
                  data={series.points.map((point) => ({
                    timestamp: point.timestamp * 1000,
                    value: point.value
                  }))}
                />
              )
            })}
          </ChartWithRef>
        ) : (
          <Box
            h='100%'
            sx={(theme) => ({
              border: `1px dashed ${theme.colors.carbon[4]}`,
              borderRadius: theme.radius.md
            })}
          >
            <Box
              h='100%'
              sx={{
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center'
              }}
            >
              <Typography variant='body-lg' c='carbon.7'>
                No trend data available.
              </Typography>
            </Box>
          </Box>
        )}
      </Box>
    )
  }
)

TrendChart.displayName = 'TrendChart'

export default TrendChart
