'use client'

import { useRef, memo } from'react'
import type { Assignment } from'@/lib/planning-types'
import { STATUS_COLORS } from'@/lib/planning-config'

// ── Constants ────────────────────────────────────────────────────────────────

const DRAG_THRESHOLD = 3 // px of movement before drag activates

// ── Helpers ───────────────────────────────────────────────────────────────────

function fmtDate(d: Date): string {
 return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`
}

// Re-applies a pre-drag scrollLeft against the calendar grid container at
// staggered checkpoints. Used by resize and drag handlers to keep the view
// parked across the loading flicker that follows a mutation. Idempotent
// (no-op when the position is already correct), so multiple passes don't
// fight each other. See KAR-30.
function restoreScrollAggressively(
 container: HTMLElement | null,
 savedScrollLeft: number | null,
): void {
 if (!container || savedScrollLeft == null || savedScrollLeft <= 0) return
 const restore = () => {
 if (container.scrollLeft !== savedScrollLeft) {
 container.scrollLeft = savedScrollLeft
 }
 }
 requestAnimationFrame(() => requestAnimationFrame(restore))
 setTimeout(restore, 100)
 setTimeout(restore, 300)
 setTimeout(restore, 600)
}

const SYNC_DOT: Record<string, string> = {
 pending:'#D97706',
 error:'#DC2626',
 conflict:'#7C3AED',
}

// ── Props ────────────────────────────────────────────────────────────────────

interface Props {
 /** All consecutive-span assignments — a0 = first, aLast = last. */
 assignments: Assignment[]
 color: string
 label: string
 /** Pixel width of one day column (= DAY_COL_W in planning-grid). */
 dayWidth: number
 /** 0-based index of the span start within the current month view. */
 startColumnIndex: number
 spanDays: number
 selectionMode: boolean
 selected: boolean
 syncStatus?: string
 onMove: (assignments: Assignment[], newStartDate: string, newConsultantId: string) => void
 onContextMenu: (assignment: Assignment, x: number, y: number) => void
 onClick: (assignment: Assignment) => void
 onCtrlClick: (id: string) => void
 onToggleSelect: (id: string) => void
 onTooltipShow: (
 assignment: Assignment,
 e: React.MouseEvent<HTMLDivElement>,
 startDate?: string,
 endDate?: string,
 ) => void
 onTooltipHide: () => void
}

// ── Component ─────────────────────────────────────────────────────────────────

function AssignmentTileInner({
 assignments, color, label, dayWidth, startColumnIndex, spanDays,
 selectionMode, selected, syncStatus,
 onMove, onContextMenu, onClick, onCtrlClick, onToggleSelect,
 onTooltipShow, onTooltipHide,
}: Props) {
 const tileRef = useRef<HTMLDivElement>(null)

 const a0 = assignments[0]
 const aLast = assignments[assignments.length - 1]
 const cancelled = a0.status ==='cancelled'

 const TILE_H = 30
 const tileLeft = startColumnIndex * dayWidth + 1
 const tileW = spanDays * dayWidth - 2

 // ── Right-click ─────────────────────────────────────────────────────────────

 function handleContextMenu(e: React.MouseEvent) {
 e.preventDefault()
 e.stopPropagation()
 onContextMenu(a0, e.clientX, e.clientY)
 }

 // ── Left mouse down — dispatch to drag (move) only ────────────────────────
 //
 // Resize via drag was removed (operator decision 2026-05-08): the user
 // resizes through the edit modal instead. handleMouseDown therefore goes
 // straight into startDrag — no edge detection, no resize handle.

 function handleMouseDown(e: React.MouseEvent) {
 if (e.button !== 0) return
 e.stopPropagation() // prevent grid's create-new handler from firing

 if (selectionMode) {
 if (e.ctrlKey || e.metaKey) onCtrlClick(a0.id)
 else onToggleSelect(a0.id)
 return
 }
 if (e.ctrlKey || e.metaKey) { onCtrlClick(a0.id); return }

 startDrag(e)
 }

 // ── Drag (move tile horizontally + optionally to another consultant row) ────

 function startDrag(e: React.MouseEvent) {
 e.preventDefault()
 onTooltipHide()

 const startX = e.clientX
 const startY = e.clientY
 const origRect = tileRef.current!.getBoundingClientRect()

 // Capture pre-drag scrollLeft (see startResize for rationale).
 const scrollContainer = document.getElementById('planning-grid-print')
 const savedScrollLeft = scrollContainer?.scrollLeft ?? null

 // Create ghost that follows the mouse across rows/columns
 const ghost = document.createElement('div')
 ghost.style.cssText = [
 `position:fixed`,
 `left:${origRect.left}px`,
 `top:${origRect.top}px`,
 `width:${origRect.width}px`,
 `height:${origRect.height}px`,
 `background:${color}`,
 `border-radius:4px`,
 `opacity:0.85`,
 `pointer-events:none`,
 `z-index:9999`,
 `font-size:9px`,
 `line-height:${origRect.height}px`,
 `padding:0 6px`,
 `color:white`,
 `overflow:hidden`,
 `white-space:nowrap`,
 `text-overflow:ellipsis`,
 `box-shadow:0 4px 12px rgba(0,0,0,.3)`,
 ].join(';')
 ghost.textContent = label
 document.body.appendChild(ghost)

 // Snap preview line — thin vertical indicator snapping to day boundaries
 const snapLine = document.createElement('div')
 snapLine.style.cssText = [
 `position:fixed`,
 `width:2px`,
 `top:0`,
 `bottom:0`,
 `background:#3B82F6`,
 `opacity:0`,
 `pointer-events:none`,
 `z-index:9998`,
 `transition:left 50ms ease`,
 ].join(';')
 document.body.appendChild(snapLine)

 if (tileRef.current) tileRef.current.style.opacity ='0.3'

 let moved = false

 function onMoveDoc(ev: MouseEvent) {
 const dx = ev.clientX - startX
 const dy = ev.clientY - startY
 if (!moved && Math.abs(dx) < DRAG_THRESHOLD && Math.abs(dy) < DRAG_THRESHOLD) return
 moved = true
 ghost.style.left = `${origRect.left + dx}px`
 ghost.style.top = `${origRect.top + dy}px`

 // Snap line: align to nearest day boundary relative to drag start
 const dayDelta = Math.round(dx / dayWidth)
 const snappedX = origRect.left + dayDelta * dayWidth
 snapLine.style.left = `${snappedX}px`
 snapLine.style.opacity ='0.6'
 }

 function onUpDoc(ev: MouseEvent) {
 document.removeEventListener('mousemove', onMoveDoc)
 document.removeEventListener('mouseup', onUpDoc)
 ghost.remove()
 snapLine.remove()
 if (tileRef.current) tileRef.current.style.opacity =''

 if (!moved) {
 // Short click — treat as select/open
 onClick(a0)
 return
 }

 const dx = ev.clientX - startX
 const dayDelta = Math.round(dx / dayWidth)

 // Ghost is gone from DOM; elementFromPoint sees the element under cursor
 const el = document.elementFromPoint(ev.clientX, ev.clientY) as HTMLElement | null
 const rowEl = el?.closest('[data-consultant-id]') as HTMLElement | null
 const newCid = rowEl?.dataset.consultantId ?? a0.consultant_id

 if (dayDelta === 0 && newCid === a0.consultant_id) {
 restoreScrollAggressively(scrollContainer, savedScrollLeft)
 return
 }

 const d = new Date(a0.date +'T00:00:00')
 d.setDate(d.getDate() + dayDelta)
 onMove(assignments, fmtDate(d), newCid)
 restoreScrollAggressively(scrollContainer, savedScrollLeft)
 }

 document.addEventListener('mousemove', onMoveDoc)
 document.addEventListener('mouseup', onUpDoc)
 }

 // Resize-via-drag was removed 2026-05-08. Range changes happen in the
 // edit modal now, which writes the new from/to dates and the parent
 // performs the same delete+insert pattern.

 // ── Render ───────────────────────────────────────────────────────────────────

 return (
 <div
 ref={tileRef}
 data-assignment-tile="true"
 onMouseDown={handleMouseDown}
 onContextMenu={handleContextMenu}
 onMouseEnter={(e) => onTooltipShow(a0, e, a0.date, aLast.date)}
 onMouseLeave={onTooltipHide}
 className="absolute rounded overflow-hidden"
 style={{
 left: tileLeft,
 width: tileW,
 height: TILE_H,
 top: 4,
 backgroundColor: color,
 opacity: cancelled ? 0.55 : 1,
 zIndex: 10,
 cursor: selectionMode ?'pointer':'grab',
 userSelect:'none',
 outline: selected ?'2px solid var(--primary)':'none',
 outlineOffset:'1px',
 fontSize:'9px',
 lineHeight: `${TILE_H}px`,
 paddingLeft: 3,
 paddingRight: 3,
 color:'#1C1C1C',
 boxSizing:'border-box',
 }}
 >
 {/* Label (only shown when wide enough) */}
 {spanDays >= 2 && (
 <span
 className="block truncate"
 style={{ textDecoration: cancelled ?'line-through':'none'}}
 >
 {label}
 </span>
 )}

 {/* Status + sync dots */}
 <span className="absolute top-0.5 right-1 flex gap-0.5"style={{ zIndex: 2 }}>
 <span
 className="w-1.5 h-1.5 rounded-full"
 style={{ backgroundColor: STATUS_COLORS[a0.status] }}
 />
 {syncStatus && SYNC_DOT[syncStatus] && (
 <span
 className="w-1.5 h-1.5 rounded-full"
 style={{ backgroundColor: SYNC_DOT[syncStatus] }}
 />
 )}
 </span>

 {/* Selection checkbox */}
 {selectionMode && (
 <span
 className={[
'absolute top-0.5 left-0.5 w-3 h-3 rounded border-2 flex items-center justify-center',
 selected ?'bg-primary border-primary':'bg-card/80 border-white/60',
 ].join('')}
 style={{ zIndex: 3 }}
 >
 {selected && (
 <svg viewBox="0 0 10 8"className="w-2 h-1.5 fill-white">
 <path d="M1 4l2.5 2.5L9 1"stroke="white"strokeWidth="1.5"fill="none"strokeLinecap="round"/>
 </svg>
 )}
 </span>
 )}
 </div>
 )
}

export default memo(AssignmentTileInner)
