import { useRef, useEffect, useState, useMemo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useFormatPrefs, formatDate } from '@/lib/date';
import { Sparkles, Pencil, Trash2, Check, Copy, CheckCircle2, Circle } from 'lucide-react';
import { cn } from '@/lib/utils';
import { Badge } from '@/components/ui/badge';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuItem,
  ContextMenuSeparator,
  ContextMenuTrigger,
} from '@/components/ui/context-menu';

// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------

export type TimelineEventType = 'milestone' | 'checkpoint' | 'activity';

export interface TimelineEvent {
  id: string;
  title: string;
  date: number;
  endDate: number | null;
  type: TimelineEventType;
  projectId: string | null;
  isCompleted: number;
  isAiSuggested: number;
}

export type EventVisualState = 'todo' | 'overdue' | 'completed';

interface ProjectTimelineProps {
  events: TimelineEvent[];
  startDate: Date;
  endDate: Date;
  /** When provided the component uses this fixed width instead of observing the container. */
  timelineWidth?: number;
  /** Stick the label column to the left edge (use inside an overflow-x-auto container). */
  stickyLabels?: boolean;
  onToggleComplete?: (id: string, currentCompleted: number) => void;
  onDelete?: (id: string) => void;
  onEdit?: (event: TimelineEvent) => void;
  onDuplicate?: (event: TimelineEvent) => void;
  onMove?: (id: string, date: number, endDate: number | null) => void;
}

// ---------------------------------------------------------------------------
// Constants — GANTT_LABEL_WIDTH is exported so callers can align the axis header
// ---------------------------------------------------------------------------

export const GANTT_LABEL_WIDTH = 160;

const ROW_HEIGHT = 28;
const BAR_HEIGHT = 24;
const BAR_ROUNDER = 12;
const DOT_R = 9;
const RESIZE_HIT = 8;

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function toTimelineX(date: number, start: Date, end: Date, tlWidth: number): number {
  const totalMs = end.getTime() - start.getTime();
  if (totalMs <= 0) return 0;
  return Math.max(0, ((date - start.getTime()) / totalMs) * tlWidth);
}

function snapToDay(ts: number): number {
  return Math.round(ts / 86_400_000) * 86_400_000;
}

export function getEventState(event: TimelineEvent): EventVisualState {
  if (event.isCompleted) return 'completed';
  const relevantDate = event.endDate ?? event.date;
  const todayStart = new Date();
  todayStart.setHours(0, 0, 0, 0);
  if (relevantDate < todayStart.getTime()) return 'overdue';
  return 'todo';
}

function getEventColor(state: EventVisualState): string {
  switch (state) {
    case 'completed': return 'var(--chart-2)';
    case 'overdue':   return 'var(--destructive)';
    case 'todo':      return 'var(--primary)';
  }
}

// ---------------------------------------------------------------------------
// Main component
// ---------------------------------------------------------------------------

export function ProjectTimeline({
  events, startDate, endDate,
  timelineWidth: propTimelineWidth, stickyLabels = false,
  onToggleComplete, onDelete, onEdit, onDuplicate, onMove,
}: ProjectTimelineProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [autoWidth, setAutoWidth] = useState(440);

  useEffect(() => {
    if (propTimelineWidth != null) return;
    const el = containerRef.current;
    if (!el) return;
    const obs = new ResizeObserver((entries) => {
      const entry = entries[0];
      if (entry) setAutoWidth(Math.max(entry.contentRect.width - GANTT_LABEL_WIDTH, 80));
    });
    obs.observe(el);
    return () => obs.disconnect();
  }, [propTimelineWidth]);

  const timelineWidth = propTimelineWidth ?? autoWidth;
  const sortedEvents = useMemo(() => [...events].sort((a, b) => a.date - b.date), [events]);

  const dayCenterX = useMemo(() => {
    const spanDays = (endDate.getTime() - startDate.getTime()) / 86_400_000;
    return (timelineWidth / Math.max(spanDays, 1)) / 2;
  }, [startDate, endDate, timelineWidth]);

  return (
    <div ref={containerRef} className="w-full">
      <div>
        {sortedEvents.map((event, i) => {
          const barLeft = toTimelineX(event.date, startDate, endDate, timelineWidth);
          const barRight = event.endDate != null
            ? toTimelineX(event.endDate, startDate, endDate, timelineWidth)
            : null;
          const barWidth = barRight != null ? Math.max(barRight - barLeft, 8) : null;
          const isOdd = i % 2 === 1;

          return (
            <div
              key={event.id}
              className="flex border-border/30"
              style={{ height: ROW_HEIGHT }}
            >
              {/* Label column */}
              <div
                style={{ width: GANTT_LABEL_WIDTH, minWidth: GANTT_LABEL_WIDTH }}
                className={cn(
                  'shrink-0 flex items-center gap-1 px-2 overflow-hidden border-r border-border/30',
                  stickyLabels && 'sticky left-0 z-[7] bg-background',
                )}
                title={event.title}
              >
                {event.isAiSuggested
                  ? <Sparkles className="h-2.5 w-2.5 shrink-0 text-muted-foreground" />
                  : null}
                <span className="text-xs text-foreground/80 truncate leading-none">
                  {event.title}
                </span>
              </div>

              {/* Timeline row */}
              <div
                className="relative flex-1"
                style={{
                  minWidth: timelineWidth,
                  height: ROW_HEIGHT,
                  // backgroundColor: isOdd ? 'color-mix(in srgb, var(--muted) 35%, transparent)' : undefined,
                }}
              >
                {/* Event shape */}
                {event.type === 'activity' && barWidth != null && (
                  <ActivityBar
                    event={event} barLeft={barLeft} barWidth={barWidth}
                    startDate={startDate} endDate={endDate} timelineWidth={timelineWidth}
                    onToggleComplete={onToggleComplete} onDelete={onDelete}
                    onEdit={onEdit} onDuplicate={onDuplicate} onMove={onMove}
                  />
                )}
                {event.type === 'checkpoint' && (
                  <CheckpointEvent
                    event={event} x={barLeft + dayCenterX}
                    startDate={startDate} endDate={endDate} timelineWidth={timelineWidth}
                    onToggleComplete={onToggleComplete} onDelete={onDelete}
                    onEdit={onEdit} onDuplicate={onDuplicate} onMove={onMove}
                  />
                )}
                {event.type !== 'activity' && event.type !== 'checkpoint' && (
                  <MilestoneEvent
                    event={event} x={barLeft + dayCenterX}
                    startDate={startDate} endDate={endDate} timelineWidth={timelineWidth}
                    onToggleComplete={onToggleComplete} onDelete={onDelete}
                    onEdit={onEdit} onDuplicate={onDuplicate} onMove={onMove}
                  />
                )}
              </div>
            </div>
          );
        })}
      </div>

    </div>
  );
}

// ---------------------------------------------------------------------------
// Shared event-shape props
// ---------------------------------------------------------------------------

interface EventShapeProps {
  event: TimelineEvent;
  startDate: Date;
  endDate: Date;
  timelineWidth: number;
  onToggleComplete?: (id: string, current: number) => void;
  onDelete?: (id: string) => void;
  onEdit?: (event: TimelineEvent) => void;
  onDuplicate?: (event: TimelineEvent) => void;
  onMove?: (id: string, date: number, endDate: number | null) => void;
}

// ---------------------------------------------------------------------------
// Drag hook
// ---------------------------------------------------------------------------

type DragMode = 'move' | 'resize-start' | 'resize-end';

function useDrag(
  event: TimelineEvent,
  startDate: Date,
  endDate: Date,
  timelineWidth: number,
  onMove?: (id: string, date: number, endDate: number | null) => void,
) {
  const dragRef = useRef<{
    mode: DragMode;
    startX: number;
    origDate: number;
    origEndDate: number | null;
    dragging: boolean;
  } | null>(null);

  const elRef = useRef<HTMLButtonElement>(null);

  const msPerPx = useCallback(
    () => (endDate.getTime() - startDate.getTime()) / timelineWidth,
    [startDate, endDate, timelineWidth],
  );

  const onPointerDown = useCallback(
    (e: React.PointerEvent<HTMLButtonElement>, mode: DragMode = 'move') => {
      if (!onMove) return;
      e.currentTarget.setPointerCapture(e.pointerId);
      dragRef.current = {
        mode, startX: e.clientX,
        origDate: event.date, origEndDate: event.endDate,
        dragging: false,
      };
    },
    [event.date, event.endDate, onMove],
  );

  const onPointerMove = useCallback(
    (e: React.PointerEvent<HTMLButtonElement>) => {
      const dr = dragRef.current;
      if (!dr || !onMove) return;
      const rawDx = e.clientX - dr.startX;
      if (Math.abs(rawDx) > 3) dr.dragging = true;
      if (!dr.dragging) return;
      const el = elRef.current;
      if (!el) return;

      const spanDays = (endDate.getTime() - startDate.getTime()) / 86_400_000;
      const pxPerDay = timelineWidth / Math.max(spanDays, 1);
      const snappedDx = Math.round(rawDx / pxPerDay) * pxPerDay;

      if (dr.mode === 'move') {
        el.style.transform = `translateX(${snappedDx}px)`;
      } else if (dr.mode === 'resize-end') {
        const origW =
          toTimelineX(dr.origEndDate ?? dr.origDate, startDate, endDate, timelineWidth) -
          toTimelineX(dr.origDate, startDate, endDate, timelineWidth);
        const bar = el.querySelector<HTMLElement>('[data-activity-bar]');
        if (bar) bar.style.width = `${Math.max(origW + snappedDx, pxPerDay)}px`;
      } else if (dr.mode === 'resize-start') {
        el.style.transform = `translateX(${snappedDx}px)`;
        const origW =
          toTimelineX(dr.origEndDate ?? dr.origDate, startDate, endDate, timelineWidth) -
          toTimelineX(dr.origDate, startDate, endDate, timelineWidth);
        const bar = el.querySelector<HTMLElement>('[data-activity-bar]');
        if (bar) bar.style.width = `${Math.max(origW - snappedDx, pxPerDay)}px`;
      }
    },
    [startDate, endDate, timelineWidth, onMove],
  );

  const onPointerUp = useCallback(
    (e: React.PointerEvent<HTMLButtonElement>) => {
      const dr = dragRef.current;
      dragRef.current = null;
      if (!dr || !onMove) return;
      if (!dr.dragging) return;

      const el = elRef.current;
      if (el) el.style.transform = '';

      const dx = e.clientX - dr.startX;
      const dMs = dx * msPerPx();
      let newDate = dr.origDate;
      let newEnd = dr.origEndDate;

      if (dr.mode === 'move') {
        newDate = snapToDay(dr.origDate + dMs);
        newEnd = dr.origEndDate != null ? snapToDay(dr.origEndDate + dMs) : null;
      } else if (dr.mode === 'resize-end' && dr.origEndDate != null) {
        newEnd = snapToDay(Math.max(dr.origEndDate + dMs, dr.origDate + 86_400_000));
      } else if (dr.mode === 'resize-start') {
        newDate = snapToDay(
          Math.min(dr.origDate + dMs, (dr.origEndDate ?? dr.origDate) - 86_400_000),
        );
      }
      onMove(event.id, newDate, newEnd);
    },
    [event.id, msPerPx, onMove],
  );

  return { elRef, onPointerDown, onPointerMove, onPointerUp, dragRef };
}

// ---------------------------------------------------------------------------
// Activity bar
// ---------------------------------------------------------------------------

function ActivityBar({
  event, barLeft, barWidth,
  startDate, endDate, timelineWidth,
  onToggleComplete, onDelete, onEdit, onDuplicate, onMove,
}: EventShapeProps & { barLeft: number; barWidth: number }) {
  const [hovered, setHovered] = useState(false);
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const onHoverIn = useCallback(() => {
    if (timer.current) clearTimeout(timer.current);
    timer.current = setTimeout(() => setHovered(true), 150);
  }, []);
  const onHoverOut = useCallback(() => {
    if (timer.current) clearTimeout(timer.current);
    timer.current = setTimeout(() => setHovered(false), 120);
  }, []);

  const { elRef, onPointerDown, onPointerMove, onPointerUp, dragRef } = useDrag(
    event, startDate, endDate, timelineWidth, onMove,
  );

  const state = getEventState(event);
  const color = getEventColor(state);
  const barTop = (ROW_HEIGHT - BAR_HEIGHT) / 2;
  const hitW = barWidth + RESIZE_HIT * 2;

  return (
    <ContextMenu>
      <Popover open={hovered}>
        <ContextMenuTrigger asChild>
          <PopoverTrigger asChild>
            <button
              ref={elRef}
              type="button"
              className="absolute focus:outline-none"
              style={{
                left: barLeft - RESIZE_HIT,
                top: 0,
                width: hitW,
                height: ROW_HEIGHT,
                cursor: onMove ? 'grab' : 'pointer',
              }}
              onMouseEnter={onHoverIn}
              onMouseLeave={onHoverOut}
              onPointerDown={(e) => {
                const rect = e.currentTarget.getBoundingClientRect();
                const relX = e.clientX - rect.left;
                if (relX <= RESIZE_HIT) onPointerDown(e, 'resize-start');
                else if (relX >= hitW - RESIZE_HIT) onPointerDown(e, 'resize-end');
                else onPointerDown(e, 'move');
              }}
              onPointerMove={onPointerMove}
              onPointerUp={onPointerUp}
            >
              <div
                data-activity-bar="true"
                style={{
                  position: 'absolute',
                  left: RESIZE_HIT,
                  top: barTop,
                  width: barWidth,
                  height: BAR_HEIGHT,
                  borderRadius: BAR_ROUNDER,
                  backgroundColor: color,
                  opacity: event.isCompleted ? 0.55 : 1,
                }}
              />
              <div style={{ position: 'absolute', left: 0, top: barTop, width: RESIZE_HIT, height: BAR_HEIGHT, cursor: 'ew-resize' }} />
              <div style={{ position: 'absolute', right: 0, top: barTop, width: RESIZE_HIT, height: BAR_HEIGHT, cursor: 'ew-resize' }} />
            </button>
          </PopoverTrigger>
        </ContextMenuTrigger>
        <PopoverContent
          className="w-56 p-3 pointer-events-none" side="top" sideOffset={4}
          onOpenAutoFocus={(e) => e.preventDefault()}
        >
          <EventTooltip event={event} state={state} />
        </PopoverContent>
      </Popover>
      <EventContextMenu event={event} onToggleComplete={onToggleComplete}
        onDuplicate={onDuplicate} onDelete={onDelete} onEdit={onEdit} />
    </ContextMenu>
  );
}

// ---------------------------------------------------------------------------
// Milestone (diamond)
// ---------------------------------------------------------------------------

function MilestoneEvent({
  event, x,
  startDate, endDate, timelineWidth,
  onToggleComplete, onDelete, onEdit, onDuplicate, onMove,
}: EventShapeProps & { x: number }) {
  const [hovered, setHovered] = useState(false);
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const onHoverIn = useCallback(() => {
    if (timer.current) clearTimeout(timer.current);
    timer.current = setTimeout(() => setHovered(true), 150);
  }, []);
  const onHoverOut = useCallback(() => {
    if (timer.current) clearTimeout(timer.current);
    timer.current = setTimeout(() => setHovered(false), 120);
  }, []);

  const { elRef, onPointerDown, onPointerMove, onPointerUp, dragRef } = useDrag(
    event, startDate, endDate, timelineWidth, onMove,
  );

  const state = getEventState(event);
  const color = getEventColor(state);
  const hitSize = DOT_R * 2 + 12;

  return (
    <ContextMenu>
      <Popover open={hovered}>
        <ContextMenuTrigger asChild>
          <PopoverTrigger asChild>
            <button
              ref={elRef}
              type="button"
              className="absolute flex items-center justify-center focus:outline-none"
              style={{
                left: x - hitSize / 2,
                top: (ROW_HEIGHT - hitSize) / 2,
                width: hitSize,
                height: hitSize,
                cursor: onMove ? 'grab' : 'pointer',
              }}
              onMouseEnter={onHoverIn}
              onMouseLeave={onHoverOut}
              onPointerDown={(e) => onPointerDown(e, 'move')}
              onPointerMove={onPointerMove}
              onPointerUp={onPointerUp}
            >
              <svg width={DOT_R * 2 + 2} height={DOT_R * 2 + 2}>
                <rect
                  x={1} y={1}
                  width={DOT_R * 2} height={DOT_R * 2}
                  rx={2}
                  fill={color}
                  opacity={event.isCompleted ? 0.55 : 1}
                  transform={`rotate(45, ${DOT_R + 1}, ${DOT_R + 1})`}
                />
                {event.isCompleted === 1 && (
                  <foreignObject x={DOT_R + 1 - 5} y={DOT_R + 1 - 5} width={10} height={10}>
                    <Check className="h-2.5 w-2.5 text-white" />
                  </foreignObject>
                )}
              </svg>
            </button>
          </PopoverTrigger>
        </ContextMenuTrigger>
        <PopoverContent
          className="w-52 p-3 pointer-events-none" side="top" sideOffset={4}
          onOpenAutoFocus={(e) => e.preventDefault()}
        >
          <EventTooltip event={event} state={state} />
        </PopoverContent>
      </Popover>
      <EventContextMenu event={event} onToggleComplete={onToggleComplete}
        onDuplicate={onDuplicate} onDelete={onDelete} onEdit={onEdit} />
    </ContextMenu>
  );
}

// ---------------------------------------------------------------------------
// Checkpoint (vertical flag line)
// ---------------------------------------------------------------------------

function CheckpointEvent({
  event, x,
  startDate, endDate, timelineWidth,
  onToggleComplete, onDelete, onEdit, onDuplicate, onMove,
}: EventShapeProps & { x: number }) {
  const [hovered, setHovered] = useState(false);
  const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
  const onHoverIn = useCallback(() => {
    if (timer.current) clearTimeout(timer.current);
    timer.current = setTimeout(() => setHovered(true), 150);
  }, []);
  const onHoverOut = useCallback(() => {
    if (timer.current) clearTimeout(timer.current);
    timer.current = setTimeout(() => setHovered(false), 120);
  }, []);

  const { elRef, onPointerDown, onPointerMove, onPointerUp, dragRef } = useDrag(
    event, startDate, endDate, timelineWidth, onMove,
  );

  const state = getEventState(event);
  const color = getEventColor(state);
  const hitW = 24;

  return (
    <ContextMenu>
      <Popover open={hovered}>
        <ContextMenuTrigger asChild>
          <PopoverTrigger asChild>
            <button
              ref={elRef}
              type="button"
              className="absolute focus:outline-none"
              style={{
                left: x - hitW / 2,
                top: 0,
                width: hitW,
                height: ROW_HEIGHT,
                cursor: onMove ? 'grab' : 'pointer',
              }}
              onMouseEnter={onHoverIn}
              onMouseLeave={onHoverOut}
              onPointerDown={(e) => onPointerDown(e, 'move')}
              onPointerMove={onPointerMove}
              onPointerUp={onPointerUp}
            >
              <svg
                width={hitW} height={ROW_HEIGHT}
                style={{ position: 'absolute', top: 0, left: 0, pointerEvents: 'none' }}
              >
                <line x1={hitW / 2} y1={4} x2={hitW / 2} y2={ROW_HEIGHT - 4}
                  stroke={color} strokeWidth={event.isCompleted ? 3 : 2} opacity={0.85} />
                <line x1={hitW / 2 - 6} y1={4} x2={hitW / 2 + 6} y2={4}
                  stroke={color} strokeWidth={2} />
                <line x1={hitW / 2 - 6} y1={ROW_HEIGHT - 4} x2={hitW / 2 + 6} y2={ROW_HEIGHT - 4}
                  stroke={color} strokeWidth={2} />
              </svg>
            </button>
          </PopoverTrigger>
        </ContextMenuTrigger>
        <PopoverContent
          className="w-52 p-3 pointer-events-none" side="top" sideOffset={4}
          onOpenAutoFocus={(e) => e.preventDefault()}
        >
          <EventTooltip event={event} state={state} />
        </PopoverContent>
      </Popover>
      <EventContextMenu event={event} onToggleComplete={onToggleComplete}
        onDuplicate={onDuplicate} onDelete={onDelete} onEdit={onEdit} />
    </ContextMenu>
  );
}

// ---------------------------------------------------------------------------
// Tooltip
// ---------------------------------------------------------------------------

function EventTooltip({ event, state }: { event: TimelineEvent; state: EventVisualState }) {
  const { t } = useTranslation();
  const prefs = useFormatPrefs();
  const stateLabel =
    state === 'completed' ? t('timeline.completed') : state === 'overdue' ? t('timeline.overdue') : t('timeline.toDo');
  const stateClass =
    state === 'completed' ? 'bg-chart-2/15 text-chart-2'
    : state === 'overdue'  ? 'bg-destructive/15 text-destructive'
    :                        'bg-primary/10 text-primary';
  const typeLabel =
    event.type === 'activity' ? t('timeline.typeActivity')
    : event.type === 'checkpoint' ? t('timeline.typeCheckpoint')
    : t('timeline.typeMilestone');

  return (
    <div className="flex flex-col gap-1.5">
      <div className="flex items-center justify-between gap-2">
        <span className="font-semibold text-sm leading-snug truncate">
          {event.isAiSuggested ? <Sparkles className="h-3 w-3 inline mr-1" /> : null}
          {event.title}
        </span>
        <Badge variant="secondary" className={`text-[10px] px-1.5 py-0 shrink-0 ${stateClass}`}>
          {stateLabel}
        </Badge>
      </div>
      <span className="text-xs text-muted-foreground">
        {formatDate(event.date, prefs)}
        {event.endDate ? ` — ${formatDate(event.endDate, prefs)}` : null}
      </span>
      <span className="text-[10px] text-muted-foreground/70">{typeLabel}</span>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Context menu
// ---------------------------------------------------------------------------

function EventContextMenu({
  event, onToggleComplete, onDuplicate, onDelete, onEdit,
}: {
  event: TimelineEvent;
  onToggleComplete?: (id: string, current: number) => void;
  onDuplicate?: (event: TimelineEvent) => void;
  onDelete?: (id: string) => void;
  onEdit?: (event: TimelineEvent) => void;
}) {
  const { t } = useTranslation();
  return (
    <ContextMenuContent>
      <ContextMenuItem onSelect={() => onToggleComplete?.(event.id, event.isCompleted)}>
        {event.isCompleted
          ? <Circle className="h-4 w-4 mr-2" />
          : <CheckCircle2 className="h-4 w-4 mr-2" />}
        {event.isCompleted ? t('timeline.markAsToDo') : t('timeline.markAsDone')}
      </ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem onSelect={() => onEdit?.(event)}>
        <Pencil className="h-4 w-4 mr-2" />
        {t('timeline.editEvent')}
      </ContextMenuItem>
      <ContextMenuItem onSelect={() => onDuplicate?.(event)}>
        <Copy className="h-4 w-4 mr-2" />
        {t('common.duplicate')}
      </ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem
        onSelect={() => onDelete?.(event.id)}
        className="text-destructive focus:text-destructive"
      >
        <Trash2 className="h-4 w-4 mr-2" />
        {t('timeline.deleteEvent')}
      </ContextMenuItem>
    </ContextMenuContent>
  );
}
