import { useAtom } from "jotai";
import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
import { ActionIcon, Menu, rem } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { notifications } from "@mantine/notifications";
import {
  IconArrowRight,
  IconClockHour4,
  IconCloudDownload,
  IconCopy,
  IconDotsVertical,
  IconFileExport,
  IconLink,
  IconStar,
  IconStarFilled,
  IconTemplate,
  IconTrash,
} from "@tabler/icons-react";

import ExportModal from "@/components/common/export-modal";
import MovePageModal from "@/features/page/components/move-page-modal.tsx";
import CopyPageModal from "@/features/page/components/copy-page-modal.tsx";
import { buildPageUrl } from "@/features/page/page.utils.ts";
import { duplicatePage } from "@/features/page/services/page-service.ts";
import { useClipboard } from "@/hooks/use-clipboard";
import { getAppUrl } from "@/lib/config.ts";
import { useQueryEmit } from "@/features/websocket/use-query-emit.ts";
import {
  useFavoriteIds,
  useAddFavoriteMutation,
  useRemoveFavoriteMutation,
} from "@/features/favorite/queries/favorite-query";

import {
  useToggleTemplateMutation,
  useToggleTemporaryMutation,
} from "@/features/page-embed/queries/page-embed-query";
import { useCollabToken } from "@/features/auth/queries/auth-query.tsx";
import { getCollaborationUrl } from "@/lib/config.ts";
import {
  makePageAvailableOffline,
  warmPageYdoc,
} from "@/features/offline/make-offline";
import { treeDataAtom } from "@/features/page/tree/atoms/tree-data-atom.ts";
import { treeModel } from "@/features/page/tree/model/tree-model";
import { pageToTreeNode } from "@/features/page/tree/utils";
import { useTreeMutation } from "@/features/page/tree/hooks/use-tree-mutation.ts";
import type { SpaceTreeNode } from "@/features/page/tree/types.ts";
import classes from "@/features/page/tree/styles/tree.module.css";

export interface NodeMenuProps {
  node: SpaceTreeNode;
  canEdit: boolean;
}

export function NodeMenu({ node, canEdit }: NodeMenuProps) {
  const { t } = useTranslation();
  const clipboard = useClipboard({ timeout: 500 });
  const { spaceSlug } = useParams();
  const { handleDelete } = useTreeMutation(node.spaceId);
  const [data, setData] = useAtom(treeDataAtom);
  const emit = useQueryEmit();
  const [exportOpened, { open: openExportModal, close: closeExportModal }] =
    useDisclosure(false);
  const [
    movePageModalOpened,
    { open: openMovePageModal, close: closeMoveSpaceModal },
  ] = useDisclosure(false);
  const [
    copyPageModalOpened,
    { open: openCopyPageModal, close: closeCopySpaceModal },
  ] = useDisclosure(false);
  const favoriteIds = useFavoriteIds("page", node.spaceId);
  const addFavorite = useAddFavoriteMutation();
  const removeFavorite = useRemoveFavoriteMutation();
  const isFavorited = favoriteIds.has(node.id);
  const toggleTemplate = useToggleTemplateMutation();
  const isTemplate = !!node.isTemplate;
  const toggleTemporary = useToggleTemporaryMutation();
  const isTemporary = !!node.temporaryExpiresAt;
  const { data: collabQuery } = useCollabToken();

  const handleMakeAvailableOffline = async () => {
    notifications.show({ message: t("Saving page for offline use...") });
    try {
      // Prefetch read queries so they get persisted to IndexedDB. The result
      // reports whether every warm step succeeded.
      const result = await makePageAvailableOffline({
        pageId: node.id,
        spaceId: node.spaceId,
      });
      // Best-effort: warm the page's Yjs document into IndexedDB.
      await warmPageYdoc(node.id, getCollaborationUrl(), collabQuery?.token);

      if (result.ok) {
        notifications.show({ message: t("Page is now available offline") });
      } else {
        // Partial warm — the page may still be partly usable offline, but some
        // queries failed to cache, so surface it as an error rather than a
        // silent success.
        notifications.show({
          message: t("Failed to make page available offline"),
          color: "red",
        });
      }
    } catch (err) {
      // makePageAvailableOffline no longer throws, but warmPageYdoc and other
      // unexpected failures stay guarded here. Log the raw error and surface the
      // real cause to the user instead of a bare generic string (AGENTS.md).
      console.error("handleMakeAvailableOffline failed", err);
      const reason =
        (err as { response?: { data?: { message?: string } } })?.response?.data
          ?.message ?? (err instanceof Error ? err.message : String(err));
      notifications.show({
        message: `${t("Failed to make page available offline")}: ${reason}`,
        color: "red",
      });
    }
  };

  const handleToggleTemplate = async () => {
    const next = !isTemplate;
    try {
      await toggleTemplate.mutateAsync({ pageId: node.id, isTemplate: next });
      // Reflect the new flag locally so the menu label updates immediately.
      setData((prev) =>
        treeModel.update(prev, node.id, { isTemplate: next } as any),
      );
      notifications.show({
        message: next
          ? t("Page marked as template")
          : t("Page is no longer a template"),
      });
    } catch {
      // mutation surfaces the error via notifications
    }
  };

  const handleToggleTemporary = async () => {
    const next = !isTemporary;
    try {
      const res = await toggleTemporary.mutateAsync({
        pageId: node.id,
        temporary: next,
      });
      // Reflect the new deadline locally so the icon/menu update immediately.
      setData((prev) =>
        treeModel.update(prev, node.id, {
          temporaryExpiresAt: res.temporaryExpiresAt,
        } as any),
      );
      notifications.show({
        message: next
          ? t("Note will move to trash unless made permanent")
          : t("Note is now permanent"),
      });
    } catch {
      // mutation surfaces the error via notifications
    }
  };

  const handleCopyLink = () => {
    const pageUrl =
      getAppUrl() + buildPageUrl(spaceSlug, node.slugId, node.name);
    clipboard.copy(pageUrl);
    notifications.show({ message: t("Link copied") });
  };

  const handleDuplicatePage = async () => {
    try {
      const duplicatedPage = await duplicatePage({ pageId: node.id });

      // figure out parent + insertion index
      const siblings = treeModel.siblingsOf(data, node.id);
      const parentId = siblings?.parentId ?? null;
      const currentIndex = siblings?.index ?? 0;
      const newIndex = currentIndex + 1;

      // Routed through the canonical mapper so the field copy stays in lockstep
      // with buildTree. The server does NOT arm a death timer on duplicate (the
      // copy's `temporaryExpiresAt` defaults to null = permanent), so the mapper
      // carries that null through and the duplicated node correctly shows no
      // clock marker — matching the server without a reload.
      const treeNodeData: SpaceTreeNode = pageToTreeNode(duplicatedPage, {
        canEdit: true,
      });

      setData((prev) =>
        treeModel.insert(prev, parentId, treeNodeData, newIndex),
      );

      setTimeout(() => {
        emit({
          operation: "addTreeNode",
          spaceId: node.spaceId,
          payload: {
            parentId,
            index: newIndex,
            data: treeNodeData,
          },
        });
      }, 50);

      notifications.show({ message: t("Page duplicated successfully") });
    } catch (err: any) {
      notifications.show({
        message: err?.response?.data?.message || "An error occurred",
        color: "red",
      });
    }
  };

  return (
    <>
      <Menu shadow="md" width={200}>
        <Menu.Target>
          <ActionIcon
            size={20}
            variant="subtle"
            color="gray"
            className={classes.actionIcon}
            aria-label={t("Page menu for {{name}}", { name: node.name || t("Untitled") })}
            tabIndex={-1}
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
            }}
          >
            <IconDotsVertical
              style={{ width: rem(20), height: rem(20) }}
              stroke={2}
            />
          </ActionIcon>
        </Menu.Target>

        <Menu.Dropdown>
          <Menu.Item
            leftSection={<IconLink size={16} />}
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              handleCopyLink();
            }}
          >
            {t("Copy link")}
          </Menu.Item>

          <Menu.Item
            leftSection={
              isFavorited ? <IconStarFilled size={16} /> : <IconStar size={16} />
            }
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              if (isFavorited) {
                removeFavorite.mutate({ type: "page", pageId: node.id });
              } else {
                addFavorite.mutate({ type: "page", pageId: node.id });
              }
            }}
          >
            {isFavorited ? t("Remove from favorites") : t("Add to favorites")}
          </Menu.Item>

          <Menu.Item
            leftSection={<IconFileExport size={16} />}
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              openExportModal();
            }}
          >
            {t("Export")}
          </Menu.Item>

          <Menu.Item
            leftSection={<IconCloudDownload size={16} />}
            onClick={(e) => {
              e.preventDefault();
              e.stopPropagation();
              handleMakeAvailableOffline();
            }}
          >
            {t("Make available offline")}
          </Menu.Item>

          {canEdit && (
            <>
              <Menu.Item
                leftSection={<IconCopy size={16} />}
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  handleDuplicatePage();
                }}
              >
                {t("Duplicate")}
              </Menu.Item>

              <Menu.Item
                leftSection={<IconArrowRight size={16} />}
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  openMovePageModal();
                }}
              >
                {t("Move to space")}
              </Menu.Item>

              <Menu.Item
                leftSection={<IconCopy size={16} />}
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  openCopyPageModal();
                }}
              >
                {t("Copy to space")}
              </Menu.Item>

              <Menu.Item
                leftSection={<IconTemplate size={16} />}
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  handleToggleTemplate();
                }}
              >
                {isTemplate ? t("Unset as template") : t("Make template")}
              </Menu.Item>

              <Menu.Item
                leftSection={<IconClockHour4 size={16} />}
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  handleToggleTemporary();
                }}
              >
                {isTemporary ? t("Make permanent") : t("Make temporary")}
              </Menu.Item>

              <Menu.Divider />
              <Menu.Item
                c="red"
                leftSection={<IconTrash size={16} />}
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  handleDelete(node.id);
                }}
              >
                {t("Move to trash")}
              </Menu.Item>
            </>
          )}
        </Menu.Dropdown>
      </Menu>

      <MovePageModal
        pageId={node.id}
        slugId={node.slugId}
        currentSpaceSlug={spaceSlug}
        onClose={closeMoveSpaceModal}
        open={movePageModalOpened}
      />

      <CopyPageModal
        pageId={node.id}
        currentSpaceSlug={spaceSlug}
        onClose={closeCopySpaceModal}
        open={copyPageModalOpened}
      />

      <ExportModal
        type="page"
        id={node.id}
        open={exportOpened}
        onClose={closeExportModal}
      />
    </>
  );
}
