import { LoadingSpinner } from "@ui/components/LoadingSpinner";
import type { KanbanTask, TaskStatus } from "@xtablo/shared-types";
import { Button } from "@xtablo/ui/components/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@xtablo/ui/components/dropdown-menu";
import {
  CalendarIcon,
  ChevronLeftIcon,
  ChevronRightIcon,
  Compass,
  Flame,
  FolderIcon,
  Gem,
  Heart,
  Leaf,
  MapIcon,
  Sparkles,
  Star,
  Sun,
  Waves,
  Zap,
} from "lucide-react";
import { useEffect, useMemo, useRef, useState } from "react";
import { twMerge } from "tailwind-merge";

// ─── Types ───────────────────────────────────────────────────────────────────

type GanttTask = KanbanTask & {
  tablos?: { id: string; name: string; color: string | null } | null;
};

interface GanttChartProps {
  tasks: GanttTask[];
  isLoading: boolean;
  onDateClick?: (date: Date) => void;
  onTaskStatusChange?: (taskId: string, status: TaskStatus) => void;
}

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

const STATUS_STYLES: Record<string, { bg: string; border: string; dot: string; label: string }> = {
  todo: {
    bg: "bg-[#EFF8FF]",
    border: "border-l-[#3B82F6]",
    dot: "bg-[#1570EF]",
    label: "À faire",
  },
  in_progress: {
    bg: "bg-[#FFF4E2]",
    border: "border-l-[#F1A62D]",
    dot: "bg-[#DB9729]",
    label: "En cours",
  },
  in_review: {
    bg: "bg-[#F4F3FF]",
    border: "border-l-[#804EEC]",
    dot: "bg-[#804EEC]",
    label: "Vérification",
  },
  done: {
    bg: "bg-[#EDFCF2]",
    border: "border-l-[#16B364]",
    dot: "bg-[#16B364]",
    label: "Terminé",
  },
};

const STATUS_TEXT_COLORS: Record<string, string> = {
  todo: "text-[#1570EF]",
  in_progress: "text-[#DB9729]",
  in_review: "text-[#804EEC]",
  done: "text-[#16B364]",
};

const ROADMAP_TASK_STATUSES: TaskStatus[] = ["todo", "in_progress", "in_review", "done"];

function getTabloIcon(color: string | null | undefined) {
  switch (color) {
    case "bg-blue-500":
      return Zap;
    case "bg-green-500":
      return Leaf;
    case "bg-purple-500":
      return Gem;
    case "bg-red-500":
      return Flame;
    case "bg-yellow-500":
      return Star;
    case "bg-indigo-500":
      return Compass;
    case "bg-pink-500":
      return Heart;
    case "bg-teal-500":
      return Waves;
    case "bg-orange-500":
      return Sun;
    case "bg-cyan-500":
      return Sparkles;
    default:
      return FolderIcon;
  }
}

function getMonday(date: Date): Date {
  const d = new Date(date);
  const day = d.getDay();
  const diff = d.getDate() - day + (day === 0 ? -6 : 1);
  d.setDate(diff);
  d.setHours(0, 0, 0, 0);
  return d;
}

function addDays(date: Date, n: number): Date {
  const d = new Date(date);
  d.setDate(d.getDate() + n);
  return d;
}

function isSameDay(a: Date, b: Date): boolean {
  return (
    a.getFullYear() === b.getFullYear() &&
    a.getMonth() === b.getMonth() &&
    a.getDate() === b.getDate()
  );
}

function formatShortDay(date: Date): string {
  return date.toLocaleDateString("fr-FR", { weekday: "short" }).replace(".", "");
}

function formatDateRange(start: Date, end: Date): string {
  const opts: Intl.DateTimeFormatOptions = { month: "long", day: "numeric" };
  const startStr = start.toLocaleDateString("fr-FR", opts);
  const endStr = end.toLocaleDateString("fr-FR", opts);
  return `${startStr} - ${endStr}`;
}

type ViewMode = "weekly" | "biweekly";

const VIEW_CONFIG = {
  weekly: { days: 7, label: "Semaine", cardHeight: 130, stepWeeks: 1 },
  biweekly: { days: 14, label: "2 semaines", cardHeight: 100, stepWeeks: 2 },
} as const;

const CARD_GAP = 10;
const CARD_TOP_OFFSET = 20;

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

export function GanttChart({ tasks, isLoading, onDateClick, onTaskStatusChange }: GanttChartProps) {
  const [weekOffset, setWeekOffset] = useState(0);
  const [viewMode, setViewMode] = useState<ViewMode>("weekly");
  const containerRef = useRef<HTMLDivElement>(null);
  const [containerWidth, setContainerWidth] = useState(992);

  const config = VIEW_CONFIG[viewMode];
  const numDays = config.days;
  const cardHeight = config.cardHeight;

  // Measure container width
  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const observer = new ResizeObserver((entries) => {
      for (const entry of entries) {
        setContainerWidth(Math.max(entry.contentRect.width, 992));
      }
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);

  const colWidth = containerWidth / numDays;

  // Compute days
  const today = useMemo(() => {
    const d = new Date();
    d.setHours(0, 0, 0, 0);
    return d;
  }, []);

  const periodStart = useMemo(() => addDays(getMonday(today), weekOffset * 7), [today, weekOffset]);
  const periodEnd = useMemo(() => addDays(periodStart, numDays - 1), [periodStart, numDays]);

  const days = useMemo(
    () => Array.from({ length: numDays }, (_, i) => addDays(periodStart, i)),
    [periodStart, numDays]
  );

  // Filter tasks with due_date in this period
  const visibleTasks = useMemo(() => {
    const start = periodStart.getTime();
    const end = addDays(periodStart, numDays).getTime();
    return tasks.filter((t) => {
      if (!t.due_date) return false;
      const d = new Date(t.due_date).getTime();
      return d >= start && d < end;
    });
  }, [tasks, periodStart, numDays]);

  // Position tasks: group by day column, stack vertically
  const positionedTasks = useMemo(() => {
    const columnSlots: number[][] = Array.from({ length: numDays }, () => []);

    return visibleTasks
      .map((task) => {
        const dueDate = new Date(task.due_date!);
        const dayIndex = days.findIndex((d) => isSameDay(d, dueDate));
        if (dayIndex === -1) return null;

        // Find first available row in this column
        const slots = columnSlots[dayIndex];
        let row = 0;
        while (slots.includes(row)) row++;
        slots.push(row);

        return {
          task,
          dayIndex,
          row,
          left: dayIndex * colWidth + 4,
          top: CARD_TOP_OFFSET + row * (cardHeight + CARD_GAP),
          width: colWidth - 8,
        };
      })
      .filter(Boolean) as Array<{
      task: GanttTask;
      dayIndex: number;
      row: number;
      left: number;
      top: number;
      width: number;
    }>;
  }, [visibleTasks, days, colWidth, numDays, cardHeight]);

  // Compute chart height
  const maxRow = positionedTasks.reduce((max, pt) => Math.max(max, pt.row), 0);
  const chartHeight = Math.max(400, (maxRow + 1) * (cardHeight + CARD_GAP) + CARD_TOP_OFFSET + 20);

  // Today indicator position
  const todayIndex = days.findIndex((d) => isSameDay(d, today));
  const todayInRange = todayIndex >= 0;

  const handleViewModeChange = (mode: ViewMode) => {
    setViewMode(mode);
    setWeekOffset(0);
  };

  if (isLoading) {
    return (
      <div className="flex items-center justify-center h-64">
        <LoadingSpinner />
      </div>
    );
  }

  return (
    <div className="w-full">
      {/* Navigation bar */}
      <div className="flex items-center justify-between mb-6">
        <div className="flex items-center gap-2">
          <Button
            variant="outline"
            size="icon"
            className="rounded-lg"
            onClick={() => setWeekOffset((w) => w - config.stepWeeks)}
          >
            <ChevronLeftIcon className="h-4 w-4" />
          </Button>
          <div className="px-4 py-2 bg-card dark:bg-gray-900 border border-border dark:border-gray-700 rounded-lg min-w-[200px] text-center">
            <span className="text-sm font-medium text-foreground">
              {formatDateRange(periodStart, periodEnd)}
            </span>
          </div>
          <Button
            variant="outline"
            size="icon"
            className="rounded-lg"
            onClick={() => setWeekOffset((w) => w + config.stepWeeks)}
          >
            <ChevronRightIcon className="h-4 w-4" />
          </Button>
        </div>

        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="outline" className="gap-2 rounded-lg">
              <CalendarIcon className="h-4 w-4" />
              <span>{config.label}</span>
              <ChevronRightIcon className="h-4 w-4 rotate-90" />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            <DropdownMenuItem onClick={() => handleViewModeChange("weekly")}>
              Semaine
            </DropdownMenuItem>
            <DropdownMenuItem onClick={() => handleViewModeChange("biweekly")}>
              2 semaines
            </DropdownMenuItem>
            <DropdownMenuItem onClick={() => setWeekOffset(0)}>
              Aller à aujourd'hui
            </DropdownMenuItem>
          </DropdownMenuContent>
        </DropdownMenu>
      </div>

      {/* Gantt chart */}
      <div
        className="bg-card dark:bg-gray-900 rounded-xl border border-border dark:border-gray-700 overflow-hidden shadow-sm"
        ref={containerRef}
      >
        <div className="overflow-x-auto">
          <div style={{ minWidth: 992 }}>
            {/* Day headers */}
            <div className="flex border-b border-border">
              {days.map((day, i) => {
                const isToday = isSameDay(day, today);
                return (
                  <button
                    key={i}
                    type="button"
                    onClick={() => onDateClick?.(new Date(day))}
                    className="flex flex-col items-center py-3 relative transition-colors hover:bg-accent/40"
                    style={{ width: colWidth }}
                  >
                    <span
                      className={twMerge(
                        "text-sm font-medium",
                        isToday ? "text-primary" : "text-muted-foreground"
                      )}
                    >
                      {formatShortDay(day)} {day.getDate()}
                    </span>
                  </button>
                );
              })}
            </div>

            {/* Grid body */}
            <div className="relative" style={{ height: chartHeight }}>
              {/* Clickable day columns */}
              <div className="absolute inset-0 flex z-0">
                {days.map((day, i) => (
                  <button
                    key={`click-${i}`}
                    type="button"
                    onClick={() => onDateClick?.(new Date(day))}
                    className="h-full transition-colors hover:bg-accent/20"
                    style={{ width: colWidth }}
                  />
                ))}
              </div>

              {/* Vertical grid lines */}
              <div className="absolute inset-0 flex pointer-events-none z-10">
                {days.map((_, i) => (
                  <div
                    key={i}
                    className={twMerge("border-border", i < numDays - 1 ? "border-r" : "")}
                    style={{ width: colWidth }}
                  />
                ))}
              </div>

              {/* Today indicator */}
              {todayInRange && (
                <div
                  className="absolute z-20 pointer-events-none"
                  style={{
                    left: (todayIndex + 0.5) * colWidth,
                    top: 0,
                    height: chartHeight,
                  }}
                >
                  <div className="absolute -top-1 left-1/2 -translate-x-1/2">
                    <div className="w-0 h-0 border-l-[8px] border-r-[8px] border-t-[10px] border-l-transparent border-r-transparent border-t-primary" />
                  </div>
                  <div className="w-0.5 h-full bg-primary mx-auto opacity-60" />
                </div>
              )}

              {/* Task cards */}
              {positionedTasks.map((pt) => {
                const status = STATUS_STYLES[pt.task.status ?? "todo"] ?? STATUS_STYLES.todo;
                const textColor =
                  STATUS_TEXT_COLORS[pt.task.status ?? "todo"] ?? STATUS_TEXT_COLORS.todo;
                const TabloIcon = pt.task.tablos ? getTabloIcon(pt.task.tablos.color) : null;

                const isCompact = viewMode === "biweekly";
                const taskCardContent = (
                  <>
                    {/* Status badge */}
                    <div className="flex items-center gap-1.5 bg-white w-fit px-2 py-0.5 rounded-full shadow-sm">
                      <span className={twMerge("w-2 h-2 rounded-full", status.dot)} />
                      {!isCompact && (
                        <span className={twMerge("text-xs font-medium", textColor)}>
                          {status.label}
                        </span>
                      )}
                    </div>

                    {/* Title */}
                    <h3
                      className={twMerge(
                        "font-semibold text-gray-900 leading-tight line-clamp-1",
                        isCompact ? "mt-1 text-xs" : "mt-2 text-sm"
                      )}
                    >
                      {pt.task.title}
                    </h3>

                    {/* Due date */}
                    <p className="text-xs text-muted-foreground mt-1">
                      {new Date(pt.task.due_date!).toLocaleDateString("fr-FR", {
                        weekday: "short",
                        day: "numeric",
                        month: isCompact ? undefined : "short",
                      })}
                    </p>

                    {/* Tablo badge — hidden in compact mode */}
                    {!isCompact && pt.task.tablos && TabloIcon && (
                      <div className="flex items-center gap-2 mt-2">
                        <div
                          className={twMerge(
                            "w-5 h-5 rounded-md flex items-center justify-center",
                            pt.task.tablos.color || "bg-gray-400"
                          )}
                        >
                          <TabloIcon className="w-3 h-3 text-white" />
                        </div>
                        <span className="text-xs text-muted-foreground truncate">
                          {pt.task.tablos.name}
                        </span>
                      </div>
                    )}
                  </>
                );

                if (!onTaskStatusChange) {
                  return (
                    <div
                      key={pt.task.id}
                      className={twMerge(
                        "absolute z-30 rounded-lg border-l-4 shadow-sm transition-all hover:shadow-md overflow-hidden text-left cursor-default",
                        isCompact ? "p-2" : "p-3",
                        status.bg,
                        status.border
                      )}
                      style={{
                        left: pt.left,
                        width: pt.width,
                        top: pt.top,
                        minWidth: isCompact ? 80 : 160,
                      }}
                    >
                      {taskCardContent}
                    </div>
                  );
                }

                return (
                  <DropdownMenu key={pt.task.id}>
                    <DropdownMenuTrigger asChild>
                      <button
                        type="button"
                        onClick={(e) => e.stopPropagation()}
                        className={twMerge(
                          "absolute z-30 rounded-lg border-l-4 shadow-sm transition-all hover:shadow-md overflow-hidden text-left",
                          isCompact ? "p-2" : "p-3",
                          status.bg,
                          status.border,
                          "cursor-pointer"
                        )}
                        style={{
                          left: pt.left,
                          width: pt.width,
                          top: pt.top,
                          minWidth: isCompact ? 80 : 160,
                        }}
                      >
                        {taskCardContent}
                      </button>
                    </DropdownMenuTrigger>
                    <DropdownMenuContent align="start" className="w-44">
                      {ROADMAP_TASK_STATUSES.map((nextStatus) => {
                        const isCurrent = nextStatus === pt.task.status;
                        return (
                          <DropdownMenuItem
                            key={nextStatus}
                            disabled={isCurrent}
                            onClick={() => onTaskStatusChange(pt.task.id, nextStatus)}
                            className="gap-2"
                          >
                            <span
                              className={twMerge(
                                "w-2 h-2 rounded-full",
                                STATUS_STYLES[nextStatus]?.dot ?? "bg-gray-400"
                              )}
                            />
                            <span>
                              {STATUS_STYLES[nextStatus]?.label ?? nextStatus}
                              {isCurrent ? " (actuel)" : ""}
                            </span>
                          </DropdownMenuItem>
                        );
                      })}
                    </DropdownMenuContent>
                  </DropdownMenu>
                );
              })}

              {/* Empty state within chart */}
              {positionedTasks.length === 0 && (
                <div className="absolute inset-0 flex flex-col items-center justify-center pointer-events-none z-10">
                  <MapIcon className="w-10 h-10 text-muted-foreground/30 mb-2" />
                  <p className="text-sm text-muted-foreground">
                    Aucune tâche avec échéance sur cette période
                  </p>
                </div>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}
