import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { useDroppable } from "@dnd-kit/core";
import {
  Folder,
  FolderOpen,
  FolderPlus,
  Inbox as InboxIcon,
  Layers,
  MoreVertical,
  Pencil,
  Share2,
  Trash2,
  Users,
  Check,
  X,
} from "lucide-react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import {
  type NoteFolder,
  type SharedFolder,
  useCreateFolder,
  useDeleteFolder,
  useSharedWithMeFolders,
  useUpdateFolder,
  userDisplayName,
} from "@/lib/notes-api";
import { useToast } from "@/hooks/use-toast";

export type FolderSelection =
  | { kind: "all" }
  | { kind: "unfiled" }
  | { kind: "folder"; id: number }
  // A folder shared WITH the current user. Distinct kind because the
  // notes inside come from a different endpoint, the rail row carries
  // the owner's display name, and every write affordance is hidden.
  | { kind: "shared-folder"; id: number };

export function selectionMatches(a: FolderSelection, b: FolderSelection): boolean {
  if (a.kind !== b.kind) return false;
  if (
    (a.kind === "folder" && b.kind === "folder") ||
    (a.kind === "shared-folder" && b.kind === "shared-folder")
  ) {
    return a.id === b.id;
  }
  return true;
}

// Stable id namespace for @dnd-kit droppables rendered by this rail.
// Kept in sync with NotesPage's onDragEnd which routes drops based on
// `over.data.current.target` (a "unfiled" | number) rather than the id
// itself, so the same target id can appear in multiple places (rail row
// AND folders browser tile) without collision.
export const folderDropId = (target: "unfiled" | number) =>
  `folder-drop-rail-${target}`;

// A row that accepts a NoteCard drop. Wraps its children in a useDroppable
// from @dnd-kit so the page-level DndContext picks up the drop and routes
// to the move-to-folder mutation. Kept inline so each rail row registers
// its own droppable id (one hook per row).
function DroppableRow({
  target,
  ownerId,
  className,
  testId,
  children,
}: {
  target: "unfiled" | number;
  // Set only for shared-folder rows, so handleDragEnd can enforce the
  // server's same-owner rule for editors.
  ownerId?: number;
  className?: string;
  testId?: string;
  children: React.ReactNode;
}) {
  const { setNodeRef, isOver } = useDroppable({
    id:
      ownerId === undefined
        ? folderDropId(target)
        : `${folderDropId(target)}-shared-${ownerId}`,
    data: { type: "folder-drop", target, ownerId },
  });
  return (
    <div
      ref={setNodeRef}
      data-testid={testId}
      data-drop-active={isOver ? "true" : "false"}
      className={className}
    >
      {children}
    </div>
  );
}

interface Props {
  folders: NoteFolder[];
  selected: FolderSelection;
  onSelect: (s: FolderSelection) => void;
  unfiledCount: number;
  totalCount: number;
  // Owner action: open the folder-share dialog. The page handles mounting
  // <FolderShareDialog> so the rail stays focused on navigation/selection.
  onShareFolder?: (folder: NoteFolder) => void;
  // "rail"        → full sidebar / chip rail with folder rows (default).
  // "chips-only"  → render only the All-notes / Unfiled / "+ New folder"
  //                 chips. Folder rows are owned by the FoldersBrowser in the
  //                 main content so test ids don't collide.
  mode?: "rail" | "chips-only";
  // Extra classes appended to the root <aside>. Used by the Notes page
  // to control flex `order-*` so the composer stacks above the rail on
  // narrow widths.
  className?: string;
}

export function FoldersRail({
  folders,
  selected,
  onSelect,
  unfiledCount,
  totalCount,
  onShareFolder,
  mode = "rail",
  className,
}: Props) {
  const { t, i18n } = useTranslation();
  const lang = i18n.language;
  // Folders shared WITH the current user. Rendered in a dedicated section
  // below the owner's folders so the two namespaces are visually distinct.
  const { data: sharedFolders = [] } = useSharedWithMeFolders();
  const [newName, setNewName] = useState("");
  const [creating, setCreating] = useState(false);
  const [editingId, setEditingId] = useState<number | null>(null);
  const [editingName, setEditingName] = useState("");
  const [pendingDelete, setPendingDelete] = useState<NoteFolder | null>(null);
  const create = useCreateFolder();
  const update = useUpdateFolder();
  const del = useDeleteFolder();
  const { toast } = useToast();

  // Map server error → user-facing toast. 409 = duplicate folder name for
  // this user; anything else is a generic failure.
  const reportFolderError = (e: unknown, fallbackKey: string, fallback: string) => {
    const status =
      typeof e === "object" && e !== null && "status" in e
        ? Number((e as { status?: unknown }).status)
        : 0;
    if (status === 409) {
      toast({
        title: t("notes.folders.duplicateTitle", "Folder already exists"),
        description: t(
          "notes.folders.duplicateDesc",
          "Pick a different name — you already have a folder with that name.",
        ),
        variant: "destructive",
      });
    } else {
      toast({ title: t(fallbackKey, fallback), variant: "destructive" });
    }
  };

  const submitCreate = () => {
    const name = newName.trim();
    if (!name) return;
    create.mutate(name, {
      onSuccess: (folder) => {
        setNewName("");
        setCreating(false);
        // Required by spec: newly-created folder is immediately selected so
        // the user lands on its (empty) view.
        onSelect({ kind: "folder", id: folder.id });
      },
      onError: (e) =>
        reportFolderError(e, "notes.folders.createFailed", "Could not create folder"),
    });
  };

  const renderRow = (
    key: string,
    {
      icon,
      label,
      count,
      isSelected,
      onClick,
      dropTargetTestId,
      dropTarget,
      action,
      content,
    }: {
      icon: React.ReactNode;
      label: React.ReactNode;
      count: number;
      isSelected: boolean;
      onClick: () => void;
      dropTargetTestId: string;
      dropTarget?: "unfiled" | number;
      action?: React.ReactNode;
      content?: React.ReactNode;
    },
  ) => {
    const className = `group rounded-lg border transition shrink-0 md:shrink ${
      isSelected
        ? "bg-foreground text-background border-foreground"
        : "border-transparent hover:bg-slate-100"
    } data-[drop-active=true]:ring-2 data-[drop-active=true]:ring-amber-400 data-[drop-active=true]:ring-offset-1`;
    const inner =
      content ?? (
        <button
          type="button"
          onClick={onClick}
          className="w-full flex items-center gap-2 px-2.5 py-1.5 text-sm text-start whitespace-nowrap"
        >
          <span className="shrink-0">{icon}</span>
          <span className="flex-1 truncate">{label}</span>
          <span
            className={`text-[10px] px-1.5 py-0.5 rounded-full ${
              isSelected
                ? "bg-background/20 text-background"
                : "bg-slate-200/80 text-muted-foreground"
            }`}
            data-testid={`${dropTargetTestId}-count`}
          >
            {count}
          </span>
          {action}
        </button>
      );
    if (dropTarget !== undefined) {
      return (
        <DroppableRow
          key={key}
          target={dropTarget}
          className={className}
          testId={dropTargetTestId}
        >
          {inner}
        </DroppableRow>
      );
    }
    return (
      <div key={key} data-testid={dropTargetTestId} className={className}>
        {inner}
      </div>
    );
  };

  return (
    <aside
      // Mobile: horizontal chip row above the notes grid.
      // Desktop (md+): fixed sidebar to the start of the grid.
      className={`w-full md:w-56 shrink-0 flex md:block gap-1 md:gap-0 md:space-y-1 overflow-x-auto md:overflow-visible pb-2 md:pb-0${className ? ` ${className}` : ""}`}
      data-testid="notes-folders-rail"
    >
      <div className="hidden md:block text-[11px] font-semibold uppercase tracking-wider text-muted-foreground px-2 mb-1">
        {t("notes.folders.title", "Folders")}
      </div>

      {renderRow("all", {
        icon: <Layers size={14} />,
        label: t("notes.folders.all", "All notes"),
        count: totalCount,
        isSelected: selected.kind === "all",
        onClick: () => onSelect({ kind: "all" }),
        dropTargetTestId: "folder-drop-all",
      })}

      {renderRow("unfiled", {
        icon: <InboxIcon size={14} />,
        label: t("notes.folders.unfiled", "Unfiled"),
        count: unfiledCount,
        isSelected: selected.kind === "unfiled",
        onClick: () => onSelect({ kind: "unfiled" }),
        dropTargetTestId: "folder-drop-unfiled",
        dropTarget: "unfiled",
      })}

      <div className="contents md:block md:pt-1">
        {mode === "rail" && folders.length > 0 && (
          <div className="hidden md:block text-[11px] font-semibold uppercase tracking-wider text-muted-foreground px-2 mb-1 mt-3">
            {t("notes.folders.myFolders", "My folders")}
          </div>
        )}
        {mode === "rail" && folders.map((f) => {
          const isEditing = editingId === f.id;
          const isSel = selected.kind === "folder" && selected.id === f.id;
          return renderRow(`f:${f.id}`, {
            icon: isSel ? <FolderOpen size={14} /> : <Folder size={14} />,
            label: f.name,
            count: f.noteCount,
            isSelected: isSel,
            onClick: () => onSelect({ kind: "folder", id: f.id }),
            dropTargetTestId: `folder-drop-${f.id}`,
            dropTarget: f.id,
            content: isEditing ? (
              <div className="flex items-center gap-1 px-2 py-1.5">
                <Folder size={14} className="text-muted-foreground shrink-0" />
                <Input
                  autoFocus
                  value={editingName}
                  onChange={(e) => setEditingName(e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === "Enter" && editingName.trim()) {
                      update.mutate(
                        { id: f.id, name: editingName.trim() },
                        {
                          onSuccess: () => setEditingId(null),
                          onError: (e) =>
                            reportFolderError(
                              e,
                              "notes.folders.renameFailed",
                              "Could not rename folder",
                            ),
                        },
                      );
                    }
                    if (e.key === "Escape") setEditingId(null);
                  }}
                  className="h-7 text-sm"
                  data-testid={`folder-rename-input-${f.id}`}
                />
                <button
                  className="p-1 text-muted-foreground hover:text-foreground"
                  onClick={() => {
                    if (editingName.trim()) {
                      update.mutate(
                        { id: f.id, name: editingName.trim() },
                        {
                          onSuccess: () => setEditingId(null),
                          onError: (e) =>
                            reportFolderError(
                              e,
                              "notes.folders.renameFailed",
                              "Could not rename folder",
                            ),
                        },
                      );
                    }
                  }}
                  aria-label={t("common.save", "Save")}
                  data-testid={`folder-rename-save-${f.id}`}
                >
                  <Check size={14} />
                </button>
                <button
                  className="p-1 text-muted-foreground hover:text-foreground"
                  onClick={() => setEditingId(null)}
                  aria-label={t("common.cancel", "Cancel")}
                >
                  <X size={14} />
                </button>
              </div>
            ) : (
              <div className="flex items-stretch">
                <button
                  type="button"
                  onClick={() => onSelect({ kind: "folder", id: f.id })}
                  className="flex-1 flex items-center gap-2 px-2.5 py-1.5 text-sm text-start min-w-0 whitespace-nowrap"
                  data-testid={`folder-select-${f.id}`}
                >
                  <span className="shrink-0">
                    {isSel ? <FolderOpen size={14} /> : <Folder size={14} />}
                  </span>
                  <span className="flex-1 truncate">{f.name}</span>
                  <span
                    className={`text-[10px] px-1.5 py-0.5 rounded-full ${
                      isSel
                        ? "bg-background/20 text-background"
                        : "bg-slate-200/80 text-muted-foreground"
                    }`}
                    data-testid={`folder-drop-${f.id}-count`}
                  >
                    {f.noteCount}
                  </span>
                  {f.sharedWithCount > 0 && (
                    <span
                      className={`inline-flex items-center gap-0.5 text-[10px] px-1.5 py-0.5 rounded-full ${
                        isSel
                          ? "bg-background/20 text-background"
                          : "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
                      }`}
                      title={t("notes.folders.sharedWithCount", {
                        defaultValue_one: "Shared with 1 person",
                        defaultValue_other: "Shared with {{count}} people",
                        count: f.sharedWithCount,
                      })}
                      data-testid={`folder-shared-badge-${f.id}`}
                    >
                      <Users size={10} />
                      {f.sharedWithCount}
                    </span>
                  )}
                </button>
                <div className="flex items-center pe-1">
                  <DropdownMenu>
                    <DropdownMenuTrigger asChild>
                      <button
                        type="button"
                        onClick={(e) => e.stopPropagation()}
                        className={`p-1 rounded hover:bg-black/10 ${
                          isSel ? "text-background/80" : "text-muted-foreground"
                        }`}
                        aria-label={t("notes.folders.actions", "Folder actions")}
                        data-testid={`folder-menu-${f.id}`}
                      >
                        <MoreVertical size={14} />
                      </button>
                    </DropdownMenuTrigger>
                    <DropdownMenuContent align="end" className="w-44">
                      <DropdownMenuItem
                        onSelect={() => {
                          setEditingId(f.id);
                          setEditingName(f.name);
                        }}
                        data-testid={`folder-rename-${f.id}`}
                      >
                        <Pencil size={14} className="me-2" />
                        {t("notes.folders.rename", "Rename folder")}
                      </DropdownMenuItem>
                      {onShareFolder && (
                        <DropdownMenuItem
                          onSelect={() => onShareFolder(f)}
                          data-testid={`folder-share-${f.id}`}
                        >
                          <Share2 size={14} className="me-2" />
                          {t("notes.folders.share", "Share folder")}
                        </DropdownMenuItem>
                      )}
                      <DropdownMenuItem
                        onSelect={() => setPendingDelete(f)}
                        className="text-red-600 focus:text-red-600"
                        data-testid={`folder-delete-${f.id}`}
                      >
                        <Trash2 size={14} className="me-2" />
                        {t("notes.folders.delete", "Delete folder")}
                      </DropdownMenuItem>
                    </DropdownMenuContent>
                  </DropdownMenu>
                </div>
              </div>
            ),
          });
        })}
      </div>

      {creating ? (
        <div className="flex items-center gap-1 px-2 py-1.5 shrink-0">
          <FolderPlus size={14} className="text-muted-foreground" />
          <Input
            autoFocus
            value={newName}
            onChange={(e) => setNewName(e.target.value)}
            placeholder={t("notes.folders.namePlaceholder", "Folder name")}
            onKeyDown={(e) => {
              if (e.key === "Enter") submitCreate();
              if (e.key === "Escape") {
                setNewName("");
                setCreating(false);
              }
            }}
            className="h-7 text-sm"
            data-testid="folder-create-input"
          />
          <Button
            size="sm"
            variant="ghost"
            className="h-7 px-1"
            disabled={!newName.trim() || create.isPending}
            onClick={submitCreate}
            data-testid="folder-create-submit"
          >
            <Check size={14} />
          </Button>
        </div>
      ) : (
        <button
          type="button"
          onClick={() => setCreating(true)}
          className="shrink-0 flex items-center gap-2 px-2.5 py-1.5 text-sm rounded-lg text-muted-foreground hover:bg-slate-100 hover:text-foreground whitespace-nowrap md:w-full"
          data-testid="folder-create-open"
        >
          <FolderPlus size={14} />
          <span>{t("notes.folders.new", "New folder")}</span>
        </button>
      )}

      {sharedFolders.length > 0 && (
        <div
          className="contents md:block md:pt-3"
          data-testid="shared-with-me-section"
        >
          {mode === "rail" && (
            <div className="hidden md:block text-[11px] font-semibold uppercase tracking-wider text-muted-foreground px-2 mb-1 mt-3">
              {t("notes.folders.sharedWithMe", "Shared with me")}
            </div>
          )}
          {sharedFolders.map((sf: SharedFolder) => {
            const isSel =
              selected.kind === "shared-folder" && selected.id === sf.id;
            const ownerName = userDisplayName(
              {
                id: sf.ownerId,
                username: sf.ownerUsername,
                displayNameAr: sf.ownerDisplayNameAr,
                displayNameEn: sf.ownerDisplayNameEn,
              },
              lang,
            );
            const canDrop = sf.myPermission === "edit";
            const rowInner = (
              <div
                className={`group rounded-lg border transition shrink-0 md:shrink ${
                  isSel
                    ? "bg-foreground text-background border-foreground"
                    : "border-transparent hover:bg-slate-100"
                }`}
                data-testid={`shared-folder-row-${sf.id}`}
              >
                <button
                  type="button"
                  onClick={() => onSelect({ kind: "shared-folder", id: sf.id })}
                  className="w-full flex items-center gap-2 px-2.5 py-1.5 text-sm text-start whitespace-nowrap"
                  data-testid={`shared-folder-select-${sf.id}`}
                  title={t("notes.folders.sharedByName", "by {{name}}", {
                    name: ownerName,
                  })}
                >
                  <span className="shrink-0">
                    <Share2 size={14} />
                  </span>
                  <span className="flex-1 min-w-0">
                    <span className="block truncate">{sf.name}</span>
                    <span
                      className={`block truncate text-[10px] ${
                        isSel ? "text-background/80" : "text-muted-foreground"
                      }`}
                    >
                      {t("notes.folders.sharedByName", "by {{name}}", {
                        name: ownerName,
                      })}
                    </span>
                  </span>
                  {/* Permission badge so the recipient can tell at a
                      glance whether they can edit this folder's notes
. */}
                  <span
                    className={`shrink-0 inline-flex items-center justify-center rounded px-1 py-0.5 text-[9px] font-medium uppercase tracking-wide ${
                      sf.myPermission === "edit"
                        ? isSel
                          ? "bg-background/20 text-background"
                          : "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300"
                        : isSel
                          ? "bg-background/10 text-background/80"
                          : "bg-slate-200 text-slate-600 dark:bg-slate-800 dark:text-slate-300"
                    }`}
                    data-testid={`shared-folder-perm-${sf.id}`}
                    title={
                      sf.myPermission === "edit"
                        ? t("notes.folders.canEdit", "You can edit")
                        : t("notes.folders.readOnly", "Read-only")
                    }
                  >
                    {sf.myPermission === "edit"
                      ? t("notes.folders.permissionEdit", "Edit")
                      : t("notes.folders.permissionView", "View")}
                  </span>
                  <span
                    className={`shrink-0 text-[10px] tabular-nums ${
                      isSel ? "text-background/80" : "text-muted-foreground"
                    }`}
                    data-testid={`shared-folder-count-${sf.id}`}
                  >
                    {sf.noteCount}
                  </span>
                </button>
              </div>
            );
            return canDrop ? (
              <DroppableRow
                key={`shared:${sf.id}`}
                target={sf.id}
                ownerId={sf.ownerId}
                testId={`shared-folder-drop-${sf.id}`}
              >
                {rowInner}
              </DroppableRow>
            ) : (
              <React.Fragment key={`shared:${sf.id}`}>{rowInner}</React.Fragment>
            );
          })}
        </div>
      )}

      <div className="hidden md:block text-[10px] text-muted-foreground/80 px-2 pt-1 leading-snug">
        {t(
          "notes.folders.dragHint",
          "Drag a note onto a folder to move it.",
        )}
      </div>

      <AlertDialog
        open={pendingDelete !== null}
        onOpenChange={(open) => {
          if (!open) setPendingDelete(null);
        }}
      >
        <AlertDialogContent
          data-testid={
            pendingDelete
              ? `folder-delete-dialog-${pendingDelete.id}`
              : "folder-delete-dialog"
          }
        >
          <AlertDialogHeader>
            <AlertDialogTitle>
              {t("notes.folders.deleteTitle", "Delete this folder?")}
            </AlertDialogTitle>
            <AlertDialogDescription>
              {t(
                "notes.folders.deleteConfirm",
                "Delete this folder? Notes inside will move to Unfiled.",
              )}
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel
              data-testid={
                pendingDelete
                  ? `folder-delete-cancel-${pendingDelete.id}`
                  : "folder-delete-cancel"
              }
            >
              {t("common.cancel", "Cancel")}
            </AlertDialogCancel>
            <AlertDialogAction
              className="bg-red-600 text-white hover:bg-red-700 focus:ring-red-600"
              data-testid={
                pendingDelete
                  ? `folder-delete-confirm-${pendingDelete.id}`
                  : "folder-delete-confirm"
              }
              onClick={() => {
                const target = pendingDelete;
                if (!target) return;
                del.mutate(target.id, {
                  onSuccess: () => {
                    if (
                      selected.kind === "folder" &&
                      selected.id === target.id
                    ) {
                      onSelect({ kind: "all" });
                    }
                  },
                  onError: (e) =>
                    reportFolderError(
                      e,
                      "notes.folders.deleteFailed",
                      "Could not delete folder",
                    ),
                });
                setPendingDelete(null);
              }}
            >
              {t("notes.folders.deleteAction", "Delete")}
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </aside>
  );
}
