"use client";

import { useCallback, useMemo, type ReactNode } from "react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
import {
  ChevronRight,
  CircleDot,
  FileText,
  Folder,
  LayoutGrid,
  List as ListIcon,
  MoreHorizontal,
  PenTool,
  Plus,
} from "lucide-react";

import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { ScrollArea } from "@/components/ui/scroll-area";
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip";
import type { ObjectType } from "@tasks/shared";
import { api } from "@/lib/trpc";
import { isSidebarNodeExpanded, useSidebarStore } from "@/lib/stores/sidebar-store";
import { useWorkspaceStore } from "@/lib/stores/workspace-store";
import { cn } from "@/lib/utils";

/** Matches server `ObjectTreeNode` shape from objects.getTree */
export type TreeNodeData = {
  id: string;
  title: string;
  type: string;
  icon: string | null;
  parentId: string | null;
  childCount: number;
  children: TreeNodeData[];
};

export type PartitionedTrees = {
  projects: TreeNodeData[];
  documents: TreeNodeData[];
  whiteboards: TreeNodeData[];
};

export function partitionRoots(roots: TreeNodeData[]): PartitionedTrees {
  return {
    projects: roots,
    documents: [],
    whiteboards: [],
  };
}

const EMPTY_PARTITIONED: PartitionedTrees = {
  projects: [],
  documents: [],
  whiteboards: [],
};

const SPACE_COLORS = [
  "bg-amber-500",
  "bg-blue-500",
  "bg-emerald-500",
  "bg-purple-500",
  "bg-pink-500",
  "bg-red-500",
  "bg-cyan-500",
  "bg-orange-500",
];

function SpaceIcon({ node }: { node: TreeNodeData }) {
  if (node.icon) {
    return <span className="flex h-5 w-5 items-center justify-center text-sm">{node.icon}</span>;
  }
  const letter = (node.title || "S").charAt(0).toUpperCase();
  const colorIndex = node.title.length % SPACE_COLORS.length;
  const colorClass = SPACE_COLORS[colorIndex];
  return (
    <span
      className={cn(
        "flex h-5 w-5 items-center justify-center rounded text-[10px] font-bold text-white",
        colorClass,
      )}
    >
      {letter}
    </span>
  );
}

function TypeIcon({ type, node }: { type: string; node?: TreeNodeData }) {
  switch (type) {
    case "project":
    case "space":
      return node ? <SpaceIcon node={node} /> : <LayoutGrid className="size-4 text-muted-foreground" />;
    case "group":
      return <Folder className="size-4 text-purple-400/80" />;
    case "task":
      return <ListIcon className="size-4 text-muted-foreground" />;
    case "document":
      return <FileText className="size-4 text-blue-400/80" />;
    case "whiteboard":
      return <PenTool className="size-4 text-muted-foreground" />;
    default:
      return <CircleDot className="size-4 text-muted-foreground" />;
  }
}

function countNodes(roots: TreeNodeData[]): number {
  let n = 0;
  const walk = (nodes: TreeNodeData[]) => {
    for (const node of nodes) {
      n += 1;
      walk(node.children);
    }
  };
  walk(roots);
  return n;
}

function CollapsibleBody({
  open,
  children,
}: {
  open: boolean;
  children: ReactNode;
}) {
  return (
    <div
      className={cn(
        "grid transition-[grid-template-rows] duration-200 ease-out",
        open ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
      )}
    >
      <div className="overflow-hidden">{children}</div>
    </div>
  );
}

function showCreateChildActions(type: string): boolean {
  return type === "project" || type === "space" || type === "group";
}

function hrefForNode(base: string, nodeId: string, nodeType: string): string {
  switch (nodeType) {
    case "document":
      return `${base}/docs/${nodeId}`;
    case "whiteboard":
      return `${base}/whiteboards/${nodeId}`;
    default:
      return `${base}/${nodeId}`;
  }
}

function MoreMenuItems({
  node,
  href,
  workspaceId,
}: {
  node: TreeNodeData;
  href: string;
  workspaceId: string;
}) {
  const utils = api.useUtils();
  const archiveObj = api.objects.archive.useMutation({
    onSuccess: () => {
      void utils.objects.getTree.invalidate();
    },
  });
  const deleteObj = api.objects.delete.useMutation({
    onSuccess: () => {
      void utils.objects.getTree.invalidate();
    },
  });
  const duplicateObj = api.objects.create.useMutation({
    onSuccess: () => {
      void utils.objects.getTree.invalidate();
    },
  });
  const toggleFav = api.favorites.toggle.useMutation({
    onSuccess: () => {
      void utils.favorites.list.invalidate();
    },
  });

  return (
    <>
      <DropdownMenuItem
        onSelect={() => {
          toggleFav.mutate({ objectId: node.id });
        }}
      >
        Favorite
      </DropdownMenuItem>
      <DropdownMenuItem onSelect={() => { /* rename - complex, placeholder */ }}>Rename</DropdownMenuItem>
      <DropdownMenuItem
        onSelect={() => {
          void navigator.clipboard.writeText(window.location.origin + href);
        }}
      >
        Copy link
      </DropdownMenuItem>
      <DropdownMenuSeparator />
      <DropdownMenuItem disabled>Color & Icon</DropdownMenuItem>
      <DropdownMenuSeparator />
      <DropdownMenuItem
        onSelect={() => {
          duplicateObj.mutate({
            type: node.type as ObjectType,
            title: `${node.title} (copy)`,
            workspaceId,
            parentId: node.parentId ?? undefined,
          });
        }}
      >
        Duplicate
      </DropdownMenuItem>
      <DropdownMenuItem
        onSelect={() => {
          archiveObj.mutate({ id: node.id });
        }}
      >
        Archive
      </DropdownMenuItem>
      <DropdownMenuItem
        className="text-destructive focus:text-destructive"
        onSelect={() => {
          if (window.confirm(`Delete "${node.title}"?`)) {
            deleteObj.mutate({ id: node.id });
          }
        }}
      >
        Delete
      </DropdownMenuItem>
    </>
  );
}

function CreateChildMenu({ parentId, workspaceId }: { parentId: string; workspaceId: string }) {
  const utils = api.useUtils();
  const create = api.objects.create.useMutation({
    onSuccess: () => {
      void utils.objects.getTree.invalidate();
    },
  });
  const handleCreate = (type: ObjectType, title: string) => {
    create.mutate({ type, title, workspaceId, parentId });
  };
  return (
    <>
      <DropdownMenuItem onSelect={() => handleCreate("task", "Untitled List")}>List</DropdownMenuItem>
      <DropdownMenuItem onSelect={() => handleCreate("document", "Untitled Doc")}>Doc</DropdownMenuItem>
      <DropdownMenuItem onSelect={() => handleCreate("group", "Untitled Folder")}>Folder</DropdownMenuItem>
      <DropdownMenuItem onSelect={() => handleCreate("whiteboard", "Untitled Whiteboard")}>
        Whiteboard
      </DropdownMenuItem>
    </>
  );
}

export function TreeNode({
  node,
  level,
  collapsed,
  base,
  pathname,
  workspaceId,
}: {
  node: TreeNodeData;
  level: number;
  collapsed: boolean;
  base: string;
  pathname: string | null;
  workspaceId: string;
}) {
  const expandedNodes = useSidebarStore((s) => s.expandedNodes);
  const toggleNode = useSidebarStore((s) => s.toggleNode);

  const hasChildren = node.children.length > 0;
  const expanded = isSidebarNodeExpanded(expandedNodes, node.id);
  const href = hrefForNode(base, node.id, node.type);
  const active =
    pathname === href || (pathname?.startsWith(href + "/") ?? false);
  const showPlus = showCreateChildActions(node.type);

  const onToggleExpand = useCallback(
    (e: React.MouseEvent) => {
      e.preventDefault();
      e.stopPropagation();
      toggleNode(node.id);
    },
    [node.id, toggleNode],
  );

  if (collapsed) {
    const link = (
      <Link
        href={href}
        className={cn(
          "flex h-8 w-full items-center justify-center rounded-md text-sidebar-foreground transition-colors duration-150",
          "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
          active &&
            "bg-sidebar-accent text-sidebar-accent-foreground shadow-[inset_3px_0_0_0_hsl(var(--primary))]",
        )}
      >
        <TypeIcon type={node.type} node={node} />
      </Link>
    );
    return (
      <div className="w-full">
        <Tooltip delayDuration={0}>
          <TooltipTrigger asChild>{link}</TooltipTrigger>
          <TooltipContent side="right" className="max-w-[240px] font-medium">
            {node.title}
          </TooltipContent>
        </Tooltip>
        {hasChildren ? (
          <div className="flex flex-col gap-px border-l border-sidebar-border/60 pl-1">
            {node.children.map((ch) => (
              <TreeNode
                key={ch.id}
                node={ch}
                level={level + 1}
                collapsed={collapsed}
                base={base}
                pathname={pathname}
                workspaceId={workspaceId}
              />
            ))}
          </div>
        ) : null}
      </div>
    );
  }

  const indentPx = level === 0 ? 0 : 8 + (level - 1) * 16;

  return (
    <div className="select-none">
      <div
        className="group relative flex min-h-7 items-center gap-0.5 rounded-md pr-1 transition-colors duration-150"
        style={{ paddingLeft: indentPx }}
      >
        <div className="flex min-h-7 min-w-0 flex-1 items-center">
          <Link
            href={href}
            className={cn(
              "flex min-h-7 min-w-0 flex-1 items-center gap-2 rounded-sm py-1 pl-1 pr-2 text-sm text-sidebar-foreground transition-colors duration-150",
              "hover:bg-sidebar-accent/80 hover:text-sidebar-accent-foreground",
              active &&
                "bg-sidebar-accent text-sidebar-accent-foreground shadow-[inset_3px_0_0_0_hsl(var(--primary))]",
            )}
          >
            {hasChildren ? (
              <button
                type="button"
                onClick={onToggleExpand}
                className="relative flex size-5 shrink-0 items-center justify-center rounded-sm"
                aria-expanded={expanded}
                aria-label={expanded ? "Collapse" : "Expand"}
              >
                <span className="group-hover:hidden">
                  <TypeIcon type={node.type} node={node} />
                </span>
                <span className="hidden rounded bg-muted group-hover:flex group-hover:items-center group-hover:justify-center group-hover:size-5">
                  <ChevronRight
                    className={cn(
                      "size-3.5 text-muted-foreground transition-transform duration-200",
                      expanded && "rotate-90",
                    )}
                  />
                </span>
              </button>
            ) : (
              <TypeIcon type={node.type} node={node} />
            )}
            <span className="min-w-0 flex-1 truncate font-medium">{node.title}</span>
            {node.childCount > 0 ? (
              <span className="shrink-0 tabular-nums text-[10px] text-muted-foreground">
                {node.childCount}
              </span>
            ) : null}
          </Link>
        </div>

        <div
          className={cn(
            "pointer-events-none flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity duration-150",
            "group-hover:pointer-events-auto group-hover:opacity-100",
          )}
        >
          {showPlus ? (
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button
                  type="button"
                  variant="ghost"
                  size="icon"
                  className="size-6 text-muted-foreground hover:text-sidebar-accent-foreground"
                  title="Create"
                  onClick={(e) => {
                    e.preventDefault();
                    e.stopPropagation();
                  }}
                >
                  <Plus className="size-3.5" />
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end" className="w-44" onClick={(e) => e.stopPropagation()}>
                <CreateChildMenu parentId={node.id} workspaceId={workspaceId} />
              </DropdownMenuContent>
            </DropdownMenu>
          ) : null}

          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button
                type="button"
                variant="ghost"
                size="icon"
                className="size-6 text-muted-foreground hover:text-sidebar-accent-foreground"
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                }}
              >
                <MoreHorizontal className="size-3.5" />
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end" className="w-48" onClick={(e) => e.stopPropagation()}>
              <MoreMenuItems node={node} href={href} workspaceId={workspaceId} />
            </DropdownMenuContent>
          </DropdownMenu>
        </div>
      </div>

      {hasChildren ? (
        <CollapsibleBody open={expanded}>
          <div className="flex flex-col gap-px pb-0.5">
            {node.children.map((ch) => (
              <TreeNode
                key={ch.id}
                node={ch}
                level={level + 1}
                collapsed={collapsed}
                base={base}
                pathname={pathname}
                workspaceId={workspaceId}
              />
            ))}
          </div>
        </CollapsibleBody>
      ) : null}
    </div>
  );
}

export function NavTree({
  collapsed,
  trees: treesProp,
}: {
  collapsed: boolean;
  trees?: PartitionedTrees;
}) {
  const pathname = usePathname();
  const params = useParams();
  const workspace = useWorkspaceStore((s) => s.currentWorkspace);
  const slugParam =
    typeof params?.workspaceSlug === "string" ? params.workspaceSlug : "";
  const base = workspace?.slug
    ? `/${workspace.slug}`
    : slugParam
      ? `/${slugParam}`
      : "";

  const workspaceId = workspace?.id ?? "";
  const favoritesQuery = api.favorites.list.useQuery(undefined, {
    enabled: Boolean(workspaceId),
  });
  const favorites = favoritesQuery.data ?? [];

  const { data, isLoading, isError } = api.objects.getTree.useQuery(
    { workspaceId: workspace?.id! },
    { enabled: Boolean(workspace?.id) },
  );

  const partitioned = useMemo(() => {
    if (treesProp) return treesProp;
    if (!workspace?.id || isLoading) return EMPTY_PARTITIONED;
    if (isError || !data?.tree) return EMPTY_PARTITIONED;
    return partitionRoots(data.tree);
  }, [treesProp, workspace?.id, isLoading, isError, data?.tree]);

  const totalCount = countNodes(partitioned.projects);

  const liveEmpty =
    Boolean(workspace?.id) &&
    !isLoading &&
    !isError &&
    data?.tree &&
    data.tree.length === 0;

  const showLoading = Boolean(workspace?.id) && isLoading && !treesProp;

  if (showLoading) {
    return (
      <ScrollArea className="flex-1">
        <div className="flex flex-col gap-2 px-3 pb-4 pt-2">
          <div className="h-7 animate-pulse rounded-md bg-sidebar-accent/50" />
          <div className="h-7 animate-pulse rounded-md bg-sidebar-accent/40" />
          <div className="h-7 animate-pulse rounded-md bg-sidebar-accent/30" />
          <div className="h-7 animate-pulse rounded-md bg-sidebar-accent/25" />
        </div>
      </ScrollArea>
    );
  }

  return (
    <ScrollArea className="flex-1">
      <div className="flex flex-col gap-0.5 px-2 pb-4 pt-1">
        {!collapsed ? (
          <div className="mb-2">
            <div className="flex items-center px-2 py-1.5">
              <span className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
                Favorites
              </span>
            </div>
            {favorites.length === 0 ? (
              <div className="px-3 py-2 text-xs text-muted-foreground/60">No favorites yet</div>
            ) : (
              <div className="flex flex-col gap-0.5 px-1">
                {favorites.map((fav) => {
                  const favHref = hrefForNode(base, fav.objectId, fav.objectType);
                  return (
                  <Link
                    key={fav.id}
                    href={favHref}
                    className={cn(
                      "flex min-h-7 items-center gap-2 rounded-sm px-2 py-1 text-sm text-sidebar-foreground transition-colors",
                      "hover:bg-sidebar-accent/80",
                      pathname === favHref &&
                        "bg-sidebar-accent text-sidebar-accent-foreground",
                    )}
                  >
                    <TypeIcon type={fav.objectType} />
                    <span className="min-w-0 flex-1 truncate font-medium">{fav.objectTitle}</span>
                  </Link>
                  );
                })}
              </div>
            )}
          </div>
        ) : null}

        {!collapsed ? (
          <div className="flex items-center justify-between px-2 py-1.5">
            <span className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
              Spaces
            </span>
            <Tooltip delayDuration={0}>
              <TooltipTrigger asChild>
                <Button
                  variant="ghost"
                  size="icon"
                  className="size-5 text-muted-foreground hover:text-sidebar-accent-foreground"
                  type="button"
                  onClick={(e) => {
                    e.preventDefault();
                  }}
                >
                  <Plus className="size-3.5" />
                </Button>
              </TooltipTrigger>
              <TooltipContent side="top">New Space</TooltipContent>
            </Tooltip>
          </div>
        ) : null}

        {liveEmpty ? (
          <div className="rounded-md border border-dashed border-sidebar-border px-3 py-6 text-center text-xs text-muted-foreground">
            No spaces yet.
            <br />
            <span className="text-[10px]">Create a space to get started.</span>
          </div>
        ) : null}

        {!liveEmpty && totalCount === 0 ? (
          <div className="rounded-md border border-dashed border-sidebar-border px-3 py-6 text-center text-xs text-muted-foreground">
            Nothing to show yet.
          </div>
        ) : null}

        {!liveEmpty && totalCount > 0 ? (
          <div className={cn("flex flex-col gap-px", collapsed && "items-center")}>
            {partitioned.projects.map((node) => (
              <TreeNode
                key={node.id}
                node={node}
                level={0}
                collapsed={collapsed}
                base={base}
                pathname={pathname}
                workspaceId={workspaceId}
              />
            ))}
          </div>
        ) : null}
      </div>
    </ScrollArea>
  );
}
