import React, { useMemo, useState, useCallback, useRef, memo, useEffect } from 'react'
import type { DayInfo, Person, DragState, ScheduleData } from './types'
import { getCellStyle, getCellStyleForPerson, getContrastColor } from './cellColors'
import { getHolidayMap } from './holidays'

const CELL_W = 32
const CELL_H = 32

const MONTH_NAMES: Record<number, string> = {
  1: 'Leden', 2: 'Únor', 3: 'Březen', 4: 'Duben', 5: 'Květen', 6: 'Červen',
  7: 'Červenec', 8: 'Srpen', 9: 'Září', 10: 'Říjen', 11: 'Listopad', 12: 'Prosinec',
}

const DAY_NAMES = ['Ne', 'Po', 'Út', 'St', 'Čt', 'Pá', 'So']

const MONTH_NAMES_SHORT: Record<number, string> = {
  1: 'Led', 2: 'Úno', 3: 'Bře', 4: 'Dub', 5: 'Kvě', 6: 'Čvn',
  7: 'Čvc', 8: 'Srp', 9: 'Zář', 10: 'Říj', 11: 'Lis', 12: 'Pro',
}

export interface SelectedCell {
  personId: string
  dayIdx: number
}

interface ScheduleTableProps {
  dayIndex: DayInfo[]
  people: Person[]
  tunnelClosures: Map<number, string>
  tunnelColors: Map<number, string>
  metroClosures: Map<number, string>
  metroColors: Map<number, string>
  d8Closures: Map<number, string>
  d8Colors: Map<number, string>
  sazltClosures: Map<number, string>
  sazltColors: Map<number, string>
  dayComments: Map<number, string>
  cellComments: Map<string, string>
  dragState: DragState | null
  onCellPointerDown: (e: React.PointerEvent, personId: string, dayIdx: number, value: string) => void
  onSetCell: (personId: string, dayIdx: number, value: string | null) => void
  onSetTunnelClosure: (dayIdx: number, text: string | null) => void
  onSetMetroClosure: (dayIdx: number, text: string | null) => void
  onSetD8Closure: (dayIdx: number, text: string | null) => void
  onSetSazltClosure: (dayIdx: number, text: string | null) => void
  scrollRef: React.RefObject<HTMLDivElement | null>
  showMetro: boolean
  showD8: boolean
  showSazlt: boolean
  hide162: boolean
  hiddenValues: Set<string>
  onContextMenu: (dayIdx: number, personId: string | null, x: number, y: number, selectedCells: SelectedCell[]) => void
  onTunnelContextMenu: (dayIdx: number, x: number, y: number) => void
  onInfoRowContextMenu: (dayIdx: number, infoRowId: string, x: number, y: number) => void
  compareData?: ScheduleData | null
}

interface EditingCell {
  personId: string | '__tunnel__' | '__metro__' | '__d8__' | '__sazlt__'
  dayIdx: number
  value: string
}

// ---------- PersonRow (memoized) ----------

const PersonRow = memo(function PersonRow({
  person,
  dayIndex,
  dragState,
  cellComments,
  dayComments,
  holidays,
  hiddenValues,
  onCellPointerDown,
  onContextMenu,
  editingCell,
  onStartEdit,
  onEditChange,
  onEditConfirm,
  onEditCancel,
  selectedCells,
  onCellClick,
  onCellDragSelectStart,
  onCellDragSelectMove,
  pmsAfterSet,
  pmsMonthAtPos,
}: {
  person: Person
  dayIndex: DayInfo[]
  dragState: DragState | null
  cellComments: Map<string, string>
  dayComments: Map<number, string>
  holidays: Map<string, string>
  hiddenValues: Set<string>
  onCellPointerDown: (e: React.PointerEvent, personId: string, dayIdx: number, value: string) => void
  onContextMenu: (dayIdx: number, personId: string | null, x: number, y: number) => void
  editingCell: EditingCell | null
  onStartEdit: (personId: string, dayIdx: number, currentValue: string) => void
  onEditChange: (value: string) => void
  onEditConfirm: () => void
  onEditCancel: () => void
  selectedCells: Set<string>
  onCellClick: (personId: string, dayIdx: number, ctrlKey: boolean, shiftKey: boolean) => void
  onCellDragSelectStart: (personId: string, dayIdx: number) => void
  onCellDragSelectMove: (personId: string, dayIdx: number) => void
  pmsAfterSet: Set<number>
  pmsMonthAtPos: Map<number, number>
}) {
  const isDragging = dragState?.personId === person.id

  return (
    <div className="flex border-b border-slate-700/50" style={{ height: CELL_H }}>
      {dayIndex.map((d, i) => {
        const cellData = person.data[String(d.idx)]
        const value = cellData?.v ?? ''
        const isColorOnly = !value && !!cellData?.color
        const isValueHidden = !!(value && hiddenValues.has(value)) || (isColorOnly && hiddenValues.has('__color_only__'))
        const style = getCellStyleForPerson(value || undefined, person.note)
        const isHoliday = holidays.has(`${d.year}-${d.month}-${d.day}`)
        const isWeekend = d.weekend || isHoliday
        const commentKey = `${person.id}-${d.idx}`
        const hasCellComment = cellComments.has(commentKey)
        const isMonthStart = d.day === 1

        const isDragOriginal = isDragging && dragState!.originalIdx === d.idx
        const isDragPreview = isDragging && dragState!.previewIdx === d.idx && dragState!.previewIdx !== dragState!.originalIdx

        const isEditing = editingCell?.personId === person.id && editingCell?.dayIdx === d.idx

        const bgStyle: React.CSSProperties = {}
        let className = `flex items-center justify-center text-sm font-mono font-bold relative
          border-r border-slate-700/20
          ${isMonthStart ? 'border-l-2 border-l-slate-500' : ''}`

        if (isDragOriginal) {
          className += ' opacity-30'
        }

        if (isDragPreview) {
          className += ' border-2 border-dashed border-amber-400/70'
        }

        if (isValueHidden) {
          // Hidden by filter — show as empty
          if (isWeekend) className += ' bg-slate-800/50'
        } else {
          const manualColor = cellData?.color
          if (manualColor) {
            bgStyle.backgroundColor = manualColor
            bgStyle.color = getContrastColor(manualColor)
          } else if (style) {
            bgStyle.backgroundColor = style.bg
            bgStyle.color = style.text
          } else if (isWeekend) {
            className += ' bg-slate-800/50'
          }
        }

        const isSelected = selectedCells.has(`${person.id}-${d.idx}`)
        if (isSelected) {
          className += ' ring-2 ring-blue-500 ring-inset z-10'
        }

        const hasCellData = !!(value || cellData?.color)
        if (hasCellData && !isDragOriginal) {
          className += ' cursor-grab active:cursor-grabbing'
        }

        const dayCell = (
          <div
            key={d.idx}
            className={className}
            style={{ width: CELL_W, height: CELL_H, ...bgStyle }}
            onPointerDown={(e) => {
              if (e.button === 2) return
              if (e.ctrlKey || e.metaKey || e.shiftKey) return // let onClick handle modifier clicks
              if (hasCellData) {
                onCellPointerDown(e, person.id, d.idx, value || '●')
              } else {
                // Start drag-select on empty cells
                onCellDragSelectStart(person.id, d.idx)
              }
            }}
            onPointerEnter={(e) => {
              if (e.buttons === 1 && !hasCellData) {
                onCellDragSelectMove(person.id, d.idx)
              }
            }}
            onClick={(e) => {
              if (e.button === 2) return
              if (dragState) return
              onCellClick(person.id, d.idx, e.ctrlKey || e.metaKey, e.shiftKey)
            }}
            onDoubleClick={() => {
              if (dragState) return
              onStartEdit(person.id, d.idx, value)
            }}
            onContextMenu={(e) => {
              e.preventDefault()
              onContextMenu(d.idx, person.id, e.clientX, e.clientY)
            }}
            title={
              `${person.name} — ${d.day}.${d.month}.${d.year}` +
              (value ? `\n${value}` : '') +
              (hasCellComment ? `\n💬 ${cellComments.get(commentKey)}` : '')
            }
          >
            {isEditing ? (
              <input
                autoFocus
                className="w-full h-full bg-white text-slate-900 text-center text-sm font-mono font-bold outline-none border-2 border-blue-500"
                style={{ width: CELL_W, height: CELL_H }}
                value={editingCell!.value}
                onChange={(e) => onEditChange(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter') onEditConfirm()
                  if (e.key === 'Escape') onEditCancel()
                }}
                onBlur={onEditConfirm}
                onClick={(e) => e.stopPropagation()}
                onPointerDown={(e) => e.stopPropagation()}
              />
            ) : (
              <>
                {isDragPreview ? (
                  <span className="truncate text-amber-300 font-semibold">{dragState!.value}</span>
                ) : value && !isValueHidden ? (
                  <span className="truncate font-medium">{value}</span>
                ) : null}
              </>
            )}
            {hasCellComment && !isEditing && (
              <span className="absolute top-0 right-0 w-0 h-0 pointer-events-none"
                style={{ borderLeft: '5px solid transparent', borderTop: '5px solid #3b82f6' }}
              />
            )}
          </div>
        )

        if (!pmsAfterSet.has(i)) return dayCell

        // PMS cell after this day (month boundary)
        const pmsMonth = pmsMonthAtPos.get(i)!
        const pmsDayIdx = -pmsMonth // negative month as special key
        const pmsData = person.data[String(pmsDayIdx)]
        const pmsValue = pmsData?.v ?? ''
        const pmsColor = pmsData?.color
        const isPmsEditing = editingCell?.personId === person.id && editingCell?.dayIdx === pmsDayIdx
        const pmsCommentKey = `${person.id}-${pmsDayIdx}`
        const hasPmsComment = cellComments.has(pmsCommentKey)
        const pmsStyle: React.CSSProperties = { width: CELL_W, height: CELL_H }
        if (pmsColor) {
          pmsStyle.backgroundColor = pmsColor
          pmsStyle.color = getContrastColor(pmsColor)
        }

        return [
          dayCell,
          <div
            key={`pms-${pmsMonth}`}
            className={`flex items-center justify-center text-sm font-mono font-bold relative
              border-r border-slate-600 cursor-text hover:brightness-125
              ${!pmsColor ? 'bg-slate-700/30' : ''}`}
            style={pmsStyle}
            onDoubleClick={() => onStartEdit(person.id, pmsDayIdx, pmsValue)}
            onClick={() => {
              if (!isPmsEditing) onStartEdit(person.id, pmsDayIdx, pmsValue)
            }}
            onContextMenu={(e) => {
              e.preventDefault()
              onContextMenu(pmsDayIdx, person.id, e.clientX, e.clientY)
            }}
            title={`PMS ${MONTH_NAMES_SHORT[pmsMonth] ?? pmsMonth}: ${pmsValue || '—'}${hasPmsComment ? `\n💬 ${cellComments.get(pmsCommentKey)}` : ''}`}
          >
            {isPmsEditing ? (
              <input
                autoFocus
                className="w-full h-full bg-white text-slate-900 text-center text-sm font-mono font-bold outline-none border-2 border-purple-500"
                style={{ width: CELL_W, height: CELL_H }}
                value={editingCell!.value}
                onChange={(e) => onEditChange(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter') onEditConfirm()
                  if (e.key === 'Escape') onEditCancel()
                }}
                onBlur={onEditConfirm}
                onClick={(e) => e.stopPropagation()}
                onPointerDown={(e) => e.stopPropagation()}
              />
            ) : (
              <span className="truncate font-medium">{pmsValue}</span>
            )}
            {hasPmsComment && !isPmsEditing && (
              <span className="absolute top-0 right-0 w-0 h-0 pointer-events-none"
                style={{ borderLeft: '5px solid transparent', borderTop: '5px solid #3b82f6' }}
              />
            )}
          </div>,
        ]
      })}
    </div>
  )
})

// ---------- Main ScheduleTable ----------

export function ScheduleTable(props: ScheduleTableProps) {
  const {
    dayIndex, people, tunnelClosures, tunnelColors,
    metroClosures, metroColors, d8Closures, d8Colors, sazltClosures, sazltColors,
    dayComments, cellComments,
    dragState, onCellPointerDown, onSetCell, onSetTunnelClosure,
    onSetMetroClosure, onSetD8Closure, onSetSazltClosure,
    showMetro, showD8, showSazlt, hide162, hiddenValues,
    scrollRef, onContextMenu, onTunnelContextMenu, onInfoRowContextMenu, compareData,
  } = props

  const [editingCell, setEditingCell] = useState<EditingCell | null>(null)
  const editInputRef = useRef<HTMLInputElement>(null)

  // ---------- Computed spans ----------

  const monthSpans = useMemo(() => {
    const spans: { month: number; year: number; startIdx: number; count: number }[] = []
    let current: (typeof spans)[0] | null = null
    for (let i = 0; i < dayIndex.length; i++) {
      const d = dayIndex[i]
      if (!current || current.month !== d.month || current.year !== d.year) {
        if (current) spans.push(current)
        current = { month: d.month, year: d.year, startIdx: i, count: 1 }
      } else {
        current.count++
      }
    }
    if (current) spans.push(current)
    return spans
  }, [dayIndex])

  const weekSpans = useMemo(() => {
    const spans: { week: number; startIdx: number; count: number }[] = []
    let current: (typeof spans)[0] | null = null
    for (let i = 0; i < dayIndex.length; i++) {
      const d = dayIndex[i]
      if (!current || current.week !== d.week) {
        if (current) spans.push(current)
        current = { week: d.week, startIdx: i, count: 1 }
      } else {
        current.count++
      }
    }
    if (current) spans.push(current)
    return spans
  }, [dayIndex])

  const dayNames = useMemo(() => {
    return dayIndex.map(d => {
      const dow = new Date(d.year, d.month - 1, d.day).getDay()
      return DAY_NAMES[dow]
    })
  }, [dayIndex])

  // Holiday map for all years in the data
  const holidays = useMemo(() => {
    const years = new Set(dayIndex.map(d => d.year))
    const combined = new Map<string, string>()
    for (const year of years) {
      const yearMap = getHolidayMap(year)
      yearMap.forEach((name, key) => combined.set(`${year}-${key}`, name))
    }
    return combined
  }, [dayIndex])

  const HIDE_162_NAMES = ['Pauzer Libor', 'Vörös Pavel', 'Janouš Petr', 'Franek Lukáš', 'Svoboda Daniel']
  const tkbPeople = useMemo(() => {
    const all = people.filter(p => p.group === 'TKB')
    return hide162 ? all.filter(p => !HIDE_162_NAMES.includes(p.name)) : all
  }, [people, hide162])
  const itPeople = useMemo(() => people.filter(p => p.group === 'IT'), [people])

  // ---------- PMS (Práce mimo směnu) month-end positions ----------

  const monthEndPositions = useMemo(() => {
    const positions: { afterDayPos: number; month: number; year: number }[] = []
    for (let i = 0; i < dayIndex.length - 1; i++) {
      if (dayIndex[i].month !== dayIndex[i + 1].month) {
        positions.push({ afterDayPos: i, month: dayIndex[i].month, year: dayIndex[i].year })
      }
    }
    const last = dayIndex[dayIndex.length - 1]
    if (last) positions.push({ afterDayPos: dayIndex.length - 1, month: last.month, year: last.year })
    return positions
  }, [dayIndex])

  // Set of dayIndex positions after which a PMS column appears
  const pmsAfterSet = useMemo(() => new Set(monthEndPositions.map(p => p.afterDayPos)), [monthEndPositions])

  // Map from dayIndex position to the month number for PMS
  const pmsMonthAtPos = useMemo(() => {
    const m = new Map<number, number>()
    for (const p of monthEndPositions) m.set(p.afterDayPos, p.month)
    return m
  }, [monthEndPositions])

  // ---------- idxToPos for drag overlay ----------

  // Maps dayIndex array position to column position (accounting for PMS columns)
  const dayPosToColPos = useMemo(() => {
    const arr: number[] = []
    let pmsCount = 0
    for (let i = 0; i < dayIndex.length; i++) {
      arr.push(i + pmsCount)
      if (pmsAfterSet.has(i)) pmsCount++
    }
    return arr
  }, [dayIndex, pmsAfterSet])

  // Maps dayIdx to column position for drag overlay
  const idxToPos = useMemo(() => {
    const map = new Map<number, number>()
    for (let i = 0; i < dayIndex.length; i++) {
      map.set(dayIndex[i].idx, dayPosToColPos[i])
    }
    return map
  }, [dayIndex, dayPosToColPos])

  // ---------- Editing handlers ----------

  const onStartEdit = useCallback((personId: string, dayIdx: number, currentValue: string) => {
    setEditingCell({ personId, dayIdx, value: currentValue })
  }, [])

  const onStartEditTunnel = useCallback((dayIdx: number, currentValue: string) => {
    setEditingCell({ personId: '__tunnel__', dayIdx, value: currentValue })
  }, [])

  const onStartEditMetro = useCallback((dayIdx: number, currentValue: string) => {
    setEditingCell({ personId: '__metro__', dayIdx, value: currentValue })
  }, [])

  const onStartEditD8 = useCallback((dayIdx: number, currentValue: string) => {
    setEditingCell({ personId: '__d8__', dayIdx, value: currentValue })
  }, [])

  const onStartEditSazlt = useCallback((dayIdx: number, currentValue: string) => {
    setEditingCell({ personId: '__sazlt__', dayIdx, value: currentValue })
  }, [])

  const onEditChange = useCallback((value: string) => {
    setEditingCell(prev => prev ? { ...prev, value } : null)
  }, [])

  const onEditConfirm = useCallback(() => {
    if (!editingCell) return
    const trimmed = editingCell.value.trim()
    if (editingCell.personId === '__tunnel__') {
      onSetTunnelClosure(editingCell.dayIdx, trimmed || null)
    } else if (editingCell.personId === '__metro__') {
      onSetMetroClosure(editingCell.dayIdx, trimmed || null)
    } else if (editingCell.personId === '__d8__') {
      onSetD8Closure(editingCell.dayIdx, trimmed || null)
    } else if (editingCell.personId === '__sazlt__') {
      onSetSazltClosure(editingCell.dayIdx, trimmed || null)
    } else {
      onSetCell(editingCell.personId, editingCell.dayIdx, trimmed || null)
    }
    setEditingCell(null)
  }, [editingCell, onSetCell, onSetTunnelClosure, onSetMetroClosure, onSetD8Closure, onSetSazltClosure])

  const onEditCancel = useCallback(() => {
    setEditingCell(null)
  }, [])

  // ---------- Multi-cell selection ----------

  const [selectedCells, setSelectedCells] = useState<Set<string>>(new Set())
  const selectionAnchorRef = useRef<{ personId: string; dayIdx: number } | null>(null)
  const isDragSelectingRef = useRef(false)
  const dragSelectStartRef = useRef<{ personId: string; dayIdx: number } | null>(null)

  const cellKey = (personId: string, dayIdx: number) => `${personId}-${dayIdx}`

  const computeRectSelection = useCallback((
    anchor: { personId: string; dayIdx: number },
    current: { personId: string; dayIdx: number },
  ): Set<string> => {
    const anchorPersonIdx = people.findIndex(p => p.id === anchor.personId)
    const currentPersonIdx = people.findIndex(p => p.id === current.personId)
    if (anchorPersonIdx < 0 || currentPersonIdx < 0) return new Set()

    const anchorDayPos = dayIndex.findIndex(d => d.idx === anchor.dayIdx)
    const currentDayPos = dayIndex.findIndex(d => d.idx === current.dayIdx)
    if (anchorDayPos < 0 || currentDayPos < 0) return new Set()

    const minP = Math.min(anchorPersonIdx, currentPersonIdx)
    const maxP = Math.max(anchorPersonIdx, currentPersonIdx)
    const minD = Math.min(anchorDayPos, currentDayPos)
    const maxD = Math.max(anchorDayPos, currentDayPos)

    const sel = new Set<string>()
    for (let pi = minP; pi <= maxP; pi++) {
      for (let di = minD; di <= maxD; di++) {
        sel.add(cellKey(people[pi].id, dayIndex[di].idx))
      }
    }
    return sel
  }, [people, dayIndex])

  const onCellClick = useCallback((personId: string, dayIdx: number, ctrlKey: boolean, shiftKey: boolean) => {
    const key = cellKey(personId, dayIdx)
    if (isDragSelectingRef.current) {
      isDragSelectingRef.current = false
      return
    }
    if (dragJustEndedRef.current) return

    if (shiftKey && selectionAnchorRef.current) {
      const rect = computeRectSelection(selectionAnchorRef.current, { personId, dayIdx })
      setSelectedCells(rect)
    } else if (ctrlKey) {
      setSelectedCells(prev => {
        const next = new Set(prev)
        if (next.has(key)) {
          next.delete(key)
        } else {
          next.add(key)
        }
        return next
      })
      selectionAnchorRef.current = { personId, dayIdx }
    } else {
      setSelectedCells(new Set([key]))
      selectionAnchorRef.current = { personId, dayIdx }
    }
  }, [computeRectSelection])

  const onCellDragSelectStart = useCallback((personId: string, dayIdx: number) => {
    dragSelectStartRef.current = { personId, dayIdx }
    isDragSelectingRef.current = false
    selectionAnchorRef.current = { personId, dayIdx }
    setSelectedCells(new Set([cellKey(personId, dayIdx)]))
  }, [])

  const onCellDragSelectMove = useCallback((personId: string, dayIdx: number) => {
    const start = dragSelectStartRef.current
    if (!start) return
    isDragSelectingRef.current = true
    const rect = computeRectSelection(start, { personId, dayIdx })
    setSelectedCells(rect)
  }, [computeRectSelection])

  // Finalize drag-select on pointerup
  useEffect(() => {
    const handleUp = () => {
      dragSelectStartRef.current = null
    }
    document.addEventListener('pointerup', handleUp)
    return () => document.removeEventListener('pointerup', handleUp)
  }, [])

  // Track when drag-move just completed so we can suppress the subsequent click
  const dragJustEndedRef = useRef(false)
  const prevDragRef = useRef<DragState | null>(null)
  useEffect(() => {
    if (prevDragRef.current && !dragState) {
      // Drag just ended — clear selection and set flag to suppress click
      setSelectedCells(new Set())
      selectionAnchorRef.current = null
      dragJustEndedRef.current = true
      setTimeout(() => { dragJustEndedRef.current = false }, 50)
    }
    prevDragRef.current = dragState
  }, [dragState])

  // Clear selection on Escape
  useEffect(() => {
    const handleKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        setSelectedCells(new Set())
        selectionAnchorRef.current = null
      }
    }
    document.addEventListener('keydown', handleKey)
    return () => document.removeEventListener('keydown', handleKey)
  }, [])

  // Context menu handler wrapping the prop to include selected cells
  const handlePersonContextMenu = useCallback((dayIdx: number, personId: string | null, x: number, y: number) => {
    if (!personId) {
      onContextMenu(dayIdx, personId, x, y, [])
      return
    }
    const key = cellKey(personId, dayIdx)
    if (selectedCells.has(key) && selectedCells.size > 1) {
      // Right-clicked on a selected cell => pass all selected cells
      const cells: SelectedCell[] = []
      selectedCells.forEach(k => {
        const lastDash = k.lastIndexOf('-')
        const pId = k.substring(0, lastDash)
        const dIdx = Number(k.substring(lastDash + 1))
        cells.push({ personId: pId, dayIdx: dIdx })
      })
      onContextMenu(dayIdx, personId, x, y, cells)
    } else {
      // Right-clicked on unselected cell => select just this one
      setSelectedCells(new Set([key]))
      selectionAnchorRef.current = { personId, dayIdx }
      onContextMenu(dayIdx, personId, x, y, [{ personId, dayIdx }])
    }
  }, [selectedCells, onContextMenu])

  // ---------- Drag overlay ----------

  const dragOverlay = useMemo(() => {
    if (!dragState) return null
    const personIdx = people.findIndex(p => p.id === dragState.personId)
    if (personIdx < 0) return null

    // Compute top accounting for section headers
    const tkbCount = tkbPeople.length
    let top: number
    const tkbIdx = tkbPeople.findIndex(p => p.id === dragState.personId)
    if (tkbIdx >= 0) {
      // TKB section: +1 for TKB section header row
      top = (1 + tkbIdx) * CELL_H
    } else {
      const itIdx = itPeople.findIndex(p => p.id === dragState.personId)
      // IT section: +1 TKB header + TKB rows + +1 IT header
      top = (1 + tkbCount + 1 + itIdx) * CELL_H
    }

    const pos = idxToPos.get(dragState.previewIdx) ?? 0
    const left = pos * CELL_W

    const style = getCellStyle(dragState.value)

    return (
      <div
        className="absolute border-2 border-dashed rounded pointer-events-none
                   flex items-center justify-center text-[10px] font-mono font-semibold"
        style={{
          left, top, width: CELL_W, height: CELL_H,
          borderColor: style?.bg ?? '#f59e0b',
          backgroundColor: (style?.bg ?? '#f59e0b') + '40',
          color: style?.text ?? '#fbbf24',
        }}
      >
        {dragState.value}
      </div>
    )
  }, [dragState, people, tkbPeople, itPeople, idxToPos])

  // ---------- Compare ghost overlay ----------

  const ghostBlocks = useMemo(() => {
    if (!compareData) return null
    const blocks: { personIdx: number; startPos: number; length: number; value: string }[] = []

    const compareDayByDate = new Map<string, number>()
    for (const cd of compareData.dayIndex) {
      compareDayByDate.set(`${cd.year}-${cd.month}-${cd.day}`, cd.idx)
    }

    const comparePersonMap = new Map<string, Record<string, { v?: string }>>()
    for (const cp of compareData.people) {
      comparePersonMap.set(cp.id, cp.data)
    }

    for (let pi = 0; pi < people.length; pi++) {
      const person = people[pi]
      const compareData2 = comparePersonMap.get(person.id)
      if (!compareData2) continue

      let runStart = -1
      let runValue = ''
      let runLength = 0
      let runHasDiff = false

      const flush = () => {
        if (runStart >= 0 && runHasDiff) {
          blocks.push({ personIdx: pi, startPos: runStart, length: runLength, value: runValue })
        }
        runStart = -1; runValue = ''; runLength = 0; runHasDiff = false
      }

      for (let di = 0; di < dayIndex.length; di++) {
        const d = dayIndex[di]
        const compIdx = compareDayByDate.get(`${d.year}-${d.month}-${d.day}`)
        const compCell = compIdx !== undefined ? compareData2[String(compIdx)] : undefined
        const compVal = compCell?.v ?? ''
        const curCell = person.data[String(d.idx)]
        const curVal = curCell?.v ?? ''
        const isDiff = compVal !== curVal

        if (compVal && compVal === runValue) {
          runLength++
          if (isDiff) runHasDiff = true
        } else {
          flush()
          if (compVal) {
            runStart = di
            runValue = compVal
            runLength = 1
            runHasDiff = isDiff
          }
        }
      }
      flush()
    }

    return blocks.length > 0 ? blocks : null
  }, [compareData, people, dayIndex])

  // ---------- Render ----------

  const totalGridW = dayIndex.length * CELL_W + monthEndPositions.length * CELL_W
  const nameColW = 200

  // Helper: render info row label (left column)
  const renderInfoRowLabel = (label: string, borderClass: string) => (
    <div className={`bg-slate-800 border-r border-slate-600 border-b ${borderClass} flex items-center px-3`} style={{ height: 28 }}>
      <span className="text-[10px] uppercase tracking-wider text-slate-500 font-semibold">{label}</span>
    </div>
  )

  // Helper: render info row cells (right column)
  const renderInfoRowCells = (
    rowId: '__tunnel__' | '__metro__' | '__d8__' | '__sazlt__',
    closures: Map<number, string>,
    colors: Map<number, string>,
    defaultBgClass: string,
    borderClass: string,
    editBorderColor: string,
    label: string,
    contextHandler: (dayIdx: number, x: number, y: number) => void,
    onStartEditFn: (dayIdx: number, currentValue: string) => void,
  ) => (
    <div className={`flex border-b ${borderClass}`} style={{ height: 28 }}>
      {dayIndex.map((d, i) => {
        const closureVal = closures.get(d.idx) ?? ''
        const closureColor = colors.get(d.idx)
        const isEditingThis = editingCell?.personId === rowId && editingCell?.dayIdx === d.idx
        const isOff = d.weekend || holidays.has(`${d.year}-${d.month}-${d.day}`)

        const rowBg: React.CSSProperties = {}
        if (closureColor) {
          rowBg.backgroundColor = closureColor
          rowBg.color = getContrastColor(closureColor)
        }

        const cells: React.ReactNode[] = []
        cells.push(
          <div
            key={d.idx}
            className={`flex items-center justify-center text-xs font-mono font-bold relative
              border-r border-slate-700/20
              ${!closureColor && closureVal ? `${defaultBgClass}` : !closureColor ? (isOff ? 'bg-slate-700/30' : 'bg-slate-800/30') : ''}
              ${d.day === 1 ? 'border-l-2 border-l-slate-500' : ''}
              cursor-pointer hover:brightness-125
            `}
            style={{ width: CELL_W, height: 28, ...rowBg }}
            onClick={() => {
              if (!isEditingThis) onStartEditFn(d.idx, closureVal)
            }}
            onContextMenu={(e) => {
              e.preventDefault()
              contextHandler(d.idx, e.clientX, e.clientY)
            }}
            title={closureVal ? `${label}: ${closureVal}` : `${d.day}.${d.month}.`}
          >
            {isEditingThis ? (
              <input
                autoFocus
                className={`w-full h-full bg-white text-slate-900 text-center text-xs font-mono font-bold outline-none border-2 ${editBorderColor}`}
                value={editingCell!.value}
                onChange={(e) => onEditChange(e.target.value)}
                onKeyDown={(e) => {
                  if (e.key === 'Enter') onEditConfirm()
                  if (e.key === 'Escape') onEditCancel()
                }}
                onBlur={onEditConfirm}
                onClick={(e) => e.stopPropagation()}
              />
            ) : (
              <span className="truncate">{closureVal}</span>
            )}
          </div>
        )
        if (pmsAfterSet.has(i)) {
          cells.push(
            <div key={`pms-info-${rowId}-${i}`} className="bg-slate-700/30" style={{ width: CELL_W, height: 28 }} />
          )
        }
        return cells
      })}
    </div>
  )

  // Helper: render section header (left column)
  const renderSectionHeaderLabel = (label: string, bgClass: string, textClass: string) => (
    <div className={`${bgClass} border-r border-slate-600 flex items-center px-3`} style={{ height: CELL_H }}>
      <span className={`text-xs font-bold ${textClass} uppercase tracking-wider`}>{label}</span>
    </div>
  )

  // Helper: render section header bar (right column)
  const renderSectionHeaderBar = (bgClass: string) => (
    <div className={`${bgClass}`} style={{ height: CELL_H, width: totalGridW }} />
  )

  return (
    <div className="rounded-lg border border-slate-700 bg-slate-800/50 overflow-hidden">
      <div className="flex">

        {/* === Fixed left name column === */}
        <div className="flex-shrink-0 z-20 bg-slate-800 border-r border-slate-600" style={{ width: nameColW }}>
          {/* Header labels */}
          <div className="bg-slate-800 border-b border-slate-700 flex items-center px-3" style={{ height: 28 }}>
            <span className="text-[10px] uppercase tracking-wider text-slate-500 font-semibold">Měsíc</span>
          </div>
          <div className="bg-slate-800 border-b border-slate-700 flex items-center px-3" style={{ height: 24 }}>
            <span className="text-[10px] uppercase tracking-wider text-slate-500 font-semibold">Týden</span>
          </div>
          <div className="bg-slate-800 border-b border-slate-700 flex items-center px-3" style={{ height: 28 }}>
            <span className="text-[10px] uppercase tracking-wider text-slate-500 font-semibold">Den</span>
          </div>
          <div className="bg-slate-800 border-b border-slate-700 flex items-center px-3" style={{ height: 24 }}>
            <span className="text-[10px] uppercase tracking-wider text-slate-500 font-semibold">Den t.</span>
          </div>

          {/* TKB info row label */}
          {renderInfoRowLabel('TKB', 'border-slate-700')}

          {/* SAZLT info row label */}
          {showSazlt && renderInfoRowLabel('SAZLT', 'border-slate-700')}

          {/* Metro info row label */}
          {showMetro && renderInfoRowLabel('Metro', 'border-slate-700')}

          {/* D8 info row label */}
          {showD8 && renderInfoRowLabel('D8', 'border-slate-600')}

          {/* TKB section header */}
          {renderSectionHeaderLabel('Pohotovost TKB', 'bg-indigo-700/60', 'text-indigo-100')}

          {/* TKB person names */}
          {tkbPeople.map((person) => (
            <div key={person.id} className="bg-slate-800 border-b border-slate-700 flex items-center px-3" style={{ height: CELL_H }}>
              <span className="text-xs text-slate-200 whitespace-nowrap truncate">
                {person.name}
                {person.note && <span className="text-slate-500 ml-1">({person.note})</span>}
              </span>
            </div>
          ))}

          {/* IT section header */}
          {renderSectionHeaderLabel('Pohotovost IT', 'bg-teal-700/60', 'text-teal-100')}

          {/* IT person names */}
          {itPeople.map((person) => (
            <div key={person.id} className="bg-slate-800 border-b border-slate-700 flex items-center px-3" style={{ height: CELL_H }}>
              <span className="text-xs text-slate-200 whitespace-nowrap truncate">
                {person.name}
                {person.note && <span className="text-slate-500 ml-1">({person.note})</span>}
              </span>
            </div>
          ))}
        </div>

        {/* === Scrollable grid === */}
        <div ref={scrollRef} className="overflow-x-auto flex-1">
          <div style={{ width: totalGridW, position: 'relative' }}>

            {/* Month header row */}
            <div className="flex border-b border-slate-700" style={{ height: 28 }}>
              {monthSpans.map(m => (
                <div
                  key={`${m.year}-${m.month}`}
                  className="flex items-center justify-center text-xs font-semibold text-slate-300 border-r border-slate-600 bg-slate-800"
                  style={{ width: (m.count + 1) * CELL_W }}
                >
                  {MONTH_NAMES[m.month]} {m.year}
                </div>
              ))}
            </div>

            {/* Week header row */}
            <div className="flex border-b border-slate-700" style={{ height: 24 }}>
              {weekSpans.map((w, i) => {
                // Check if any PMS column falls inside this week span
                const spanEnd = w.startIdx + w.count - 1
                const pmsCols: number[] = []
                for (let di = w.startIdx; di <= spanEnd; di++) {
                  if (pmsAfterSet.has(di)) pmsCols.push(di)
                }
                if (pmsCols.length === 0) {
                  return (
                    <div
                      key={`w-${i}`}
                      className="flex items-center justify-center text-[10px] text-slate-400 border-r border-slate-700/50 bg-slate-800/80"
                      style={{ width: w.count * CELL_W }}
                    >
                      {w.week}
                    </div>
                  )
                }
                // Split span around PMS columns
                const fragments: React.ReactNode[] = []
                let fragStart = w.startIdx
                for (const pmsPos of pmsCols) {
                  const fragCount = pmsPos - fragStart + 1
                  if (fragCount > 0) {
                    fragments.push(
                      <div
                        key={`w-${i}-f-${fragStart}`}
                        className="flex items-center justify-center text-[10px] text-slate-400 border-r border-slate-700/50 bg-slate-800/80"
                        style={{ width: fragCount * CELL_W }}
                      >
                        {w.week}
                      </div>
                    )
                  }
                  fragments.push(
                    <div key={`w-${i}-pms-${pmsPos}`} className="bg-slate-700/30" style={{ width: CELL_W, height: 24 }} />
                  )
                  fragStart = pmsPos + 1
                }
                // Remaining fragment after last PMS
                const remaining = spanEnd - fragStart + 1
                if (remaining > 0) {
                  fragments.push(
                    <div
                      key={`w-${i}-f-${fragStart}`}
                      className="flex items-center justify-center text-[10px] text-slate-400 border-r border-slate-700/50 bg-slate-800/80"
                      style={{ width: remaining * CELL_W }}
                    >
                      {w.week}
                    </div>
                  )
                }
                return <React.Fragment key={`w-${i}`}>{fragments}</React.Fragment>
              })}
            </div>

            {/* Day number header row (DEN) — PMS rotated text appears here */}
            <div className="flex border-b border-slate-700" style={{ height: 28 }}>
              {dayIndex.map((d, i) => {
                const comment = dayComments.get(d.idx)
                const holidayName = holidays.get(`${d.year}-${d.month}-${d.day}`)
                const isHoliday = !!holidayName
                const isOff = d.weekend || isHoliday
                const cells: React.ReactNode[] = []
                cells.push(
                  <div
                    key={d.idx}
                    className={`flex items-center justify-center text-[10px] font-mono relative
                      ${isOff ? 'text-red-400/70 bg-slate-700/30' : 'text-slate-400 bg-slate-800/50'}
                      ${d.day === 1 ? 'border-l-2 border-l-slate-500' : ''}
                    `}
                    style={{ width: CELL_W }}
                    title={`${d.day}.${d.month}.${d.year}${holidayName ? `\n🎉 ${holidayName}` : ''}${comment ? `\n${comment}` : ''}`}
                  >
                    {d.day}
                    {(comment || holidayName) && (
                      <span className={`absolute bottom-0.5 left-1/2 -translate-x-1/2 w-1.5 h-1.5 rounded-full pointer-events-none ${holidayName ? 'bg-red-400' : 'bg-blue-500'}`} />
                    )}
                  </div>
                )
                if (pmsAfterSet.has(i)) {
                  cells.push(
                    <div
                      key={`pms-den-${i}`}
                      className="relative"
                      style={{ width: CELL_W, height: 28, zIndex: 15 }}
                    >
                      {/* Merged cell overlay covering DEN + DEN T. + all info rows */}
                      <div
                        className="absolute flex items-center justify-center border-l border-r border-slate-600 overflow-hidden"
                        style={{
                          top: 0,
                          left: 0,
                          width: CELL_W,
                          height: 28 + 24 + 28 + (showSazlt ? 28 : 0) + (showMetro ? 28 : 0) + (showD8 ? 28 : 0),
                          backgroundColor: '#283040',
                          zIndex: 15,
                        }}
                      >
                        <span
                          className="text-[10px] font-bold text-slate-300 pointer-events-none select-none"
                          style={{
                            writingMode: 'vertical-rl',
                            transform: 'rotate(180deg)',
                          }}
                        >
                          Práce mimo směnu
                        </span>
                      </div>
                    </div>
                  )
                }
                return cells
              })}
            </div>

            {/* Day name header row */}
            <div className="flex border-b border-slate-700" style={{ height: 24 }}>
              {dayIndex.map((d, i) => {
                const isHoliday = holidays.has(`${d.year}-${d.month}-${d.day}`)
                const isOff = d.weekend || isHoliday
                const cells: React.ReactNode[] = []
                cells.push(
                  <div
                    key={d.idx}
                    className={`flex items-center justify-center text-[9px] font-mono
                      ${isOff ? 'text-red-400/70 bg-slate-700/30' : 'text-slate-500 bg-slate-800/50'}
                      ${d.day === 1 ? 'border-l-2 border-l-slate-500' : ''}
                    `}
                    style={{ width: CELL_W }}
                  >
                    {dayNames[i]}
                  </div>
                )
                if (pmsAfterSet.has(i)) {
                  cells.push(
                    <div key={`pms-dayname-${i}`} className="bg-slate-700/30" style={{ width: CELL_W, height: 24 }} />
                  )
                }
                return cells
              })}
            </div>

            {/* TKB info row */}
            {renderInfoRowCells('__tunnel__', tunnelClosures, tunnelColors,
              'bg-orange-700/40 text-orange-200', 'border-slate-700', 'border-orange-400', 'TKB',
              onTunnelContextMenu, onStartEditTunnel)}

            {/* SAZLT info row */}
            {showSazlt && renderInfoRowCells('__sazlt__', sazltClosures, sazltColors,
              'bg-amber-700/40 text-amber-200', 'border-slate-700', 'border-amber-400', 'SAZLT',
              (dayIdx, x, y) => onInfoRowContextMenu(dayIdx, 'sazlt', x, y), onStartEditSazlt)}

            {/* Metro info row */}
            {showMetro && renderInfoRowCells('__metro__', metroClosures, metroColors,
              'bg-blue-700/40 text-blue-200', 'border-slate-700', 'border-blue-400', 'Metro',
              (dayIdx, x, y) => onInfoRowContextMenu(dayIdx, 'metro', x, y), onStartEditMetro)}

            {/* D8 info row */}
            {showD8 && renderInfoRowCells('__d8__', d8Closures, d8Colors,
              'bg-green-700/40 text-green-200', 'border-slate-600', 'border-green-400', 'D8',
              (dayIdx, x, y) => onInfoRowContextMenu(dayIdx, 'd8', x, y), onStartEditD8)}

            {/* Data rows container (with overlay) */}
            <div style={{ position: 'relative' }}>

              {/* TKB section header bar */}
              {renderSectionHeaderBar('bg-indigo-700/60')}

              {/* TKB person rows */}
              {tkbPeople.map((person) => (
                <PersonRow
                  key={person.id}
                  person={person}
                  dayIndex={dayIndex}
                  dragState={dragState}
                  cellComments={cellComments}
                  dayComments={dayComments}
                  holidays={holidays}
                  hiddenValues={hiddenValues}
                  onCellPointerDown={onCellPointerDown}
                  onContextMenu={handlePersonContextMenu}
                  editingCell={editingCell}
                  onStartEdit={onStartEdit}
                  onEditChange={onEditChange}
                  onEditConfirm={onEditConfirm}
                  onEditCancel={onEditCancel}
                  selectedCells={selectedCells}
                  onCellClick={onCellClick}
                  onCellDragSelectStart={onCellDragSelectStart}
                  onCellDragSelectMove={onCellDragSelectMove}
                  pmsAfterSet={pmsAfterSet}
                  pmsMonthAtPos={pmsMonthAtPos}
                />
              ))}

              {/* IT section header bar */}
              {renderSectionHeaderBar('bg-teal-700/60')}

              {/* IT person rows */}
              {itPeople.map((person) => (
                <PersonRow
                  key={person.id}
                  person={person}
                  dayIndex={dayIndex}
                  dragState={dragState}
                  cellComments={cellComments}
                  dayComments={dayComments}
                  holidays={holidays}
                  hiddenValues={hiddenValues}
                  onCellPointerDown={onCellPointerDown}
                  onContextMenu={handlePersonContextMenu}
                  editingCell={editingCell}
                  onStartEdit={onStartEdit}
                  onEditChange={onEditChange}
                  onEditConfirm={onEditConfirm}
                  onEditCancel={onEditCancel}
                  selectedCells={selectedCells}
                  onCellClick={onCellClick}
                  onCellDragSelectStart={onCellDragSelectStart}
                  onCellDragSelectMove={onCellDragSelectMove}
                  pmsAfterSet={pmsAfterSet}
                  pmsMonthAtPos={pmsMonthAtPos}
                />
              ))}

              {/* Compare ghost blocks */}
              {ghostBlocks && ghostBlocks.map((gb, i) => {
                const colPos = dayPosToColPos[gb.startPos] ?? gb.startPos
                const left = colPos * CELL_W
                const tkbCount = tkbPeople.length
                let top: number
                if (gb.personIdx < tkbCount) {
                  top = (1 + gb.personIdx) * CELL_H
                } else {
                  top = (1 + tkbCount + 1 + (gb.personIdx - tkbCount)) * CELL_H
                }
                // Ghost block width: count columns between start and end, including any PMS columns in between
                const endDayPos = gb.startPos + gb.length - 1
                const endColPos = dayPosToColPos[endDayPos] ?? endDayPos
                const width = (endColPos - colPos + 1) * CELL_W
                const cellStyle = getCellStyle(gb.value)

                return (
                  <div
                    key={`ghost-${i}`}
                    className="absolute pointer-events-none flex items-center justify-center text-[10px] font-mono font-semibold"
                    style={{
                      left, top, width, height: CELL_H,
                      borderWidth: 2,
                      borderStyle: 'dashed',
                      borderRadius: 2,
                      borderColor: (cellStyle?.bg ?? '#64748b') + 'b3',
                      backgroundColor: (cellStyle?.bg ?? '#64748b') + '33',
                      color: (cellStyle?.text ?? '#94a3b8') + 'cc',
                    }}
                    title={`Bylo: ${gb.value} (${gb.length} dní)`}
                  >
                    {gb.value}
                  </div>
                )
              })}

              {/* Drag overlay */}
              {dragOverlay}
            </div>
          </div>
        </div>

      </div>
    </div>
  )
}
