import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
} from 'react'
import * as echarts from 'echarts'
import ReactECharts from 'echarts-for-react'

import type { BeaconVibrationSample } from '../../../../entities/beacon/model/types'
import type {
  MachineCalibrationRule,
  MachineStateDefinition,
  RecognizedMachineStateInterval,
} from '../../../../entities/machine/model/types'
import {
  buildCalibrationTooltip,
  type CalibrationTimeRange,
  formatCalibrationDateTime,
  getIntervalAtTime,
  getNearestVibrationSample,
  getStateDefinitionById,
} from './utils'

type MachineVibrationChartProps = {
  definitions: MachineStateDefinition[]
  isSelectionMode: boolean
  intervals: RecognizedMachineStateInterval[]
  onRangeSelect: (range: CalibrationTimeRange | null) => void
  onRuleSelect: (rule: MachineCalibrationRule) => void
  rules: MachineCalibrationRule[]
  selectedRange: CalibrationTimeRange | null
  samples: BeaconVibrationSample[]
  tooltipGroupId: string
}

type VibrationTooltipParam = {
  axisValue?: number | string
  data?: [number, number, number]
}

type AxisPointerLabelParam = {
  value: number | string
}

type CalibrationChartInstance = {
  dispatchAction: (payload: { type: string; [key: string]: unknown }) => void
  group?: string
}

type BrushSelectedEvent = {
  batch?: Array<{
    areas?: Array<{
      coordRange?: [number | string, number | string]
    }>
  }>
}

type ChartClickEvent = {
  componentType?: string
  data?: {
    ruleId?: number
  } | Array<{
    ruleId?: number
  }>
}

const fontFamily = 'Montserrat Variable, Montserrat, sans-serif'

function hexToRgba(color: string, alpha: number) {
  if (!/^#[\da-f]{6}$/i.test(color)) return `rgba(3, 28, 154, ${alpha})`

  const red = Number.parseInt(color.slice(1, 3), 16)
  const green = Number.parseInt(color.slice(3, 5), 16)
  const blue = Number.parseInt(color.slice(5, 7), 16)

  return `rgba(${red}, ${green}, ${blue}, ${alpha})`
}

function formatAxisNumber(value: number | string) {
  const numericValue = Number(value)

  if (!Number.isFinite(numericValue)) return String(value)

  return String(Number(numericValue.toFixed(3)))
}

function roundAxisBound(value: number, direction: 'down' | 'up') {
  const multiplier = 10

  return direction === 'up'
    ? Math.ceil(value * multiplier) / multiplier
    : Math.floor(value * multiplier) / multiplier
}

export function MachineVibrationChart({
  definitions,
  isSelectionMode,
  intervals,
  onRangeSelect,
  onRuleSelect,
  rules,
  selectedRange,
  samples,
  tooltipGroupId,
}: MachineVibrationChartProps) {
  const chartRef = useRef<CalibrationChartInstance | null>(null)

  useEffect(() => {
    const chart = chartRef.current

    if (!chart) return

    chart.dispatchAction({
      type: 'takeGlobalCursor',
      key: 'brush',
      brushOption: {
        brushMode: 'single',
        brushType: isSelectionMode ? 'lineX' : false,
      },
    })
  }, [isSelectionMode])

  useEffect(() => {
    const chart = chartRef.current

    if (!chart || selectedRange) return

    chart.dispatchAction({
      areas: [],
      type: 'brush',
    })
  }, [selectedRange])

  const handleBrushSelected = useCallback((event: BrushSelectedEvent) => {
    const coordRange = event.batch?.[0]?.areas?.[0]?.coordRange

    if (!coordRange) {
      onRangeSelect(null)
      return
    }

    const start = Number(coordRange[0])
    const end = Number(coordRange[1])

    if (!Number.isFinite(start) || !Number.isFinite(end) || start === end) {
      onRangeSelect(null)
      return
    }

    onRangeSelect({
      end: Math.max(start, end),
      start: Math.min(start, end),
    })
  }, [onRangeSelect])

  const handleClick = useCallback((event: ChartClickEvent) => {
    if (event.componentType !== 'markArea') return

    const data = Array.isArray(event.data) ? event.data[0] : event.data
    const rule = rules.find((item) => item.id === data?.ruleId)

    if (rule) {
      onRuleSelect(rule)
    }
  }, [onRuleSelect, rules])

  const chartEvents = useMemo(() => ({
    brushSelected: handleBrushSelected,
    brushselected: handleBrushSelected,
    click: handleClick,
  }), [handleBrushSelected, handleClick])

  const option = useMemo(() => {
    const values = samples.map((sample) => sample.vibration)
    const minValue = Math.max(
      0,
      roundAxisBound(Math.min(...values, 0) - 0.05, 'down'),
    )
    const maxValue = roundAxisBound(Math.max(...values, 1) + 0.08, 'up')
    const ruleAreas = rules.map((rule) => {
      const definition = getStateDefinitionById(
        definitions,
        rule.stateDefinitionId,
      )
      const color = definition?.color ?? '#031d9a'

      return [
        {
          itemStyle: {
            color: hexToRgba(color, 0.1),
          },
          name: definition?.name ?? 'Правило',
          ruleId: rule.id,
          yAxis: rule.vibrationMin,
        },
        {
          ruleId: rule.id,
          yAxis: rule.vibrationMax,
        },
      ]
    })

    return {
      animation: true,
      animationDuration: 220,
      textStyle: {
        fontFamily,
        fontWeight: 500,
      },
      tooltip: {
        trigger: 'axis',
        triggerOn: 'mousemove|click',
        axisPointer: {
          type: 'line',
          axis: 'x',
          lineStyle: {
            color: 'rgba(3, 28, 154, 0.35)',
            width: 1,
          },
          label: {
            show: true,
            backgroundColor: '#20263a',
            color: '#ffffff',
            fontFamily,
            formatter: (params: AxisPointerLabelParam) => {
              return formatCalibrationDateTime(Number(params.value))
            },
          },
        },
        borderWidth: 0,
        backgroundColor: '#20263a',
        padding: 12,
        textStyle: {
          color: '#ffffff',
          fontSize: 13,
          fontFamily,
        },
        extraCssText:
          'border-radius: 14px; box-shadow: 0 12px 32px rgba(16, 24, 40, 0.22);',
        transitionDuration: 0,
        formatter: (
          params: VibrationTooltipParam | VibrationTooltipParam[],
        ) => {
          const point = Array.isArray(params) ? params[0] : params
          const timeMillis = Number(point?.axisValue ?? point?.data?.[0])

          if (!Number.isFinite(timeMillis)) return ''

          const sample = getNearestVibrationSample(samples, timeMillis)
          const interval = getIntervalAtTime(intervals, timeMillis)

          return buildCalibrationTooltip({
            definitions,
            interval,
            sample,
            timeMillis,
          })
        },
      },
      axisPointer: {
        link: [
          {
            xAxisIndex: 'all',
          },
        ],
      },
      brush: {
        brushMode: 'single',
        brushStyle: {
          borderColor: 'rgba(3, 28, 154, 0.36)',
          color: 'rgba(3, 28, 154, 0.12)',
        },
        throttleDelay: 80,
        throttleType: 'debounce',
        toolbox: [],
        xAxisIndex: 0,
      },
      grid: {
        left: 52,
        right: 28,
        top: 28,
        bottom: 46,
        containLabel: true,
      },
      dataZoom: [
        {
          type: 'slider',
          height: 22,
          bottom: 10,
          borderColor: 'transparent',
          backgroundColor: 'rgba(146, 155, 170, 0.12)',
          fillerColor: 'rgba(3, 28, 154, 0.1)',
          showDataShadow: false,
          showDetail: false,
          brushSelect: false,
        },
        {
          type: 'inside',
        },
      ],
      xAxis: {
        type: 'time',
        axisLabel: {
          color: '#738098',
          fontSize: 12,
          fontFamily,
          hideOverlap: true,
        },
        axisLine: {
          lineStyle: {
            color: 'rgba(146, 155, 170, 0.24)',
          },
        },
        axisTick: {
          show: false,
        },
        splitLine: {
          show: false,
        },
      },
      yAxis: {
        type: 'value',
        min: minValue,
        max: maxValue,
        name: 'vibration',
        nameTextStyle: {
          color: '#8c96aa',
          fontSize: 12,
          fontFamily,
        },
        axisLabel: {
          color: '#738098',
          fontSize: 12,
          fontFamily,
          formatter: formatAxisNumber,
        },
        splitLine: {
          lineStyle: {
            color: 'rgba(146, 155, 170, 0.12)',
            type: 'dashed',
          },
        },
      },
      series: [
        {
          name: 'Вибрация',
          type: 'line',
          data: samples.map((sample) => [
            sample.timeMillis,
            sample.vibration,
            sample.duration,
          ]),
          symbol: 'none',
          showSymbol: false,
          sampling: 'lttb',
          smooth: true,
          lineStyle: {
            width: 2,
            color: '#031d9a',
          },
          itemStyle: {
            color: '#031d9a',
          },
          areaStyle: {
            color: {
              type: 'linear',
              x: 0,
              y: 0,
              x2: 0,
              y2: 1,
              colorStops: [
                {
                  offset: 0,
                  color: 'rgba(3, 29, 154, 0.22)',
                },
                {
                  offset: 1,
                  color: 'rgba(3, 29, 154, 0.02)',
                },
              ],
            },
          },
          markArea: {
            data: ruleAreas,
            label: {
              show: false,
            },
            emphasis: {
              itemStyle: {
                opacity: 0.18,
              },
            },
            silent: false,
          },
        },
      ],
    }
  }, [definitions, intervals, rules, samples])

  if (samples.length === 0) {
    return (
      <div className="machine-chart-empty">
        Нет данных вибрации за выбранный период
      </div>
    )
  }

  return (
    <div className="machine-vibration-chart">
      <ReactECharts
        option={option}
        notMerge
        lazyUpdate
        onEvents={chartEvents}
        onChartReady={(chart) => {
          chartRef.current = chart
          chart.group = tooltipGroupId
          echarts.connect(tooltipGroupId)
        }}
        style={{ height: 320 }}
      />
    </div>
  )
}
