"use client";
import * as React from "react";
import { flash } from "@open-mercato/ui/backend/FlashMessages";
import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
import { isSafeNavigationHref, parseObjectActionId, toErrorMessage } from "../utils.js";
function toStateChangeErrorMessage(payload, t) {
  if (payload && typeof payload === "object" && !Array.isArray(payload)) {
    const code = payload.code;
    if (code === "messages_sender_archive_unsupported") {
      return t(
        "messages.errors.archiveSenderMessageUnsupported",
        "You cannot archive messages you sent."
      );
    }
  }
  return toErrorMessage(payload);
}
function useMessageDetailsActions({
  id,
  t,
  detail,
  detailQuery,
  attachments,
  isArchived,
  onDeleted,
  onMarkedUnread,
  refreshDetailWithoutAutoMarkRead,
  suppressAutoMarkRead
}) {
  const [replyOpen, setReplyOpen] = React.useState(false);
  const [forwardOpen, setForwardOpen] = React.useState(false);
  const [editOpen, setEditOpen] = React.useState(false);
  const [updatingState, setUpdatingState] = React.useState(false);
  const [executingActionId, setExecutingActionId] = React.useState(null);
  const [pendingActionConfirmation, setPendingActionConfirmation] = React.useState(null);
  const [deleteConfirmationOpen, setDeleteConfirmationOpen] = React.useState(false);
  const [activeConversationAction, setActiveConversationAction] = React.useState(null);
  const { runMutation, retryLastMutation } = useGuardedMutation({
    contextId: "messages-detail-actions"
  });
  const requestAndRefresh = React.useCallback(async (url, method, options) => {
    setUpdatingState(true);
    try {
      await runMutation({
        operation: async () => {
          const call = await apiCall(url, { method });
          if (!call.ok) {
            throw new Error(
              toStateChangeErrorMessage(call.result, t) ?? t("messages.errors.stateChangeFailed", "Failed to update message state.")
            );
          }
          if (options?.skipDetailAutoMarkRead) {
            await refreshDetailWithoutAutoMarkRead();
          } else {
            await detailQuery.refetch();
          }
        },
        context: { resourceKind: "message", messageId: id, action: "state-change", retryLastMutation },
        mutationPayload: { messageId: id, action: "state-change", url, method }
      });
      options?.onSuccess?.();
    } catch (err) {
      flash(
        err instanceof Error ? err.message : t("messages.errors.stateChangeFailed", "Failed to update message state."),
        "error"
      );
    } finally {
      setUpdatingState(false);
    }
  }, [detailQuery, id, refreshDetailWithoutAutoMarkRead, retryLastMutation, runMutation, t]);
  const handleDelete = React.useCallback(async () => {
    setUpdatingState(true);
    try {
      await runMutation({
        // Carry the OSS optimistic-lock header so a stale tab cannot delete a
        // concurrently-changed message; throw a status-carrying error on failure
        // so the 409 conflict surfaces on the shared RecordConflictBanner.
        operation: () => withScopedApiRequestHeaders(
          buildOptimisticLockHeader(detail?.updatedAt),
          async () => {
            const call = await apiCall(`/api/messages/${encodeURIComponent(id)}`, {
              method: "DELETE"
            });
            if (!call.ok) {
              throw Object.assign(
                new Error(toErrorMessage(call.result) ?? t("messages.errors.deleteFailed", "Failed to delete message.")),
                {
                  status: call.status,
                  ...call.result && typeof call.result === "object" ? call.result : {}
                }
              );
            }
          }
        ),
        context: { resourceKind: "message", messageId: id, action: "delete", retryLastMutation },
        mutationPayload: { messageId: id, action: "delete" }
      });
      flash(t("messages.flash.deleted", "Message deleted."), "success");
      onDeleted();
    } catch (err) {
      if (surfaceRecordConflict(err, t)) return;
      flash(
        err instanceof Error ? err.message : t("messages.errors.deleteFailed", "Failed to delete message."),
        "error"
      );
    } finally {
      setUpdatingState(false);
    }
  }, [detail?.updatedAt, id, onDeleted, retryLastMutation, runMutation, t]);
  const runConversationAction = React.useCallback(async (action, options) => {
    setActiveConversationAction(action);
    try {
      await runMutation({
        operation: async () => {
          const call = await apiCall(options.url, { method: options.method });
          if (!call.ok) {
            throw new Error(
              toErrorMessage(call.result) ?? t("messages.errors.conversationActionFailed", "Failed to update conversation.")
            );
          }
        },
        context: { resourceKind: "conversation", messageId: id, action, retryLastMutation },
        mutationPayload: { messageId: id, action, url: options.url, method: options.method }
      });
      flash(options.successMessage, "success");
      if (options.onSuccess) {
        options.onSuccess();
        return;
      }
      if (options.skipDetailAutoMarkRead) {
        await refreshDetailWithoutAutoMarkRead();
      } else {
        await detailQuery.refetch();
      }
    } catch (err) {
      flash(
        err instanceof Error ? err.message : t("messages.errors.conversationActionFailed", "Failed to update conversation."),
        "error"
      );
    } finally {
      setActiveConversationAction(null);
    }
  }, [detailQuery, id, refreshDetailWithoutAutoMarkRead, retryLastMutation, runMutation, t]);
  const archiveConversation = React.useCallback(async (messageId) => {
    const targetMessageId = messageId ?? id;
    await runConversationAction("archiveConversation", {
      url: `/api/messages/${encodeURIComponent(targetMessageId)}/conversation/archive`,
      method: "PUT",
      successMessage: t("messages.flash.conversationArchived", "Conversation archived.")
    });
  }, [id, runConversationAction, t]);
  const unarchiveConversation = React.useCallback(async (messageId) => {
    const targetMessageId = messageId ?? id;
    await runConversationAction("unarchiveConversation", {
      url: `/api/messages/${encodeURIComponent(targetMessageId)}/conversation/archive`,
      method: "DELETE",
      successMessage: t("messages.flash.conversationUnarchived", "Conversation restored from archive.")
    });
  }, [id, runConversationAction, t]);
  const markConversationUnread = React.useCallback(async (messageId) => {
    suppressAutoMarkRead();
    const targetMessageId = messageId ?? id;
    await runConversationAction("markAllUnread", {
      url: `/api/messages/${encodeURIComponent(targetMessageId)}/conversation/read`,
      method: "DELETE",
      successMessage: t("messages.flash.conversationMarkedUnread", "Conversation marked unread."),
      onSuccess: onMarkedUnread
    });
  }, [id, onMarkedUnread, runConversationAction, suppressAutoMarkRead, t]);
  const markConversationRead = React.useCallback(async (messageId) => {
    const targetMessageId = messageId ?? id;
    await runConversationAction("markAllRead", {
      url: `/api/messages/${encodeURIComponent(targetMessageId)}/conversation/read`,
      method: "PUT",
      successMessage: t("messages.flash.conversationMarkedRead", "Conversation marked read.")
    });
  }, [id, runConversationAction, t]);
  const deleteConversation = React.useCallback(async (messageId) => {
    const targetMessageId = messageId ?? id;
    await runConversationAction("deleteConversation", {
      url: `/api/messages/${encodeURIComponent(targetMessageId)}/conversation`,
      method: "DELETE",
      successMessage: t("messages.flash.conversationDeleted", "Conversation deleted."),
      onSuccess: onDeleted
    });
  }, [id, onDeleted, runConversationAction, t]);
  const handleDeleteDialogKeyDown = React.useCallback((event) => {
    if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
      event.preventDefault();
      void handleDelete();
      return;
    }
    if (event.key === "Escape") {
      event.preventDefault();
      setDeleteConfirmationOpen(false);
    }
  }, [handleDelete]);
  const executeAction = React.useCallback(async (action, payload) => {
    setExecutingActionId(action.id);
    try {
      const call = await runMutation({
        // Carry the OSS optimistic-lock header so a stale tab cannot execute an
        // action against a concurrently-changed message; throw a status-carrying
        // error on failure so the 409 conflict surfaces on the shared banner.
        operation: () => withScopedApiRequestHeaders(
          buildOptimisticLockHeader(detail?.updatedAt),
          async () => {
            const result = await apiCall(
              `/api/messages/${encodeURIComponent(id)}/actions/${encodeURIComponent(action.id)}`,
              {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify(payload ?? {})
              }
            );
            if (!result.ok) {
              throw Object.assign(
                new Error(toErrorMessage(result.result) ?? t("messages.errors.actionFailed", "Failed to execute action.")),
                {
                  status: result.status,
                  ...result.result && typeof result.result === "object" ? result.result : {}
                }
              );
            }
            return result;
          }
        ),
        context: { resourceKind: "message", messageId: id, action: "execute-action", retryLastMutation },
        mutationPayload: { messageId: id, action: "execute-action", actionId: action.id }
      });
      flash(t("messages.flash.actionSuccess", "Action completed."), "success");
      const redirectTarget = call.result?.result?.redirect;
      if (typeof redirectTarget === "string" && redirectTarget.trim().length > 0 && isSafeNavigationHref(redirectTarget)) {
        window.location.href = redirectTarget;
        return;
      }
      await detailQuery.refetch();
    } catch (err) {
      if (surfaceRecordConflict(err, t)) return;
      flash(
        err instanceof Error ? err.message : t("messages.errors.actionFailed", "Failed to execute action."),
        "error"
      );
    } finally {
      setExecutingActionId(null);
    }
  }, [detail?.updatedAt, detailQuery, id, retryLastMutation, runMutation, t]);
  const handleExecuteAction = React.useCallback(async (action, payload) => {
    if (executingActionId || detail?.actionTaken) return;
    if (action.confirmRequired) {
      setPendingActionConfirmation({ action, payload });
      return;
    }
    await executeAction(action, payload);
  }, [detail?.actionTaken, executeAction, executingActionId]);
  const handleConfirmPendingAction = React.useCallback(async () => {
    if (!pendingActionConfirmation || executingActionId || detail?.actionTaken) return;
    const { action, payload } = pendingActionConfirmation;
    setPendingActionConfirmation(null);
    await executeAction(action, payload);
  }, [detail?.actionTaken, executeAction, executingActionId, pendingActionConfirmation]);
  const handleActionConfirmDialogKeyDown = React.useCallback((event) => {
    if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
      event.preventDefault();
      void handleConfirmPendingAction();
      return;
    }
    if (event.key === "Escape") {
      event.preventDefault();
      setPendingActionConfirmation(null);
    }
  }, [handleConfirmPendingAction]);
  const contentProps = React.useMemo(() => ({
    message: {
      id: detail?.id ?? "",
      type: detail?.type ?? "default",
      subject: detail?.subject ?? "",
      body: detail?.body ?? "",
      bodyFormat: detail?.bodyFormat ?? "text",
      priority: detail?.priority ?? "normal",
      sentAt: detail?.sentAt ? new Date(detail.sentAt) : null,
      senderName: detail?.senderName ?? void 0,
      senderUserId: detail?.senderUserId ?? "",
      actionData: detail?.actionData ?? null,
      actionTaken: detail?.actionTaken ?? null,
      actionTakenAt: detail?.actionTakenAt ? new Date(detail.actionTakenAt) : null
    },
    objects: (detail?.objects ?? []).map((item) => ({
      id: item.id,
      entityModule: item.entityModule,
      entityType: item.entityType,
      entityId: item.entityId,
      actionRequired: item.actionRequired,
      snapshot: item.snapshot ?? void 0
    })),
    attachments: (attachments ?? []).map((item) => ({
      id: item.id,
      fileName: item.fileName,
      fileSize: item.fileSize,
      mimeType: item.mimeType,
      url: item.url
    }))
  }), [attachments, detail]);
  const handleExecuteActionById = React.useCallback(async (actionId, payload) => {
    const action = detail?.actionData?.actions?.find((entry) => {
      if (parseObjectActionId(entry.id)) return false;
      return entry.id === actionId;
    });
    if (!action) return;
    await handleExecuteAction(action, payload);
  }, [detail?.actionData?.actions, handleExecuteAction]);
  const messageActions = React.useMemo(() => {
    return (detail?.actionData?.actions ?? []).filter((action) => !parseObjectActionId(action.id));
  }, [detail?.actionData?.actions]);
  const objectActionsByObjectId = React.useMemo(() => {
    const byObjectId = /* @__PURE__ */ new Map();
    for (const action of detail?.actionData?.actions ?? []) {
      const parsed = parseObjectActionId(action.id);
      if (!parsed) continue;
      const current = byObjectId.get(parsed.objectId) ?? /* @__PURE__ */ new Map();
      current.set(parsed.actionId, action);
      byObjectId.set(parsed.objectId, current);
    }
    return byObjectId;
  }, [detail?.actionData?.actions]);
  const toggleRead = React.useCallback(async () => {
    if (detail?.isRead) {
      suppressAutoMarkRead();
    }
    await requestAndRefresh(
      `/api/messages/${encodeURIComponent(id)}/read`,
      detail?.isRead ? "DELETE" : "PUT",
      detail?.isRead ? { skipDetailAutoMarkRead: true, onSuccess: onMarkedUnread } : void 0
    );
  }, [detail?.isRead, id, onMarkedUnread, requestAndRefresh, suppressAutoMarkRead]);
  const toggleArchive = React.useCallback(async () => {
    await requestAndRefresh(
      `/api/messages/${encodeURIComponent(id)}/archive`,
      isArchived ? "DELETE" : "PUT"
    );
  }, [id, isArchived, requestAndRefresh]);
  return {
    replyOpen,
    setReplyOpen,
    forwardOpen,
    setForwardOpen,
    editOpen,
    setEditOpen,
    updatingState,
    executingActionId,
    pendingActionConfirmation,
    setPendingActionConfirmation,
    deleteConfirmationOpen,
    setDeleteConfirmationOpen,
    activeConversationAction,
    toggleRead,
    toggleArchive,
    archiveConversation,
    unarchiveConversation,
    deleteConversation,
    markConversationUnread,
    markConversationRead,
    requestAndRefresh,
    handleDelete,
    handleDeleteDialogKeyDown,
    handleExecuteAction,
    handleConfirmPendingAction,
    handleActionConfirmDialogKeyDown,
    contentProps,
    handleExecuteActionById,
    messageActions,
    objectActionsByObjectId
  };
}
export {
  useMessageDetailsActions
};
//# sourceMappingURL=useMessageDetailsActions.js.map
