"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
  DndContext,
  DragOverlay,
  KeyboardSensor,
  closestCenter,
  PointerSensor,
  useSensor,
  useSensors,
} from "@dnd-kit/core";
import {
  SortableContext,
  useSortable,
  verticalListSortingStrategy,
  arrayMove,
  sortableKeyboardCoordinates,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { useDroppable } from "@dnd-kit/core";
import { Icons } from "@turbostarter/ui-web/icons";
import { Input } from "@turbostarter/ui-web/input";
import { toast } from "sonner";
import { api } from "~/lib/api/client";
import { pathsConfig } from "~/config/paths";
import { diagramTypeConfig } from "../DiagramCard";
import { ProjectContextMenu } from "./ProjectContextMenu";

import type { DiagramResponse } from "../DiagramCard";
import type { DragStartEvent, DragEndEvent, DragOverEvent } from "@dnd-kit/core";

interface ProjectTreeProps {
  selectedProjectId: string | null;
  onSelectProject: (projectId: string | null) => void;
}

function SortableDiagramItem({
  diagram: d,
  onClick,
}: {
  diagram: DiagramResponse;
  onClick: () => void;
}) {
  const config = diagramTypeConfig[d.type];
  const TypeIcon = config.icon;
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } =
    useSortable({
      id: d.id,
      data: { type: "diagram", diagram: d, sourceProjectId: d.projectId },
    });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.4 : 1,
  };

  return (
    <button
      ref={setNodeRef}
      style={style}
      {...attributes}
      {...listeners}
      className="flex w-full items-center gap-2 rounded-md px-2 py-1 text-xs hover:bg-accent/50 cursor-grab active:cursor-grabbing"
      onClick={onClick}
    >
      <TypeIcon className={`h-3 w-3 shrink-0 ${config.color}`} />
      <span className="truncate">{d.title}</span>
    </button>
  );
}

function DroppableProject({
  projectId,
  isOver,
  children,
}: {
  projectId: string | null;
  isOver: boolean;
  children: React.ReactNode;
}) {
  const { setNodeRef } = useDroppable({
    id: `project-${projectId ?? "unorganized"}`,
    data: { type: "project", projectId },
  });

  return (
    <div
      ref={setNodeRef}
      className={isOver ? "rounded-md ring-2 ring-primary/50" : ""}
    >
      {children}
    </div>
  );
}

export function ProjectTree({ selectedProjectId, onSelectProject }: ProjectTreeProps) {
  const router = useRouter();
  const queryClient = useQueryClient();
  const [expandedProjects, setExpandedProjects] = useState<Set<string>>(new Set());
  const [renamingId, setRenamingId] = useState<string | null>(null);
  const [renameName, setRenameName] = useState("");
  const [activeDiagram, setActiveDiagram] = useState<DiagramResponse | null>(null);
  const [overProjectId, setOverProjectId] = useState<string | null | undefined>(undefined);

  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
  );

  const { data: projectsData } = useQuery({
    queryKey: ["projects"],
    queryFn: async () => {
      const res = await api.projects.$get();
      return await res.json();
    },
  });

  const { data: allDiagrams } = useQuery({
    queryKey: ["diagrams"],
    queryFn: async () => {
      const res = await api.diagrams.$get({ query: {} });
      return await res.json();
    },
  });

  const moveDiagramMutation = useMutation({
    mutationFn: async ({ id, projectId }: { id: string; projectId: string | null }) => {
      const res = await api.diagrams[":id"].$patch({
        param: { id },
        json: { projectId },
      });
      if (!res.ok) throw new Error("Failed to move diagram");
      return res.json();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["diagrams"] });
    },
    onError: () => {
      toast.error("Failed to move diagram");
      queryClient.invalidateQueries({ queryKey: ["diagrams"] });
    },
  });

  const reorderMutation = useMutation({
    mutationFn: async (items: { id: string; sortOrder: number }[]) => {
      const res = await api.diagrams.reorder.$post({ json: { items } });
      if (!res.ok) throw new Error("Failed to reorder");
      return res.json();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["diagrams"] });
    },
    onError: () => {
      toast.error("Failed to reorder diagrams");
      queryClient.invalidateQueries({ queryKey: ["diagrams"] });
    },
  });

  const renameMutation = useMutation({
    mutationFn: async ({ id, name }: { id: string; name: string }) => {
      const res = await api.projects[":id"].$patch({
        param: { id },
        json: { name },
      });
      return await res.json();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["projects"] });
      setRenamingId(null);
      toast.success("Project renamed");
    },
    onError: () => {
      toast.error("Failed to rename project");
    },
  });

  const projects = projectsData?.data ?? [];
  const diagrams = (allDiagrams?.data ?? []) as DiagramResponse[];

  const toggleExpand = (projectId: string) => {
    setExpandedProjects((prev) => {
      const next = new Set(prev);
      if (next.has(projectId)) next.delete(projectId);
      else next.add(projectId);
      return next;
    });
  };

  const getDiagramsForProject = (projectId: string) =>
    diagrams.filter((d) => d.projectId === projectId);

  const unorganizedDiagrams = diagrams.filter((d) => !d.projectId);

  const handleRename = (projectId: string, originalName: string) => {
    if (!renameName.trim() || renameName.trim() === originalName) {
      setRenamingId(null);
      return;
    }
    renameMutation.mutate({ id: projectId, name: renameName.trim() });
  };

  const itemClass = (isActive: boolean) =>
    `flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm cursor-pointer hover:bg-accent/50 ${
      isActive ? "bg-accent text-accent-foreground" : ""
    }`;

  function handleDragStart(event: DragStartEvent) {
    const data = event.active.data.current;
    if (data?.type === "diagram") {
      setActiveDiagram(data.diagram as DiagramResponse);
    }
  }

  function handleDragOver(event: DragOverEvent) {
    const over = event.over;
    if (!over) {
      setOverProjectId(undefined);
      return;
    }
    const overData = over.data.current;
    if (overData?.type === "project") {
      setOverProjectId(overData.projectId as string | null);
    } else if (overData?.type === "diagram") {
      setOverProjectId(
        (overData.diagram as DiagramResponse).projectId ?? undefined,
      );
    } else {
      setOverProjectId(undefined);
    }
  }

  function handleDragEnd(event: DragEndEvent) {
    setActiveDiagram(null);
    setOverProjectId(undefined);

    const { active, over } = event;
    if (!over || !active.data.current) return;

    const activeData = active.data.current;
    const overData = over.data.current;

    if (activeData.type !== "diagram") return;

    const draggedDiagram = activeData.diagram as DiagramResponse;
    const sourceProjectId = activeData.sourceProjectId as string | null;

    // Cross-project drag: dropped on a project droppable
    if (overData?.type === "project") {
      const targetProjectId = overData.projectId as string | null;
      if (sourceProjectId !== targetProjectId) {
        // Optimistic update
        queryClient.setQueryData(["diagrams"], (old: { data: DiagramResponse[] } | undefined) => {
          if (!old) return old;
          return {
            ...old,
            data: old.data.map((d) =>
              d.id === draggedDiagram.id ? { ...d, projectId: targetProjectId } : d,
            ),
          };
        });
        moveDiagramMutation.mutate({ id: draggedDiagram.id, projectId: targetProjectId });
      }
      return;
    }

    // Intra-project reorder: dropped on another diagram in same project
    if (overData?.type === "diagram") {
      const overDiagram = overData.diagram as DiagramResponse;
      const targetProjectId = overDiagram.projectId;

      if (sourceProjectId !== targetProjectId) {
        // Cross-project via dropping on diagram item
        queryClient.setQueryData(["diagrams"], (old: { data: DiagramResponse[] } | undefined) => {
          if (!old) return old;
          return {
            ...old,
            data: old.data.map((d) =>
              d.id === draggedDiagram.id ? { ...d, projectId: targetProjectId } : d,
            ),
          };
        });
        moveDiagramMutation.mutate({ id: draggedDiagram.id, projectId: targetProjectId });
        return;
      }

      // Same project reorder
      const projectDiagrams = diagrams.filter(
        (d) => d.projectId === sourceProjectId,
      );
      const oldIndex = projectDiagrams.findIndex((d) => d.id === active.id);
      const newIndex = projectDiagrams.findIndex((d) => d.id === over.id);

      if (oldIndex !== -1 && newIndex !== -1 && oldIndex !== newIndex) {
        const reordered = arrayMove(projectDiagrams, oldIndex, newIndex);
        const items = reordered.map((d, i) => ({ id: d.id, sortOrder: i }));

        // Optimistic update
        queryClient.setQueryData(["diagrams"], (old: { data: DiagramResponse[] } | undefined) => {
          if (!old) return old;
          const sortMap = new Map(items.map((item) => [item.id, item.sortOrder]));
          return {
            ...old,
            data: old.data.map((d) =>
              sortMap.has(d.id) ? { ...d, sortOrder: sortMap.get(d.id)! } : d,
            ),
          };
        });

        reorderMutation.mutate(items);
      }
    }
  }

  const dragOverlayDiagram = activeDiagram;

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragStart={handleDragStart}
      onDragOver={handleDragOver}
      onDragEnd={handleDragEnd}
    >
      <div className="space-y-1">
        {/* All Diagrams */}
        <button
          className={itemClass(selectedProjectId === null)}
          onClick={() => onSelectProject(null)}
        >
          <Icons.LayoutDashboard className="h-4 w-4 shrink-0" />
          <span className="truncate">All Diagrams</span>
          <span className="ml-auto text-xs text-muted-foreground">{diagrams.length}</span>
        </button>

        {/* Unorganized — droppable */}
        <DroppableProject
          projectId={null}
          isOver={overProjectId === null}
        >
          <button
            className={itemClass(selectedProjectId === "unorganized")}
            onClick={() => onSelectProject("unorganized")}
          >
            <Icons.Inbox className="h-4 w-4 shrink-0" />
            <span className="truncate">Unorganized</span>
            <span className="ml-auto text-xs text-muted-foreground">
              {unorganizedDiagrams.length}
            </span>
          </button>
        </DroppableProject>

        {/* Separator */}
        {projects.length > 0 && <div className="my-2 border-t" />}

        {/* Projects */}
        {projects.map((proj) => {
          const projectDiagrams = getDiagramsForProject(proj.id);
          const isExpanded = expandedProjects.has(proj.id);
          const isActive = selectedProjectId === proj.id;
          const isRenaming = renamingId === proj.id;

          return (
            <DroppableProject
              key={proj.id}
              projectId={proj.id}
              isOver={overProjectId === proj.id}
            >
              <div className="group flex items-center">
                <button
                  className="p-0.5 hover:bg-accent rounded"
                  onClick={(e) => {
                    e.stopPropagation();
                    toggleExpand(proj.id);
                  }}
                >
                  {isExpanded ? (
                    <Icons.ChevronDown className="h-3 w-3" />
                  ) : (
                    <Icons.ChevronRight className="h-3 w-3" />
                  )}
                </button>

                {isRenaming ? (
                  <div className="flex-1 px-1">
                    <Input
                      value={renameName}
                      onChange={(e) => setRenameName(e.target.value)}
                      onBlur={() => handleRename(proj.id, proj.name)}
                      onKeyDown={(e) => {
                        if (e.key === "Enter") handleRename(proj.id, proj.name);
                        if (e.key === "Escape") setRenamingId(null);
                      }}
                      className="h-7 text-xs"
                      autoFocus
                    />
                  </div>
                ) : (
                  <button
                    className={`${itemClass(isActive)} flex-1`}
                    onClick={() => onSelectProject(proj.id)}
                  >
                    <Icons.FolderOpen className="h-4 w-4 shrink-0" />
                    <span className="truncate">{proj.name}</span>
                    <span className="ml-auto text-xs text-muted-foreground">
                      {projectDiagrams.length}
                    </span>
                  </button>
                )}

                {!isRenaming && (
                  <ProjectContextMenu
                    project={proj}
                    onStartRename={() => {
                      setRenameName(proj.name);
                      setRenamingId(proj.id);
                    }}
                  />
                )}
              </div>

              {/* Expanded diagram list with sortable */}
              {isExpanded && projectDiagrams.length > 0 && (
                <div className="ml-6 space-y-0.5">
                  <SortableContext
                    items={projectDiagrams.map((d) => d.id)}
                    strategy={verticalListSortingStrategy}
                  >
                    {projectDiagrams.map((d) => (
                      <SortableDiagramItem
                        key={d.id}
                        diagram={d}
                        onClick={() =>
                          router.push(pathsConfig.dashboard.user.diagram(d.id))
                        }
                      />
                    ))}
                  </SortableContext>
                </div>
              )}
            </DroppableProject>
          );
        })}
      </div>

      {/* Drag overlay */}
      <DragOverlay>
        {dragOverlayDiagram ? (
          <div className="flex items-center gap-2 rounded-md bg-background/90 px-2 py-1 text-xs shadow-md ring-1 ring-border">
            {(() => {
              const config = diagramTypeConfig[dragOverlayDiagram.type];
              const TypeIcon = config.icon;
              return (
                <>
                  <TypeIcon className={`h-3 w-3 shrink-0 ${config.color}`} />
                  <span className="truncate">{dragOverlayDiagram.title}</span>
                </>
              );
            })()}
          </div>
        ) : null}
      </DragOverlay>
    </DndContext>
  );
}
