// frontend/src/components/ui/DateTimePicker.jsx
//
// Two-column date+time picker popover.
// Left  — calendar with month/year navigation; click month-year header to enter year-scroll mode.
// Right — Cupertino-style drum dials for HH and mm.
//         • Drag up/down to scroll
//         • Mouse-wheel over the dial
//         • Click the selected (centre) item to type a value directly
//
// Props:
//   value       — ISO string or ''
//   onChange    — (isoString) => void
//   label       — string
//   name        — string
//   hint        — string
//   error       — string
//   disabled    — boolean
//   required    — boolean
//   placeholder — string (default 'DD/MM/YYYY HH:mm')

import { useState, useRef, useEffect, useCallback, useMemo } from 'react'
import { createPortal } from 'react-dom'

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

function pad(n) { return String(n).padStart(2, '0') }

function toDisplay(iso) {
  if (!iso) return ''
  const d = new Date(iso)
  if (isNaN(d.getTime())) return ''
  return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()}  ${pad(d.getHours())}:${pad(d.getMinutes())}`
}

function makeIso(year, month, day, hours, minutes) {
  const d = new Date(year, month, day, hours, minutes)
  return isNaN(d.getTime()) ? null : d.toISOString()
}

function buildGrid(year, month) {
  const firstDay    = new Date(year, month, 1).getDay()
  const startOffset = (firstDay + 6) % 7          // Mon-first
  const daysInMonth = new Date(year, month + 1, 0).getDate()
  const daysInPrev  = new Date(year, month,     0).getDate()
  const cells = []
  for (let i = startOffset - 1; i >= 0; i--)
    cells.push({ day: daysInPrev - i, month: month - 1, year: month === 0 ? year - 1 : year,  current: false })
  for (let d = 1; d <= daysInMonth; d++)
    cells.push({ day: d, month, year, current: true })
  const rem = cells.length % 7
  if (rem > 0)
    for (let d = 1; d <= 7 - rem; d++)
      cells.push({ day: d, month: month + 1, year: month === 11 ? year + 1 : year, current: false })
  return cells
}

const DAYS   = ['Mo','Tu','We','Th','Fr','Sa','Su']
const MONTHS = ['January','February','March','April','May','June',
                'July','August','September','October','November','December']
const MONTHS_SHORT = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']

// ─── Icons ───────────────────────────────────────────────────────────────────

const IconCalendar  = () => <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
const IconChevLeft  = () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6"/></svg>
const IconChevRight = () => <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 18 15 12 9 6"/></svg>

// ─── Cupertino Drum Dial ──────────────────────────────────────────────────────
// Shows 5 visible items centred on `value`. Supports drag, wheel, click-to-edit.

const ITEM_H    = 28   // px — height of each drum item
const VISIBLE   = 7    // odd number: centre item is selected
const HALF      = Math.floor(VISIBLE / 2)

function Drum({ value, max, onChange }) {
  // value: 0-based integer (0–23 for hours, 0–59 for minutes)
  const wrapAt   = max + 1   // 24 or 60
  const [editing, setEditing] = useState(false)
  const [editVal, setEditVal] = useState('')
  const inputRef  = useRef(null)
  const drumRef   = useRef(null)

  // Drag state
  const drag = useRef({ active: false, startY: 0, startVal: 0 })

  // Build the 5 visible items around current value
  const items = useMemo(() => {
    return Array.from({ length: VISIBLE }, (_, i) => {
      const offset = i - HALF
      const v = ((value + offset) % wrapAt + wrapAt) % wrapAt
      return { v, offset }
    })
  }, [value, wrapAt])

  // ── Drag ────────────────────────────────────────────────────────────────────
  function onMouseDown(e) {
    if (editing) return
    e.preventDefault()
    drag.current = { active: true, startY: e.clientY, startVal: value }
    window.addEventListener('mousemove', onMouseMove)
    window.addEventListener('mouseup',   onMouseUp)
  }

  function onMouseMove(e) {
    if (!drag.current.active) return
    const delta = Math.round((drag.current.startY - e.clientY) / ITEM_H)
    const next  = ((drag.current.startVal + delta) % wrapAt + wrapAt) % wrapAt
    onChange(next)
  }

  function onMouseUp() {
    drag.current.active = false
    window.removeEventListener('mousemove', onMouseMove)
    window.removeEventListener('mouseup',   onMouseUp)
  }

  // ── Wheel ───────────────────────────────────────────────────────────────────
  function onWheel(e) {
    e.preventDefault()
    e.stopPropagation()
    const dir  = e.deltaY > 0 ? 1 : -1
    const next = ((value + dir) % wrapAt + wrapAt) % wrapAt
    onChange(next)
  }

  // ── Click to edit ────────────────────────────────────────────────────────────
  function onClickCenter() {
    setEditVal(pad(value))
    setEditing(true)
    setTimeout(() => inputRef.current?.select(), 0)
  }

  function commitEdit() {
    const n = parseInt(editVal, 10)
    if (!isNaN(n) && n >= 0 && n < wrapAt) onChange(n)
    setEditing(false)
  }

  function onEditKey(e) {
    if (e.key === 'Enter') commitEdit()
    if (e.key === 'Escape') setEditing(false)
  }

  // ── Click on non-centre item ─────────────────────────────────────────────────
  function onClickItem(offset) {
    if (offset === 0) { onClickCenter(); return }
    const next = ((value + offset) % wrapAt + wrapAt) % wrapAt
    onChange(next)
  }

  // Opacity/scale per position
  const STYLE_BY_OFFSET = {
    '-3': { opacity: 0.10, fontSize: '0.64rem', fontWeight: 400 },
    '-2': { opacity: 0.22, fontSize: '0.72rem', fontWeight: 400 },
    '-1': { opacity: 0.50, fontSize: '0.85rem', fontWeight: 400 },
     '0': { opacity: 1,    fontSize: '1.1rem',  fontWeight: 700 },
     '1': { opacity: 0.50, fontSize: '0.85rem', fontWeight: 400 },
     '2': { opacity: 0.22, fontSize: '0.72rem', fontWeight: 400 },
     '3': { opacity: 0.10, fontSize: '0.64rem', fontWeight: 400 },
  }

  return (
    <div
      ref={drumRef}
      className="dtp-drum"
      onMouseDown={onMouseDown}
      onWheel={onWheel}
      style={{ height: ITEM_H * VISIBLE }}
    >
      {/* Selection highlight band */}
      <div className="dtp-drum-band" style={{ top: ITEM_H * HALF, height: ITEM_H }} />

      {/* Items */}
      {items.map(({ v, offset }) => {
        const s = STYLE_BY_OFFSET[String(offset)] || {}
        const isCenter = offset === 0
        return (
          <div
            key={offset}
            className={`dtp-drum-item${isCenter ? ' dtp-drum-item--center' : ''}`}
            style={{ height: ITEM_H, ...s }}
            onClick={() => onClickItem(offset)}
          >
            {isCenter && editing ? (
              <input
                ref={inputRef}
                className="dtp-drum-edit"
                value={editVal}
                onChange={e => setEditVal(e.target.value)}
                onBlur={commitEdit}
                onKeyDown={onEditKey}
                onClick={e => e.stopPropagation()}
                maxLength={2}
              />
            ) : (
              pad(v)
            )}
          </div>
        )
      })}

      {/* Top/bottom fade masks */}
      <div className="dtp-drum-fade dtp-drum-fade--top" />
      <div className="dtp-drum-fade dtp-drum-fade--bottom" />
    </div>
  )
}

// ─── Year Scroll Wheel ───────────────────────────────────────────────────────

function YearPicker({ year, onSelect }) {
  const listRef  = useRef(null)
  const MIN_YEAR = 1970
  const MAX_YEAR = new Date().getFullYear() + 10
  const years    = useMemo(() => {
    const arr = []
    for (let y = MIN_YEAR; y <= MAX_YEAR; y++) arr.push(y)
    return arr
  }, [])

  // Scroll selected year into view on mount
  useEffect(() => {
    const el = listRef.current?.querySelector('[data-selected="true"]')
    el?.scrollIntoView({ block: 'center', behavior: 'instant' })
  }, [])

  return (
    <div ref={listRef} className="dtp-year-list">
      {years.map(y => (
        <button
          key={y}
          type="button"
          data-selected={y === year}
          className={`dtp-year-item${y === year ? ' dtp-year-item--selected' : ''}`}
          onClick={() => onSelect(y)}
        >
          {y}
        </button>
      ))}
    </div>
  )
}

// ─── Popover ─────────────────────────────────────────────────────────────────

function Popover({ triggerRef, onClose, value, onChange }) {
  const now     = new Date()
  const initial = value ? new Date(value) : now

  const [viewYear,   setViewYear]   = useState(initial.getFullYear())
  const [viewMonth,  setViewMonth]  = useState(initial.getMonth())
  const [yearMode,   setYearMode]   = useState(false)   // true = year scroll wheel

  const [selYear,  setSelYear]  = useState(value ? initial.getFullYear() : null)
  const [selMonth, setSelMonth] = useState(value ? initial.getMonth()    : null)
  const [selDay,   setSelDay]   = useState(value ? initial.getDate()     : null)
  const [hours,    setHours]    = useState(initial.getHours())
  const [minutes,  setMinutes]  = useState(initial.getMinutes())

  const popRef = useRef(null)
  const [pos,  setPos]  = useState({ top: 0, left: 0 })

  // Position below trigger, flip up if would go off screen
  useEffect(() => {
    if (!triggerRef.current) return
    const r     = triggerRef.current.getBoundingClientRect()
    const panelH = 320
    const top   = r.bottom + 8 + panelH > window.innerHeight ? r.top - panelH - 8 : r.bottom + 8
    // Keep within viewport width
    const panelW = 520
    const left   = Math.min(r.left, window.innerWidth - panelW - 12)
    setPos({ top, left })
  }, [triggerRef])

  // Close on outside click / Escape
  useEffect(() => {
    function onDown(e) {
      if (popRef.current?.contains(e.target)) return
      if (triggerRef.current?.contains(e.target)) return
      onClose()
    }
    function onKey(e) { if (e.key === 'Escape') onClose() }
    document.addEventListener('mousedown', onDown)
    document.addEventListener('keydown',   onKey)
    return () => {
      document.removeEventListener('mousedown', onDown)
      document.removeEventListener('keydown',   onKey)
    }
  }, [onClose, triggerRef])

  function emit(sy, sm, sd, h, mi) {
    if (sy === null || sm === null || sd === null) return
    const iso = makeIso(sy, sm, sd, h, mi)
    if (iso) onChange(iso)
  }

  function selectDay(cell) {
    setSelYear(cell.year); setSelMonth(cell.month); setSelDay(cell.day)
    if (!cell.current) { setViewYear(cell.year); setViewMonth(cell.month) }
    emit(cell.year, cell.month, cell.day, hours, minutes)
  }

  function prevMonth() {
    if (viewMonth === 0) { setViewMonth(11); setViewYear(y => y - 1) }
    else setViewMonth(m => m - 1)
  }
  function nextMonth() {
    if (viewMonth === 11) { setViewMonth(0); setViewYear(y => y + 1) }
    else setViewMonth(m => m + 1)
  }

  function handleYearSelect(y) {
    setViewYear(y)
    setYearMode(false)
    // If a day was already selected in same month, update its year
    if (selDay !== null) {
      setSelYear(y)
      emit(y, selMonth ?? viewMonth, selDay, hours, minutes)
    }
  }

  function handleHours(h) {
    setHours(h)
    emit(selYear, selMonth, selDay, h, minutes)
  }
  function handleMinutes(mi) {
    setMinutes(mi)
    emit(selYear, selMonth, selDay, hours, mi)
  }

  function setNow() {
    const n = new Date()
    setViewYear(n.getFullYear()); setViewMonth(n.getMonth()); setYearMode(false)
    setSelYear(n.getFullYear()); setSelMonth(n.getMonth()); setSelDay(n.getDate())
    setHours(n.getHours()); setMinutes(n.getMinutes())
    onChange(makeIso(n.getFullYear(), n.getMonth(), n.getDate(), n.getHours(), n.getMinutes()))
  }

  const cells   = buildGrid(viewYear, viewMonth)
  const todayY  = now.getFullYear()
  const todayMo = now.getMonth()
  const todayD  = now.getDate()

  const displayDate = selDay !== null
    ? `${pad(selDay)} ${MONTHS_SHORT[selMonth]}  ${selYear}`
    : 'No date selected'

  return createPortal(
    <div
      ref={popRef}
      className="dtp-popover"
      style={{ top: pos.top, left: pos.left }}
      onMouseDown={e => e.stopPropagation()}
    >
      {/* ═══════════════════ TWO-COLUMN BODY ═══════════════════ */}
      <div className="dtp-body">

        {/* ── LEFT: Calendar ───────────────────────────────────── */}
        <div className="dtp-left">

          {/* Month/year nav */}
          <div className="dtp-cal-nav">
            {!yearMode && (
              <button type="button" className="dtp-nav-btn" onClick={prevMonth} aria-label="Previous month">
                <IconChevLeft />
              </button>
            )}
            <button
              type="button"
              className={`dtp-month-label${yearMode ? ' dtp-month-label--active' : ''}`}
              onClick={() => setYearMode(v => !v)}
              title={yearMode ? 'Back to calendar' : 'Pick a year'}
            >
              {MONTHS[viewMonth]} {viewYear}
              <span className="dtp-month-caret">{yearMode ? '▲' : '▼'}</span>
            </button>
            {!yearMode && (
              <button type="button" className="dtp-nav-btn" onClick={nextMonth} aria-label="Next month">
                <IconChevRight />
              </button>
            )}
          </div>

          {/* Calendar grid OR year picker */}
          {yearMode ? (
            <YearPicker year={viewYear} onSelect={handleYearSelect} />
          ) : (
            <div className="dtp-grid">
              {DAYS.map(d => <div key={d} className="dtp-dow">{d}</div>)}
              {cells.map((cell, i) => {
                const isSel   = selYear === cell.year && selMonth === cell.month && selDay === cell.day
                const isToday = todayY  === cell.year && todayMo  === cell.month && todayD === cell.day
                return (
                  <button
                    key={i}
                    type="button"
                    onClick={() => selectDay(cell)}
                    className={[
                      'dtp-day',
                      !cell.current        ? 'dtp-day--other'    : '',
                      isSel                ? 'dtp-day--selected'  : '',
                      isToday && !isSel    ? 'dtp-day--today'    : '',
                    ].filter(Boolean).join(' ')}
                    aria-pressed={isSel}
                  >
                    {cell.day}
                  </button>
                )
              })}
            </div>
          )}
        </div>

        {/* ── Divider ──────────────────────────────────────────── */}
        <div className="dtp-divider" />

        {/* ── RIGHT: Time dials ────────────────────────────────── */}
        <div className="dtp-right">
          <div className="dtp-time-label-row">
            <span className="dtp-section-label">Set time</span>
          </div>

          <div className="dtp-drums-wrap">
            <Drum value={hours}   max={23} onChange={handleHours}   />
            <span className="dtp-drums-sep">:</span>
            <Drum value={minutes} max={59} onChange={handleMinutes} />
          </div>
        </div>
      </div>

      {/* ═══════════════════ FOOTER ═══════════════════ */}
      <div className="dtp-footer">
        <div className="dtp-footer-left">
          <span className="dtp-footer-date">{displayDate}</span>
          <div className="dtp-footer-time">{pad(hours)}:{pad(minutes)}</div>
        </div>
        <div className="dtp-footer-actions">
          <button type="button" className="dtp-now-btn" onClick={setNow}>
            Set to Now
          </button>
          <button type="button" className="dtp-done-btn" onClick={onClose}>Done</button>
        </div>
      </div>
    </div>,
    document.body
  )
}

// ─── Main export ──────────────────────────────────────────────────────────────

export default function DateTimePicker({
  value        = '',
  onChange,
  label,
  name,
  hint,
  error,
  disabled     = false,
  required     = false,
  placeholder  = 'DD/MM/YYYY HH:mm',
}) {
  const [open, setOpen] = useState(false)
  const triggerRef      = useRef(null)
  const inputId         = `dtp-${name || 'field'}`
  const errorId         = error ? `${inputId}-error` : undefined
  const hintId          = hint  ? `${inputId}-hint`  : undefined

  const handleOpen  = useCallback(() => { if (!disabled) setOpen(true)  }, [disabled])
  const handleClose = useCallback(() => setOpen(false), [])
  const handleChange = useCallback((iso) => onChange?.(iso), [onChange])

  const display = toDisplay(value)

  return (
    <div className="flex flex-col" style={{ gap: 'var(--space-2)' }}>
      {label && (
        <label
          htmlFor={inputId}
          style={{ fontSize: 'var(--font-size-sm)', fontWeight: 'var(--font-weight-medium)', color: 'var(--color-text-secondary)', lineHeight: 'var(--line-height-tight)' }}
        >
          {label}
          {required && <span aria-hidden="true" style={{ color: 'var(--color-danger)', marginLeft: 'var(--space-1)' }}>*</span>}
        </label>
      )}

      <button
        ref={triggerRef}
        id={inputId}
        type="button"
        disabled={disabled}
        onClick={handleOpen}
        aria-haspopup="dialog"
        aria-expanded={open}
        aria-invalid={error ? true : undefined}
        aria-describedby={[errorId, hintId].filter(Boolean).join(' ') || undefined}
        className={['dtp-trigger', 'input', error ? 'input-error' : ''].filter(Boolean).join(' ')}
      >
        <span className={display ? 'dtp-trigger-value' : 'dtp-trigger-placeholder'}>
          {display || placeholder}
        </span>
        <span className="dtp-trigger-icon" aria-hidden="true"><IconCalendar /></span>
      </button>

      {open && (
        <Popover
          triggerRef={triggerRef}
          onClose={handleClose}
          value={value}
          onChange={handleChange}
        />
      )}

      {hint && !error && (
        <span id={hintId} style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-text-muted)' }}>{hint}</span>
      )}
      {error && (
        <span id={errorId} role="alert" style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-danger)', fontWeight: 'var(--font-weight-medium)' }}>{error}</span>
      )}
    </div>
  )
}
