import { memo, useEffect, useLayoutEffect, useRef, useState, useMemo } from 'react';
import {
  ChartGantt,
  Sparkles,
  ChevronsDownUp,
  ChevronsUpDown,
  ChevronDown,
  Undo2,
  Redo2,
  Plus,
} from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
import { trpc } from '@/lib/trpc';
import { Button } from '@/components/ui/button';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Badge } from '@/components/ui/badge';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import {
  Empty,
  EmptyHeader,
  EmptyMedia,
  EmptyTitle,
  EmptyDescription,
} from '@/components/ui/empty';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { ProjectTimeline, GANTT_LABEL_WIDTH, type TimelineEvent } from './ProjectTimeline';
import { TimelineAxisHeader, HEADER_HEIGHT } from './TimelineAxisHeader';
import { type ProjectGroup } from './ProjectTimelineBox';
import { useFloatingChat } from '@/context/FloatingChatContext';

type ZoomLevel = 'day' | 'week' | 'month';
const COLUMN_PX = 32;
const DAY_PX_BY_ZOOM: Record<ZoomLevel, number> = {
  day: COLUMN_PX,
  week: COLUMN_PX / 7,
  month: COLUMN_PX / 30,
};
const ZOOM_LEVELS: ZoomLevel[] = ['day', 'week', 'month'];
const PAD_MS = 7 * 24 * 60 * 60 * 1000;

export interface TimelineGanttViewProps {
  projectGroups: ProjectGroup[];
  events: TimelineEvent[];
  canUndo: boolean;
  canRedo: boolean;
  onUndo: () => void;
  onRedo: () => void;
  onAdd: () => void;
  /** Extra controls after zoom tabs — render prop receives compact flag. */
  renderHeaderExtras?: (compact: boolean) => React.ReactNode;
  onToggleComplete: (id: string, current: number) => void;
  onDelete: (id: string) => void;
  onEdit: (ev: TimelineEvent) => void;
  onDuplicate: (ev: TimelineEvent) => void;
  onMove: (id: string, date: number, endDate: number | null) => void;
  sectionId: string;
  sectionLabel: string;
  projectId?: string;
  className?: string;
}

function TimelineGanttViewInner({
  projectGroups,
  events,
  canUndo,
  canRedo,
  onUndo,
  onRedo,
  onAdd,
  renderHeaderExtras,
  onToggleComplete,
  onDelete,
  onEdit,
  onDuplicate,
  onMove,
  sectionId,
  sectionLabel,
  projectId,
  className,
}: TimelineGanttViewProps) {
  const { t } = useTranslation();
  const [collapsed, setCollapsed] = useState<Set<string>>(new Set());
  const [zoomLevel, setZoomLevel] = useState<ZoomLevel>('day');

  const sectionRef = useRef<HTMLDivElement>(null);
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const [viewportWidth, setViewportWidth] = useState(0);
  const hasCenteredRef = useRef(false);

  const { data: savedZoom } = trpc.settings.getTimelineZoom.useQuery();
  const saveZoom = trpc.settings.setTimelineZoom.useMutation();

  const { registerSection, unregisterSection } = useFloatingChat();

  useEffect(() => {
    if (savedZoom && savedZoom !== zoomLevel) setZoomLevel(savedZoom as ZoomLevel);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [savedZoom]);

  function changeZoom(level: ZoomLevel) {
    setZoomLevel(level);
    saveZoom.mutate({ level });
  }

  useEffect(() => {
    registerSection({ id: sectionId, label: sectionLabel, ref: sectionRef, projectId });
    return () => unregisterSection(sectionId);
  }, [sectionId, sectionLabel, projectId, registerSection, unregisterSection]);

  useEffect(() => {
    const el = scrollContainerRef.current;
    if (!el) return;
    const observer = new ResizeObserver((entries) => {
      const entry = entries[0];
      if (entry) setViewportWidth(entry.contentRect.width);
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);

  const DAY_PX = DAY_PX_BY_ZOOM[zoomLevel];

  const minViewportDays = viewportWidth > 0
    ? Math.ceil(Math.max(0, viewportWidth - GANTT_LABEL_WIDTH) / DAY_PX)
    : 0;

  const globalRange = useMemo(() => {
    const ts = events.flatMap((e) => (e.endDate ? [e.date, e.endDate] : [e.date]));
    const now = Date.now();
    let startMs = ts.length ? Math.min(now, ...ts) - PAD_MS : now - 30 * 86_400_000;
    let endMs   = ts.length ? Math.max(now, ...ts) + PAD_MS : now + 60 * 86_400_000;
    const spanDays = (endMs - startMs) / 86_400_000;
    if (minViewportDays > spanDays) {
      const extraDays = Math.ceil(minViewportDays - spanDays);
      const halfLeft  = Math.ceil(extraDays / 2);
      const halfRight = extraDays - halfLeft;
      startMs -= halfLeft  * 86_400_000;
      endMs   += halfRight * 86_400_000;
    }
    const startDate = new Date(startMs);
    startDate.setHours(0, 0, 0, 0);
    const endDate = new Date(endMs);
    endDate.setHours(0, 0, 0, 0);
    endDate.setDate(endDate.getDate() + 1);
    return { startDate, endDate };
  }, [events, minViewportDays]);

  const timelineWidth = useMemo(() => {
    const totalDays = Math.ceil(
      (globalRange.endDate.getTime() - globalRange.startDate.getTime()) / 86_400_000,
    );
    return totalDays * DAY_PX;
  }, [globalRange, DAY_PX]);

  const gridLines = useMemo(() => {
    const lines: number[] = [];
    const startMs = globalRange.startDate.getTime();
    const endMs = globalRange.endDate.getTime();
    const totalDays = Math.round((endMs - startMs) / 86_400_000);
    if (zoomLevel === 'day') {
      for (let i = 1; i < totalDays; i++) lines.push(i * DAY_PX);
    } else if (zoomLevel === 'week') {
      const dow = globalRange.startDate.getDay();
      const daysToMonday = ((8 - dow) % 7) || 7;
      for (let dayIdx = daysToMonday; dayIdx < totalDays; dayIdx += 7) {
        lines.push(dayIdx * DAY_PX);
      }
    } else {
      const start = globalRange.startDate;
      let d = new Date(start.getFullYear(), start.getMonth() + 1, 1);
      while (d.getTime() < endMs) {
        const dayIdx = (d.getTime() - startMs) / 86_400_000;
        lines.push(dayIdx * DAY_PX);
        d = new Date(d.getFullYear(), d.getMonth() + 1, 1);
      }
    }
    return lines;
  }, [globalRange, zoomLevel, DAY_PX]);

  const weekendCells = useMemo(() => {
    if (zoomLevel !== 'day') return [] as { index: number }[];
    const totalDays = Math.ceil(
      (globalRange.endDate.getTime() - globalRange.startDate.getTime()) / 86_400_000,
    );
    const start = new Date(globalRange.startDate);
    start.setHours(0, 0, 0, 0);
    const cells: { index: number }[] = [];
    for (let i = 0; i < totalDays; i++) {
      const d = new Date(start.getTime() + i * 86_400_000);
      const dow = d.getDay();
      if (dow === 0 || dow === 6) cells.push({ index: i });
    }
    return cells;
  }, [globalRange, zoomLevel]);

  const todayLineX = useMemo(() => {
    const now = Date.now();
    const totalMs = globalRange.endDate.getTime() - globalRange.startDate.getTime();
    if (totalMs <= 0 || now < globalRange.startDate.getTime() || now > globalRange.endDate.getTime()) return null;
    return ((now - globalRange.startDate.getTime()) / totalMs) * timelineWidth;
  }, [globalRange, timelineWidth]);

  const hasEvents = events.length > 0;

  const allCollapsed =
    projectGroups.length > 0 &&
    projectGroups.every((g) => collapsed.has(g.projectId ?? '__no_project__'));

  function toggleAll() {
    if (allCollapsed) {
      setCollapsed(new Set());
    } else {
      setCollapsed(new Set(projectGroups.map((g) => g.projectId ?? '__no_project__')));
    }
  }

  function toggleOne(key: string, open: boolean) {
    setCollapsed((prev) => {
      const next = new Set(prev);
      if (open) next.delete(key);
      else next.add(key);
      return next;
    });
  }

  useLayoutEffect(() => {
    if (hasCenteredRef.current) return;
    const el = scrollContainerRef.current;
    if (!el || !hasEvents || todayLineX == null || viewportWidth === 0) return;
    el.scrollLeft = Math.max(0, GANTT_LABEL_WIDTH + todayLineX - el.clientWidth / 2);
    hasCenteredRef.current = true;
  }, [hasEvents, todayLineX, viewportWidth]);

  return (
    <div
      ref={sectionRef}
      data-ai-section={sectionId}
      className={cn('@container flex flex-col gap-4 w-full', className)}
    >
      {/* Header: Legend + Actions */}
      <div className="flex items-center min-w-0 shrink-0">

        {/* Legend — dots+text ≥800px, dots-only 440–799px, hidden <440px */}
        <div className="@max-[440px]:hidden flex items-center gap-3 text-xs text-muted-foreground shrink-0">
          <div className="flex items-center gap-1.5">
            <svg width={14} height={14}><circle cx={7} cy={7} r={5} fill="var(--primary)" /></svg>
            <span className="hidden @[800px]:inline">{t('timeline.toDo')}</span>
          </div>
          <div className="flex items-center gap-1.5">
            <svg width={14} height={14}><circle cx={7} cy={7} r={5} fill="var(--destructive)" /></svg>
            <span className="hidden @[800px]:inline">{t('timeline.overdue')}</span>
          </div>
          <div className="flex items-center gap-1.5">
            <svg width={14} height={14}><circle cx={7} cy={7} r={5} fill="var(--chart-2)" /></svg>
            <span className="hidden @[800px]:inline">{t('timeline.completed')}</span>
          </div>
          <div className="flex items-center gap-1.5">
            <Sparkles className="h-3.5 w-3.5" />
            <span className="hidden @[800px]:inline">{t('timeline.aiSuggested')}</span>
          </div>
        </div>

        {/* Actions */}
        <TooltipProvider>
          <div className="flex items-center gap-2 ml-auto">
            <Tooltip>
              <TooltipTrigger asChild>
                <Button
                  size="sm"
                  variant="ghost"
                  className="h-8 w-8 p-0"
                  disabled={!canUndo}
                  onClick={onUndo}
                >
                  <Undo2 className="size-3.5" />
                </Button>
              </TooltipTrigger>
              <TooltipContent>{t('timeline.undoTooltip')}</TooltipContent>
            </Tooltip>
            <Tooltip>
              <TooltipTrigger asChild>
                <Button
                  size="sm"
                  variant="ghost"
                  className="h-8 w-8 p-0"
                  disabled={!canRedo}
                  onClick={onRedo}
                >
                  <Redo2 className="size-3.5" />
                </Button>
              </TooltipTrigger>
              <TooltipContent>{t('timeline.redoTooltip')}</TooltipContent>
            </Tooltip>

            {/* Zoom: Tabs ≥440px, Select <440px */}
            <div className="@max-[440px]:hidden">
              <Tabs value={zoomLevel} onValueChange={(v) => changeZoom(v as ZoomLevel)}>
                <TabsList>
                  {ZOOM_LEVELS.map((level) => (
                    <TabsTrigger key={level} value={level}>
                      {t(`timeline.zoom${level.charAt(0).toUpperCase()}${level.slice(1)}`)}
                    </TabsTrigger>
                  ))}
                </TabsList>
              </Tabs>
            </div>
            <div className="@[440px]:hidden">
              <Select value={zoomLevel} onValueChange={(v) => changeZoom(v as ZoomLevel)}>
                <SelectTrigger size="sm" className="w-[7.5rem]">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  {ZOOM_LEVELS.map((level) => (
                    <SelectItem key={level} value={level}>
                      {t(`timeline.zoom${level.charAt(0).toUpperCase()}${level.slice(1)}`)}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>

            {/* renderHeaderExtras: wide ≥800px, compact <800px */}
            <div className="hidden @[800px]:block">{renderHeaderExtras?.(false)}</div>
            <div className="@[800px]:hidden">{renderHeaderExtras?.(true)}</div>

            {/* Add: text+icon ≥800px, icon-only <800px */}
            <div className="hidden @[800px]:flex">
              <Button size="sm" onClick={onAdd}>
                <Plus data-icon="inline-start" />
                {t('common.add')}
              </Button>
            </div>
            <div className="@[800px]:hidden">
              <Tooltip>
                <TooltipTrigger asChild>
                  <Button size="sm" className="h-8 w-8 p-0" onClick={onAdd}>
                    <Plus className="size-3.5" />
                  </Button>
                </TooltipTrigger>
                <TooltipContent>{t('timeline.addEvent')}</TooltipContent>
              </Tooltip>
            </div>
          </div>
        </TooltipProvider>
      </div>

      {/* Gantt card */}
      {!hasEvents ? (
        <Empty>
          <EmptyHeader>
            <EmptyMedia variant="icon"><ChartGantt /></EmptyMedia>
            <EmptyTitle>{t('timeline.noEventsTitle')}</EmptyTitle>
            <EmptyDescription>{t('timeline.noEventsDescription')}</EmptyDescription>
          </EmptyHeader>
        </Empty>
      ) : (
        <div className="flex-1 min-h-0 flex flex-col overflow-hidden">
          <div ref={scrollContainerRef} className="overflow-auto flex-1 min-h-0">
            <div className="relative" style={{ minWidth: GANTT_LABEL_WIDTH + timelineWidth }}>

              {/* Sticky calendar header */}
              <div className="flex sticky top-0 z-10 bg-background">
                <div
                  style={{ width: GANTT_LABEL_WIDTH, minWidth: GANTT_LABEL_WIDTH }}
                  className="sticky left-0 z-[11] bg-background shrink-0 flex items-center justify-end px-2"
                >
                  <Button
                    size="sm"
                    variant="ghost"
                    className="h-7 w-7 p-0"
                    onClick={toggleAll}
                    title={allCollapsed ? t('timeline.expandAll') : t('timeline.collapseAll')}
                  >
                    {allCollapsed
                      ? <ChevronsUpDown className="size-3.5" />
                      : <ChevronsDownUp className="size-3.5" />}
                  </Button>
                </div>
                <TimelineAxisHeader
                  startDate={globalRange.startDate}
                  endDate={globalRange.endDate}
                  fixedWidth={timelineWidth}
                  paddingX={0}
                  granularity={zoomLevel}
                />
              </div>

              {/* Grid lines aligned to current zoom unit */}
              {gridLines.map((x, i) => (
                <div
                  key={`gl-${i}`}
                  aria-hidden
                  className="absolute pointer-events-none"
                  style={{
                    left: GANTT_LABEL_WIDTH + x,
                    top: HEADER_HEIGHT,
                    bottom: 0,
                    width: 1,
                    background: 'color-mix(in srgb, var(--border) 40%, transparent)',
                    zIndex: 0,
                  }}
                />
              ))}

              {/* Weekend shading (day view only) */}
              {weekendCells.map(({ index }) => (
                <div
                  key={`we-${index}`}
                  aria-hidden
                  className="absolute pointer-events-none"
                  style={{
                    left: GANTT_LABEL_WIDTH + index * DAY_PX,
                    top: HEADER_HEIGHT,
                    bottom: 0,
                    width: DAY_PX,
                    background: 'color-mix(in srgb, var(--muted) 50%, transparent)',
                    zIndex: 0,
                  }}
                />
              ))}

              {/* Today line spanning full height */}
              {todayLineX != null && (
                <div
                  aria-hidden
                  className="absolute top-0 bottom-0 pointer-events-none z-[5]"
                  style={{
                    left: GANTT_LABEL_WIDTH + todayLineX,
                    width: 1.5,
                    background: 'var(--destructive)',
                    opacity: 0.7,
                  }}
                />
              )}

              {/* Project groups */}
              {projectGroups.map((group) => {
                const key = group.projectId ?? '__no_project__';
                return (
                  <Collapsible
                    key={key}
                    open={!collapsed.has(key)}
                    onOpenChange={(open) => toggleOne(key, open)}
                    defaultOpen
                  >
                    <CollapsibleTrigger asChild>
                      <button
                        type="button"
                        className="flex w-full items-center border-b bg-muted/20 hover:bg-muted/40 transition-colors h-9"
                      >
                        <div
                          style={{ width: GANTT_LABEL_WIDTH, minWidth: GANTT_LABEL_WIDTH }}
                          className="sticky left-0 z-[8] bg-background flex items-center gap-2 px-3 border-r h-full shrink-0"
                        >
                          <ChevronDown className="size-3.5 shrink-0 text-muted-foreground transition-transform duration-200 [[data-state=closed]_&]:-rotate-90" />
                          <span className="text-sm font-semibold truncate">{group.projectName}</span>
                          {group.projectStatus === 'archived' && (
                            <Badge variant="outline" className="text-[9px] ml-auto shrink-0">
                              Archived
                            </Badge>
                          )}
                        </div>
                        <div className="flex-1" style={{ minWidth: timelineWidth }} />
                      </button>
                    </CollapsibleTrigger>
                    <CollapsibleContent>
                      <ProjectTimeline
                        events={group.events}
                        startDate={globalRange.startDate}
                        endDate={globalRange.endDate}
                        timelineWidth={timelineWidth}
                        stickyLabels
                        onToggleComplete={onToggleComplete}
                        onDelete={onDelete}
                        onEdit={onEdit}
                        onDuplicate={onDuplicate}
                        onMove={onMove}
                      />
                    </CollapsibleContent>
                  </Collapsible>
                );
              })}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

export const TimelineGanttView = memo(TimelineGanttViewInner);
