import { getValueFormat } from '@baurine/grafana-value-formats'
import { Axis, BarSeries, BrushEndListener, Chart, Position, ScaleType, Settings, Tooltip } from '@elastic/charts'
import { useChartThemeConfig } from '@tidb-cloud-uikit/theme/Chart'
import { Box, Flex, Prism, Skeleton, Stack, Typography } from '@tidbcloud/uikit'
import { ComponentClass, ComponentPropsWithoutRef, PropsWithChildren, forwardRef, useMemo } from 'react'

import { useTimeFormatter } from 'dbaas/components/TimeComponent'
import { compactSql } from 'dbaas/screens/NextGen/premium/shared/diagnosis/HighlightSQL'

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
