import {
  useCallback,
  useMemo,
} from 'react'
import ReactECharts from 'echarts-for-react'

import type { BeaconVibrationSample } from '../../../../entities/beacon/model/types'
import type {
  MachineCalibrationRule,
  MachineStateDefinition,
} from '../../../../entities/machine/model/types'
import {
  formatCalibrationDuration,
  formatVibration,
  getStateDefinitionById,
} from './utils'

type MachineVibrationHistogramChartProps = {
  definitions: MachineStateDefinition[]
  onBinSelect: (bin: MachineVibrationHistogramBin | null) => void
  rules: MachineCalibrationRule[]
  selectedBin: MachineVibrationHistogramBin | null
  samples: BeaconVibrationSample[]
}

export type MachineVibrationHistogramBin = {
  count: number
  durationSeconds: number
  end: number
  label: string
  ruleId: number | null
  start: number
  stateColor: string | null
  stateDefinitionId: number | null
  stateName: string | null
}

type HistogramTooltipParam = {
  dataIndex?: number
}

type HistogramClickParam = {
  dataIndex?: number
}

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

function findRuleForBin(
  binStart: number,
  binEnd: number,
  definitions: MachineStateDefinition[],
  rules: MachineCalibrationRule[],
) {
  const rule = [...rules]
    .sort((first, second) => first.priority - second.priority)
    .find((item) => (
      binStart <= item.vibrationMax &&
      binEnd >= item.vibrationMin
    ))

  if (!rule) return null

  const definition = getStateDefinitionById(definitions, rule.stateDefinitionId)

  return {
    ruleId: rule.id,
    stateColor: definition?.color ?? '#031d9a',
    stateDefinitionId: rule.stateDefinitionId,
    stateName: definition?.name ?? 'Состояние',
  }
}

function buildHistogramBins(
  definitions: MachineStateDefinition[],
  rules: MachineCalibrationRule[],
  samples: BeaconVibrationSample[],
) {
  if (samples.length === 0) return []

  const values = samples.map((sample) => sample.vibration)
  const rawMinValue = Math.min(...values)
  const rawMaxValue = Math.max(...values)
  const fallbackPadding = rawMinValue === rawMaxValue
    ? Math.max(0.05, rawMaxValue * 0.12)
    : 0
  const minValue = Math.max(0, rawMinValue - fallbackPadding)
  const maxValue = rawMaxValue + fallbackPadding
  const binCount = Math.min(24, Math.max(10, Math.ceil(Math.sqrt(samples.length))))
  const step = Math.max(0.01, (maxValue - minValue) / binCount)

  const bins: MachineVibrationHistogramBin[] = Array.from({ length: binCount }, (_, index) => {
    const start = minValue + step * index
    const end = index === binCount - 1 ? maxValue : start + step
    const state = findRuleForBin(start, end, definitions, rules)

    return {
      count: 0,
      durationSeconds: 0,
      end,
      label: `${formatVibration(start)}–${formatVibration(end)}`,
      ruleId: state?.ruleId ?? null,
      start,
      stateColor: state?.stateColor ?? null,
      stateDefinitionId: state?.stateDefinitionId ?? null,
      stateName: state?.stateName ?? null,
    }
  })

  samples.forEach((sample) => {
    const binIndex = Math.min(
      bins.length - 1,
      Math.max(0, Math.floor((sample.vibration - minValue) / step)),
    )

    bins[binIndex].count += 1
    bins[binIndex].durationSeconds += sample.duration
  })

  return bins
}

export function MachineVibrationHistogramChart({
  definitions,
  onBinSelect,
  rules,
  selectedBin,
  samples,
}: MachineVibrationHistogramChartProps) {
  const bins = useMemo(
    () => buildHistogramBins(definitions, rules, samples),
    [definitions, rules, samples],
  )
  const handleClick = useCallback((params: HistogramClickParam) => {
    const bin = typeof params.dataIndex === 'number'
      ? bins[params.dataIndex]
      : null

    onBinSelect(bin ?? null)
  }, [bins, onBinSelect])
  const chartEvents = useMemo(() => ({
    click: handleClick,
  }), [handleClick])
  const option = useMemo(() => ({
    animation: true,
    animationDuration: 220,
    textStyle: {
      fontFamily,
      fontWeight: 500,
    },
    tooltip: {
      trigger: 'item',
      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);',
      formatter: (params: HistogramTooltipParam) => {
        const bin = typeof params.dataIndex === 'number'
          ? bins[params.dataIndex]
          : null

        if (!bin) return ''

        return `
          <div style="min-width:240px;">
            <div style="color:#aeb8cc;font-size:12px;margin-bottom:8px;">
              ${bin.label}
            </div>
            <div style="display:grid;grid-template-columns:1fr auto;gap:7px 18px;">
              <span>Время</span>
              <strong>${formatCalibrationDuration(bin.durationSeconds)}</strong>
              <span>Сообщений</span>
              <strong>${bin.count}</strong>
              <span>Состояние</span>
              <strong>${bin.stateName ?? '—'}</strong>
            </div>
          </div>
        `
      },
    },
    grid: {
      left: 52,
      right: 28,
      top: 24,
      bottom: 42,
      containLabel: true,
    },
    xAxis: {
      type: 'category',
      data: bins.map((bin) => bin.label),
      axisLabel: {
        color: '#738098',
        fontSize: 11,
        fontFamily,
        hideOverlap: true,
        interval: 1,
      },
      axisLine: {
        lineStyle: {
          color: 'rgba(146, 155, 170, 0.24)',
        },
      },
      axisTick: {
        show: false,
      },
    },
    yAxis: {
      type: 'value',
      name: 'мин',
      nameTextStyle: {
        color: '#8c96aa',
        fontSize: 12,
        fontFamily,
      },
      axisLabel: {
        color: '#738098',
        fontSize: 12,
        fontFamily,
      },
      splitLine: {
        lineStyle: {
          color: 'rgba(146, 155, 170, 0.1)',
          type: 'dashed',
        },
      },
    },
    series: [
      {
        name: 'Время',
        type: 'bar',
        barMaxWidth: 32,
        data: bins.map((bin) => {
          const isSelected =
            selectedBin?.start === bin.start &&
            selectedBin?.end === bin.end
          const color = bin.stateColor ?? '#031d9a'

          return {
            value: Number((bin.durationSeconds / 60).toFixed(1)),
            itemStyle: {
              borderColor: isSelected ? color : 'transparent',
              borderWidth: isSelected ? 3 : 0,
              color,
              opacity: bin.stateColor ? 0.88 : 0.72,
            },
          }
        }),
        itemStyle: {
          borderRadius: [6, 6, 0, 0],
        },
      },
    ],
  }), [bins, selectedBin])

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

  return (
    <div className="machine-vibration-histogram-chart">
      <ReactECharts
        option={option}
        notMerge
        lazyUpdate
        onEvents={chartEvents}
        style={{ height: 260 }}
      />
    </div>
  )
}
