import { useMemo, useState } from 'react';
import { Calendar, ChevronLeft, ChevronRight, X } from 'lucide-react';
import { Popover } from './Popover';
import { cn } from '@/lib/cn';

/**
 * Brand-themed date + time picker. Replaces `<input type="datetime-local">`
 * specifically because the native popup is a browser-chrome widget that
 * doesn't honour `accent-color` (it lives outside the page's CSS scope) —
 * which leaves dates highlighted in iOS-blue even after page styling.
 *
 * Wire format: emits and accepts `YYYY-MM-DDTHH:mm` strings (the same
 * shape as the native `<input type="datetime-local">` it replaces). Empty
 * string clears the value. Designed as a drop-in for the Maintenance
 * Card's ETA field; keep this component small + self-contained so adding
 * it elsewhere stays cheap.
 *
 * Internally builds a 6-row × 7-col calendar grid via plain Date math
 * (no date-fns or other dependency). Days from the leading/trailing
 * adjacent months are rendered greyed-out so the grid stays a clean
 * rectangle no matter which weekday the 1st lands on.
 *
 * Time controls use a hidden-on-submit pattern: the user can type into
 * the hour/minute inputs or click + and − to step. Steps are 1 hour /
 * 5 minutes — fine-grained enough for an "ETA" use case without forcing
 * users to type when the rough time is already close.
 */
export function DateTimePicker({
  value,
  onChange,
  placeholder = 'Pick a date and time',
}: {
  value: string | null | undefined;
  onChange: (next: string) => void;
  placeholder?: string;
}) {
  // Parse the incoming value into a Date once; treat any malformed input
  // as "no value". Trailing seconds (which the native input doesn't emit
  // but the API sometimes does) are tolerated via slice.
  const parsed = useMemo(() => {
    if (!value) return null;
    const trimmed = (value || '').slice(0, 16);
    const d = new Date(trimmed);
    return isNaN(d.getTime()) ? null : d;
  }, [value]);

  // The month currently rendered in the calendar grid. Independent of the
  // selected date so the user can browse forward/back without losing their
  // pick. Defaults to the selected month if there is one, else today.
  const initial = parsed ?? new Date();
  const [viewYear, setViewYear]   = useState(initial.getFullYear());
  const [viewMonth, setViewMonth] = useState(initial.getMonth());

  // Time state — when no value yet, default to a reasonable "right now",
  // rounded down to the nearest 5 minutes so the displayed time and the
  // step-control increments line up.
  const fallbackHour = parsed?.getHours()   ?? new Date().getHours();
  const fallbackMin  = parsed?.getMinutes() ?? Math.floor(new Date().getMinutes() / 5) * 5;
  const [hour, setHour]   = useState(fallbackHour);
  const [minute, setMinute] = useState(fallbackMin);

  /** Format a Date into the `YYYY-MM-DDTHH:mm` string the input bus expects. */
  const toWireFormat = (d: Date): string => {
    const pad = (n: number) => String(n).padStart(2, '0');
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
  };

  /** Pretty display in the trigger button when a value is set. */
  const displayLabel = parsed
    ? parsed.toLocaleString(undefined, {
        year:   'numeric',
        month:  'short',
        day:    'numeric',
        hour:   'numeric',
        minute: '2-digit',
      })
    : placeholder;

  /** Emit a new value built from the picked day + current hour/minute. */
  const commit = (year: number, month: number, day: number, h = hour, m = minute) => {
    const d = new Date(year, month, day, h, m, 0, 0);
    onChange(toWireFormat(d));
  };

  /** Bump month by ±1 with year-wrap. */
  const stepMonth = (delta: number) => {
    let m = viewMonth + delta;
    let y = viewYear;
    if (m < 0)  { m = 11; y--; }
    if (m > 11) { m = 0;  y++; }
    setViewYear(y);
    setViewMonth(m);
  };

  // Build the 42-cell calendar grid. Always exactly 6 rows so the popover
  // doesn't reflow when navigating across months with different lengths.
  const grid = useMemo(() => {
    const first = new Date(viewYear, viewMonth, 1);
    const startWeekday = first.getDay(); // 0 = Sunday
    const startDate = new Date(viewYear, viewMonth, 1 - startWeekday);
    const cells: { date: Date; inMonth: boolean }[] = [];
    for (let i = 0; i < 42; i++) {
      const d = new Date(startDate);
      d.setDate(startDate.getDate() + i);
      cells.push({ date: d, inMonth: d.getMonth() === viewMonth });
    }
    return cells;
  }, [viewYear, viewMonth]);

  const today = new Date();
  const isSameDay = (a: Date, b: Date | null) =>
    !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();

  // Localised month name + day-name header.
  const monthLabel = new Date(viewYear, viewMonth, 1).toLocaleString(undefined, { month: 'long', year: 'numeric' });
  const dayHeaders = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];

  return (
    <Popover
      align="left"
      width={340}
      trigger={(open, toggle) => (
        <button
          type="button"
          onClick={toggle}
          className={cn(
            'h-9 w-full inline-flex items-center justify-between gap-2 rounded-lg border border-ink-200 bg-white px-3 text-sm text-left',
            'focus:outline-none focus:ring-2 focus:ring-brand-200 focus:border-brand-500 transition-colors',
            open && 'border-brand-500 ring-2 ring-brand-200',
            !parsed && 'text-ink-400',
          )}
        >
          <span className="flex items-center gap-2 min-w-0">
            <Calendar className="h-4 w-4 text-ink-500 shrink-0" />
            <span className="truncate">{displayLabel}</span>
          </span>
          {parsed && (
            <span
              role="button"
              aria-label="Clear date and time"
              title="Clear"
              onClick={(e) => { e.stopPropagation(); onChange(''); }}
              className="h-5 w-5 grid place-items-center rounded text-ink-400 hover:text-ink-700 hover:bg-ink-100 shrink-0"
            >
              <X className="h-3 w-3" />
            </span>
          )}
        </button>
      )}
    >
      {(close) => (
        <div className="p-3">
          {/* Month nav row */}
          <div className="flex items-center justify-between mb-2">
            <button
              type="button"
              onClick={() => stepMonth(-1)}
              aria-label="Previous month"
              className="h-7 w-7 grid place-items-center rounded-md text-ink-500 hover:bg-ink-100 hover:text-ink-900"
            >
              <ChevronLeft className="h-4 w-4" />
            </button>
            <p className="text-sm font-semibold text-ink-900 capitalize">{monthLabel}</p>
            <button
              type="button"
              onClick={() => stepMonth(1)}
              aria-label="Next month"
              className="h-7 w-7 grid place-items-center rounded-md text-ink-500 hover:bg-ink-100 hover:text-ink-900"
            >
              <ChevronRight className="h-4 w-4" />
            </button>
          </div>

          {/* Day-of-week header */}
          <div className="grid grid-cols-7 gap-1 mb-1">
            {dayHeaders.map((d, i) => (
              <div key={i} className="text-[10px] uppercase tracking-wider text-ink-500 font-medium text-center">
                {d}
              </div>
            ))}
          </div>

          {/* 42-cell calendar grid */}
          <div className="grid grid-cols-7 gap-1">
            {grid.map(({ date, inMonth }, i) => {
              const selected = isSameDay(date, parsed);
              const isToday  = isSameDay(date, today);
              return (
                <button
                  key={i}
                  type="button"
                  onClick={() => commit(date.getFullYear(), date.getMonth(), date.getDate())}
                  className={cn(
                    'h-8 w-full grid place-items-center rounded-md text-xs transition-colors',
                    !inMonth && 'text-ink-300',
                    inMonth && !selected && 'text-ink-800 hover:bg-ink-100',
                    selected && 'bg-brand-500 text-white font-semibold hover:bg-brand-600',
                    !selected && isToday && inMonth && 'ring-1 ring-inset ring-brand-300',
                  )}
                >
                  {date.getDate()}
                </button>
              );
            })}
          </div>

          {/* Time controls */}
          <div className="mt-3 pt-3 border-t border-ink-100 flex items-center gap-2">
            <span className="text-[11px] uppercase tracking-wider text-ink-500 font-medium">Time</span>
            <div className="flex items-center gap-1 ml-auto">
              <NumberStepper
                value={hour}
                min={0}
                max={23}
                step={1}
                pad
                onChange={(h) => {
                  setHour(h);
                  if (parsed) commit(parsed.getFullYear(), parsed.getMonth(), parsed.getDate(), h, minute);
                }}
              />
              <span className="text-ink-400">:</span>
              <NumberStepper
                value={minute}
                min={0}
                max={59}
                step={5}
                pad
                onChange={(m) => {
                  setMinute(m);
                  if (parsed) commit(parsed.getFullYear(), parsed.getMonth(), parsed.getDate(), hour, m);
                }}
              />
            </div>
          </div>

          {/* Footer actions */}
          <div className="mt-3 flex items-center justify-between gap-2">
            <button
              type="button"
              onClick={() => { onChange(''); close(); }}
              className="text-[11px] font-medium text-ink-500 hover:text-ink-800"
            >
              Clear
            </button>
            <div className="flex items-center gap-2">
              <button
                type="button"
                onClick={() => {
                  const t = new Date();
                  setViewYear(t.getFullYear());
                  setViewMonth(t.getMonth());
                  setHour(t.getHours());
                  setMinute(Math.floor(t.getMinutes() / 5) * 5);
                  commit(t.getFullYear(), t.getMonth(), t.getDate(), t.getHours(), Math.floor(t.getMinutes() / 5) * 5);
                }}
                className="text-[11px] font-medium text-brand-700 hover:text-brand-800"
              >
                Now
              </button>
              <button
                type="button"
                onClick={close}
                className="h-7 px-3 text-xs font-medium rounded-md bg-brand-500 text-white hover:bg-brand-600"
              >
                Done
              </button>
            </div>
          </div>
        </div>
      )}
    </Popover>
  );
}

/**
 * Inline numeric input with +/− step buttons. Used for hour/minute controls
 * in the time picker so the user can either type directly or click. `pad`
 * zero-pads single-digit values in the displayed input.
 */
function NumberStepper({
  value, min, max, step, onChange, pad,
}: {
  value: number;
  min: number;
  max: number;
  step: number;
  onChange: (next: number) => void;
  pad?: boolean;
}) {
  const clamp = (n: number) => Math.max(min, Math.min(max, n));
  const bump = (delta: number) => onChange(clamp(value + delta));
  return (
    <div className="inline-flex items-center rounded-md border border-ink-200 bg-white overflow-hidden">
      <button
        type="button"
        onClick={() => bump(-step)}
        aria-label="Decrement"
        className="h-7 w-6 grid place-items-center text-ink-500 hover:bg-ink-100 hover:text-ink-900"
      >
        −
      </button>
      <input
        type="text"
        inputMode="numeric"
        value={pad ? String(value).padStart(2, '0') : String(value)}
        onChange={(e) => {
          const parsed = parseInt(e.target.value.replace(/\D/g, ''), 10);
          if (Number.isFinite(parsed)) onChange(clamp(parsed));
          else onChange(min);
        }}
        className="h-7 w-10 text-center text-xs tabular-nums focus:outline-none focus:ring-2 focus:ring-brand-200"
      />
      <button
        type="button"
        onClick={() => bump(step)}
        aria-label="Increment"
        className="h-7 w-6 grid place-items-center text-ink-500 hover:bg-ink-100 hover:text-ink-900"
      >
        +
      </button>
    </div>
  );
}
