import { useEffect, useMemo, useRef, useState } from 'react'
import type { MouseEvent as ReactMouseEvent } from 'react'
import { Chart } from 'chart.js/auto'
import type { ChartArea, TooltipItem } from 'chart.js'

import { useIsCoarsePointer } from '../../hooks/useMediaQuery'
import type { SlotData } from '../../types/dashboard'
import { CHART_LAYOUT_PADDING } from './chartConstants'
import {
  computeNegWeekendRanges,
  createNowLinePluginRef,
  createSlotBackgroundPluginRefs,
} from './chartPlugins'

const COL = {
  fve: '#EF9F27',
  baz: '#378ADD',
  ev: '#534AB7',
  tc: '#D4537E',
  bat: '#1D9E75',
  sit: '#E24B4A',
  buy: '#E24B4A',
  sell: '#1D9E75',
} as const

function kwFromW(w: number | null | undefined): number | null {
  if (w == null || Number.isNaN(Number(w))) return null
  return Number(w) / 1000
}

function sumW(a: number | null, b: number | null): number | null {
  if (a == null && b == null) return null
  return (a ?? 0) + (b ?? 0)
}

export type EnergyLegendItem = {
  key: string
  label: string
  color: string
  dashed?: boolean
  dashStyle?: 'dashed' | 'dotted'
}

export const ENERGY_LEGEND: EnergyLegendItem[] = [
  { key: 'fve_real', label: 'FVE skutečnost', color: COL.fve },
  { key: 'fve_pred', label: 'FVE předpověď', color: COL.fve, dashed: true },
  { key: 'fve_corr', label: 'FVE korigovaná', color: COL.fve, dashed: true, dashStyle: 'dotted' },
  { key: 'baz_real', label: 'Spotřeba skutečnost', color: COL.baz },
  { key: 'baz_pred', label: 'Spotřeba předpověď', color: COL.baz, dashed: true },
  { key: 'ev', label: 'EV plán', color: COL.ev },
  { key: 'tc', label: 'TČ plán', color: COL.tc },
  { key: 'bat', label: 'Baterie', color: COL.bat },
  { key: 'sit', label: 'Síť', color: COL.sit },
  { key: 'buy_price', label: 'Cena nákup', color: COL.buy, dashed: true },
  { key: 'sell_price', label: 'Cena prodej', color: COL.sell, dashed: true },
]

type Props = {
  slots: SlotData[]
  nowIndex: number
  hidden: Set<string>
  onToggle: (key: string) => void
  onChartArea?: (area: ChartArea) => void
}

export function EnergyChart({ slots, nowIndex, hidden, onToggle, onChartArea }: Props) {
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const chartRef = useRef<Chart | null>(null)
  const wrapRef = useRef<HTMLDivElement>(null)
  const onChartAreaRef = useRef(onChartArea)
  onChartAreaRef.current = onChartArea

  // Touch zařízení: hover tooltip vypnutý; tap vybere slot do panelu nad grafem.
  const isCoarse = useIsCoarsePointer()
  const [touchIdx, setTouchIdx] = useState<number | null>(null)

  const slotsRef = useRef<SlotData[]>([])
  const negRangesRef = useRef<ReturnType<typeof computeNegWeekendRanges>>([])
  const nowIndexRef = useRef(0)
  const labelsRef = useRef<string[]>([])

  slotsRef.current = slots
  nowIndexRef.current = nowIndex

  const labels = useMemo(
    () =>
      slots.map((s) => {
        const d = new Date(s.interval_start)
        return d.toLocaleTimeString('cs-CZ', {
          hour: '2-digit',
          minute: '2-digit',
          timeZone: 'Europe/Prague',
        })
      }),
    [slots],
  )
  labelsRef.current = labels

  const negRanges = useMemo(() => computeNegWeekendRanges(slots, nowIndex), [slots, nowIndex])
  negRangesRef.current = negRanges

  const windowKey = useMemo(
    () => (slots.length ? `${slots[0]!.interval_start}|${slots.length}` : ''),
    [slots],
  )

  const series = useMemo(() => {
    const fveReal = slots.map((s, i) => (i <= nowIndex ? kwFromW(s.pv_power_w) : null))
    const fvePred = slots.map((s) => kwFromW(sumW(s.pv_a_forecast_w, s.pv_b_forecast_w)))
    const fveCorr = slots.map((s) =>
      kwFromW(s.pv_forecast_corrected_w ?? sumW(s.pv_a_forecast_w, s.pv_b_forecast_w)),
    )
    const bazReal = slots.map((s, i) => (i <= nowIndex ? kwFromW(s.load_power_w) : null))
    const bazPred = slots.map((s) => kwFromW(s.load_baseline_w))
    const ev = slots.map((s) => kwFromW(sumW(s.ev1_setpoint_w, s.ev2_setpoint_w)))
    const tc = slots.map((s) => kwFromW(s.heat_pump_setpoint_w))
    const bat = slots.map((s, i) =>
      i <= nowIndex ? kwFromW(s.battery_power_w) : kwFromW(s.battery_setpoint_w),
    )
    const sit = slots.map((s, i) =>
      i <= nowIndex ? kwFromW(s.grid_power_w) : kwFromW(s.grid_setpoint_w),
    )
    const buy = slots.map((s) => (s.buy_price == null ? null : s.buy_price))
    const sell = slots.map((s) => (s.sell_price == null ? null : s.sell_price))
    return { fveReal, fvePred, fveCorr, bazReal, bazPred, ev, tc, bat, sit, buy, sell }
  }, [slots, nowIndex])

  const bgPlugin = useMemo(
    () => createSlotBackgroundPluginRefs(slotsRef, negRangesRef),
    [],
  )
  const nowPlugin = useMemo(() => createNowLinePluginRef(nowIndexRef, 'teď'), [])

  useEffect(() => {
    const canvas = canvasRef.current
    if (!canvas || !windowKey) return

    const mkDs = (
      key: string,
      label: string,
      d: (number | null)[],
      color: string,
      opts: {
        fill?: boolean | 'origin'
        dashed?: boolean
        dash?: number[]
        yAxisID?: string
        order: number
        borderWidth?: number
      },
    ) => ({
      label,
      data: d,
      borderColor: color,
      backgroundColor:
        opts.fill === true ? `${color}33` : opts.fill === 'origin' ? `${color}40` : undefined,
      fill: opts.fill ?? false,
      borderDash: opts.dash ?? (opts.dashed ? [5, 4] : undefined),
      borderWidth: opts.borderWidth ?? (opts.dashed ? 1 : 1.2),
      pointRadius: 0,
      hitRadius: 6,
      tension: 0.15,
      yAxisID: opts.yAxisID ?? 'y',
      order: opts.order,
      hidden: hidden.has(key),
    })

    const chart = new Chart(canvas, {
      type: 'line',
      plugins: [bgPlugin, nowPlugin],
      data: {
        labels: [...labels],
        datasets: [
          mkDs('sit', 'Síť', series.sit, COL.sit, { fill: 'origin', order: 2 }),
          mkDs('bat', 'Baterie', series.bat, COL.bat, { fill: 'origin', order: 3 }),
          mkDs('ev', 'EV plán', series.ev, COL.ev, { fill: true, order: 4 }),
          mkDs('tc', 'TČ plán', series.tc, COL.tc, { fill: true, order: 5 }),
          mkDs('baz_real', 'Spotřeba ■', series.bazReal, COL.baz, { fill: true, order: 6 }),
          mkDs('fve_real', 'FVE ■', series.fveReal, COL.fve, { fill: true, order: 7 }),
          mkDs('baz_pred', 'Spotřeba ···', series.bazPred, COL.baz, { dashed: true, order: 8 }),
          mkDs('fve_pred', 'FVE ···', series.fvePred, COL.fve, { dashed: true, order: 9 }),
          mkDs('fve_corr', 'FVE (korig.)', series.fveCorr, COL.fve, {
            dashed: true,
            dash: [2, 3],
            order: 9,
            borderWidth: 1,
          }),
          mkDs('buy_price', 'Nákup', series.buy, COL.buy, {
            dashed: true,
            yAxisID: 'y1',
            order: 10,
            borderWidth: 1,
          }),
          mkDs('sell_price', 'Prodej', series.sell, COL.sell, {
            dashed: true,
            yAxisID: 'y1',
            order: 11,
            borderWidth: 1,
          }),
        ],
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        layout: { padding: { ...CHART_LAYOUT_PADDING } },
        plugins: {
          legend: { display: false },
          tooltip: {
            enabled: !isCoarse,
            callbacks: {
              title(items: TooltipItem<'line'>[]) {
                const i = items[0]?.dataIndex ?? 0
                return labelsRef.current[i] ?? ''
              },
              label(ctx: TooltipItem<'line'>) {
                const label = ctx.dataset.label ?? ''
                const v = ctx.parsed.y as number | null
                if (v == null || Number.isNaN(v)) return `${label}: —`
                if (ctx.dataset.yAxisID === 'y1') return `${label}: ${v.toFixed(3)} Kč/kWh`
                return `${label}: ${v.toFixed(2)} kW`
              },
            },
          },
        },
        scales: {
          x: {
            type: 'category',
            offset: false,
            grid: { color: 'rgba(148,163,184,0.12)' },
            ticks: {
              color: '#94a3b8',
              maxRotation: 0,
              autoSkip: false,
              callback(_val: string | number, i: number) {
                return i % 8 === 0 ? labelsRef.current[i] ?? '' : ''
              },
            },
          },
          y: {
            position: 'left',
            grid: { color: 'rgba(148,163,184,0.12)' },
            ticks: { color: '#94a3b8', font: { size: 10 } },
            title: { display: true, text: 'kW', color: '#64748b', font: { size: 10 } },
          },
          y1: {
            position: 'right',
            grid: { drawOnChartArea: false },
            ticks: { color: '#94a3b8', font: { size: 9 } },
            title: { display: true, text: 'Kč/kWh', color: '#64748b', font: { size: 10 } },
          },
        },
      },
    })

    chartRef.current = chart
    requestAnimationFrame(() => {
      const a = chart.chartArea
      if (a) onChartAreaRef.current?.(a)
    })

    const ro = new ResizeObserver(() => {
      chart.resize()
      requestAnimationFrame(() => {
        const a = chart.chartArea
        if (a) onChartAreaRef.current?.(a)
      })
    })
    ro.observe(canvas.parentElement ?? canvas)

    return () => {
      ro.disconnect()
      chart.destroy()
      chartRef.current = null
    }
    // Jen při změně okna (první slot / počet) nebo typu pointeru; data dorovnává druhý effect.
  }, [windowKey, bgPlugin, nowPlugin, isCoarse])

  useEffect(() => {
    const ch = chartRef.current
    if (!ch || !slots.length) return
    ch.data.labels = [...labels]
    const dss = ch.data.datasets
    if (!dss?.length) return
    const s = series
    const rows: (number | null)[][] = [
      s.sit,
      s.bat,
      s.ev,
      s.tc,
      s.bazReal,
      s.fveReal,
      s.bazPred,
      s.fvePred,
      s.fveCorr,
      s.buy,
      s.sell,
    ]
    rows.forEach((data, i) => {
      const ds = dss[i]
      if (ds) ds.data = data
    })
    ch.update('none')
    requestAnimationFrame(() => {
      const a = ch.chartArea
      if (a) onChartAreaRef.current?.(a)
    })
  }, [labels, series, slots.length])

  const keys = [
    'sit',
    'bat',
    'ev',
    'tc',
    'baz_real',
    'fve_real',
    'baz_pred',
    'fve_pred',
    'fve_corr',
    'buy_price',
    'sell_price',
  ] as const

  useEffect(() => {
    const ch = chartRef.current
    const dss = ch?.data.datasets
    if (!ch || !dss?.length) return
    keys.forEach((k, i) => {
      const ds = dss[i]
      if (ds) ds.hidden = hidden.has(k)
    })
    ch.update('none')
  }, [hidden])

  // Tap mimo graf zruší vybraný slot (touch panel).
  useEffect(() => {
    if (touchIdx == null) return
    const onDocDown = (e: PointerEvent) => {
      const el = wrapRef.current
      if (el && e.target instanceof Node && !el.contains(e.target)) setTouchIdx(null)
    }
    document.addEventListener('pointerdown', onDocDown)
    return () => document.removeEventListener('pointerdown', onDocDown)
  }, [touchIdx])

  const handleCanvasTap = (ev: ReactMouseEvent<HTMLCanvasElement>) => {
    if (!isCoarse) return
    const ch = chartRef.current
    if (!ch) return
    // getElementsAtEventForMode je v runtime Chart, ale verejne typy chart.js 4.4 ji nedeklaruji
    const chWithHit = ch as unknown as {
      getElementsAtEventForMode(
        e: Event,
        mode: string,
        options: { intersect: boolean },
        useFinalPosition: boolean,
      ): Array<{ index: number }>
    }
    const els = chWithHit.getElementsAtEventForMode(ev.nativeEvent, 'index', { intersect: false }, false)
    setTouchIdx(els.length ? els[0]!.index : null)
  }

  // Texty panelu reusují stejné formátování jako tooltip callbacks (kW / Kč/kWh).
  const touchInfo = useMemo(() => {
    if (!isCoarse || touchIdx == null || touchIdx < 0 || touchIdx >= slots.length) return null
    const rows: Array<{ key: string; label: string; text: string; color: string }> = []
    const push = (
      key: string,
      label: string,
      v: number | null | undefined,
      color: string,
      unit: 'kW' | 'czk',
    ) => {
      if (hidden.has(key)) return
      const text =
        v == null || Number.isNaN(v)
          ? '—'
          : unit === 'czk'
            ? `${v.toFixed(3)} Kč/kWh`
            : `${v.toFixed(2)} kW`
      rows.push({ key, label, text, color })
    }
    push('fve_real', 'FVE ■', series.fveReal[touchIdx], COL.fve, 'kW')
    push('fve_pred', 'FVE ···', series.fvePred[touchIdx], COL.fve, 'kW')
    push('fve_corr', 'FVE (korig.)', series.fveCorr[touchIdx], COL.fve, 'kW')
    push('baz_real', 'Spotřeba ■', series.bazReal[touchIdx], COL.baz, 'kW')
    push('baz_pred', 'Spotřeba ···', series.bazPred[touchIdx], COL.baz, 'kW')
    push('ev', 'EV plán', series.ev[touchIdx], COL.ev, 'kW')
    push('tc', 'TČ plán', series.tc[touchIdx], COL.tc, 'kW')
    push('bat', 'Baterie', series.bat[touchIdx], COL.bat, 'kW')
    push('sit', 'Síť', series.sit[touchIdx], COL.sit, 'kW')
    push('buy_price', 'Nákup', series.buy[touchIdx], COL.buy, 'czk')
    push('sell_price', 'Prodej', series.sell[touchIdx], COL.sell, 'czk')
    return { title: labels[touchIdx] ?? '', rows }
  }, [isCoarse, touchIdx, series, labels, hidden, slots.length])

  return (
    <div ref={wrapRef} className="relative flex flex-col gap-2">
      {touchInfo ? (
        <div className="absolute inset-x-1 top-0 z-20 rounded-lg border border-slate-700 bg-slate-950/95 px-3 py-2 shadow-xl">
          <div className="mb-1 flex items-center justify-between gap-2 text-[11px] font-semibold text-slate-100">
            <span>{touchInfo.title}</span>
            <button
              type="button"
              onClick={() => setTouchIdx(null)}
              className="px-1 text-slate-400"
              aria-label="Zavřít detail slotu"
            >
              ✕
            </button>
          </div>
          <div className="flex flex-wrap gap-x-3 gap-y-0.5 text-[10px]">
            {touchInfo.rows.map((r) => (
              <span key={r.key} style={{ color: r.color }}>
                {r.label}: <span className="text-slate-200">{r.text}</span>
              </span>
            ))}
          </div>
        </div>
      ) : null}
      <div className="h-chart-sm w-full sm:h-chart-md lg:h-chart-lg">
        <canvas
          ref={canvasRef}
          onClick={handleCanvasTap}
          className="max-h-chart-sm w-full sm:max-h-chart-md lg:max-h-chart-lg"
          role="img"
          aria-label="Graf výkonů a cen"
        />
      </div>
      <div className="flex flex-wrap gap-x-3 gap-y-1.5 px-1">
        {ENERGY_LEGEND.map((item) => {
          const off = hidden.has(item.key)
          return (
            <button
              key={item.key}
              type="button"
              onClick={() => onToggle(item.key)}
              className={`inline-flex items-center gap-1.5 rounded-md px-1.5 py-0.5 text-[11px] transition hover:bg-white/5 ${
                off ? 'text-slate-500 line-through opacity-60' : 'text-slate-200'
              }`}
            >
              <span
                className="h-2.5 w-4 shrink-0 rounded-sm border border-white/10"
                style={{
                  backgroundColor: off ? 'transparent' : item.color,
                  borderStyle: item.dashStyle === 'dotted' ? 'dotted' : item.dashed ? 'dashed' : 'solid',
                }}
              />
              {item.label}
            </button>
          )
        })}
      </div>
    </div>
  )
}
