import { useEffectEvent } from "react";
import { toast } from "sonner";
import { invalidateAiProfilesCache } from "../../services/aiService";
import { EDIT_OPERATION_TERMINAL_EVENT } from "../waitForEditOperationTerminal";
export function useSocketMessageHandler(deps) {
    const { sessionId, mode, setWebSocketMessages, setActiveSessions, sendClientInfo, itemsRepository, currentItemDescriptor, loadItem, loadItemVersions, setComments, setSuggestedEdits, setActiveFieldActions, setQuotaInfo, requestRefresh, currentItemRef, addRecentEdit, socketMessageListeners, isItemUsedInCurrentPage, updateOperationInHistory, markOperationComplete, } = deps;
    // Wrap the entire message handler in useEffectEvent to ensure it always
    // accesses the latest values (e.g., viewName in loadItem) without stale closures
    const handleMessage = useEffectEvent(async (event) => {
        if (!event.data?.toString?.().startsWith?.("{"))
            return;
        const message = JSON.parse(event.data);
        if (message.type === "user:notification") {
        }
        // Ensure AI profile callers don't get stuck on the TTL cache after updates.
        if (message.type === "agent-profiles-changed") {
            try {
                invalidateAiProfilesCache();
            }
            catch { }
        }
        try {
            const webSocketMessage = {
                id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
                timestamp: new Date().toISOString(),
                type: message.type || "unknown",
                payload: message.payload,
                rawMessage: JSON.stringify(message, null, 2),
            };
            setWebSocketMessages((prev) => [webSocketMessage, ...prev].slice(0, 1000));
        }
        catch { }
        if (message.type === "active-sessions") {
            setActiveSessions((prev) => {
                if (!Array.isArray(message.payload)) {
                    globalThis.__alpacaActiveSessions = prev;
                    return prev;
                }
                const valid = message.payload.filter((s) => s && s.sessionId && s.user);
                if (valid.length === 0 && prev.length > 0) {
                    globalThis.__alpacaActiveSessions = prev;
                    return prev;
                }
                globalThis.__alpacaActiveSessions = valid;
                return valid;
            });
            try {
                const payload = Array.isArray(message.payload)
                    ? message.payload
                    : [];
                const hasMySession = payload.some((s) => s && s.sessionId === sessionId);
                if (!hasMySession)
                    setTimeout(() => sendClientInfo(), 300);
            }
            catch { }
        }
        if (message.type === "session:revoked") {
            try {
                window.dispatchEvent(new CustomEvent("parhelia:session-revoked", {
                    detail: { reason: message.payload?.reason ?? "session-revoked" },
                }));
            }
            catch { }
            return;
        }
        if (message.type === "item-changed") {
            if (message.payload.action === "delete") {
                const parentId = message.payload?.parentId ?? message.payload?.changes?.parentId;
                itemsRepository.onItemsDeleted([
                    { item: message.payload.item, parentId },
                ]);
                if (message.payload.item.id === currentItemDescriptor?.id && parentId) {
                    loadItem({
                        id: parentId,
                        language: currentItemDescriptor?.language ?? "en",
                        version: 0,
                    });
                }
            }
            else {
                await itemsRepository.refreshItems([message.payload.item], message.payload.changes);
                const changes = message?.payload?.changes;
                const isVersionChange = changes?.versions === true;
                // Invalidate version 0 cache when versions change so "latest" resolves correctly
                if (isVersionChange &&
                    message.payload.item?.id &&
                    message.payload.item?.language) {
                    itemsRepository.invalidateVersionZeroCache(message.payload.item.id, message.payload.item.language);
                }
                if (isVersionChange &&
                    message.payload.item.id === currentItemDescriptor?.id) {
                    await loadItemVersions({ preserveOnError: true });
                }
                // If the current view is "Latest" (v0) and versions changed for the current item,
                // re-resolve latest->concrete version mapping so the iframe can refresh to the new version.
                if (isVersionChange &&
                    currentItemDescriptor?.id === message.payload.item.id &&
                    currentItemDescriptor?.version === 0) {
                    try {
                        window.dispatchEvent(new CustomEvent("parhelia:item-version-added", {
                            detail: {
                                itemId: message.payload?.item?.id ?? null,
                                language: message.payload?.item?.language ?? null,
                            },
                        }));
                    }
                    catch { }
                }
            }
        }
        if (message.type === "item-version-added") {
            // Invalidate version 0 cache for this item so "latest" resolves to the new version
            const versionAddedItem = message.payload?.item;
            if (versionAddedItem?.id && versionAddedItem?.language) {
                itemsRepository.invalidateVersionZeroCache(versionAddedItem.id, versionAddedItem.language);
            }
            if (currentItemRef.current) {
                if (currentItemRef.current.id === message.payload.item.id) {
                    await loadItemVersions({ preserveOnError: true });
                    // Auto-reload item content if "Latest" (version 0) is selected
                    // IMPORTANT: contentEditorItem.descriptor is the RESOLVED version (e.g. 9),
                    // while the selection can still be "Latest" (version 0).
                    const isLatestSelected = currentItemDescriptor?.version === 0;
                    if (isLatestSelected) {
                        // Only show toast if this version was added by another user/session
                        const isOwnVersion = message.payload.sessionId === sessionId;
                        if (!isOwnVersion) {
                            toast.info("A new version was added. Content has been updated.");
                        }
                        // Reload the item to show the new version's content
                        // Using await to ensure repository is updated before refresh
                        // Keep the selection on "Latest" (v0); the item itself resolves to the newest version.
                        if (currentItemDescriptor) {
                            await loadItem({ ...currentItemDescriptor }, { skipViewChange: true });
                        }
                        // Signal page-view context to re-resolve "Latest" → concrete version mapping.
                        // This avoids relying on itemsRepository.revision (which caused refresh loops while typing).
                        try {
                            window.dispatchEvent(new CustomEvent("parhelia:item-version-added", {
                                detail: {
                                    itemId: message.payload?.item?.id ?? null,
                                    language: message.payload?.item?.language ?? null,
                                },
                            }));
                        }
                        catch { }
                        // We intentionally avoid calling requestRefresh here:
                        // the page-view context will update editUrl to the new concrete version,
                        // and that change will drive an iframe refresh without a race.
                    }
                    else {
                        // Non-latest selections: refresh only the current view.
                        requestRefresh("immediate");
                    }
                }
            }
        }
        if (message.type === "translation-finished") {
            if (currentItemRef.current?.id === message.payload.itemId) {
                // Final refresh when translation finishes to ensure preview is up to date
                // If translation completion produced a new version while we're viewing "Latest" (v0),
                // re-resolve latest->concrete version mapping so the iframe follows the new version.
                const isLatestSelected = currentItemDescriptor?.version === 0;
                if (isLatestSelected) {
                    try {
                        window.dispatchEvent(new CustomEvent("parhelia:item-version-added", {
                            detail: {
                                itemId: message.payload?.itemId ?? null,
                                language: currentItemDescriptor?.language ?? null,
                            },
                        }));
                    }
                    catch { }
                    // Like item-version-added: avoid requestRefresh race; editUrl change will refresh iframe.
                    return;
                }
                requestRefresh("delayed");
            }
        }
        if (message.type === "comment-updated") {
            setComments((x) => {
                const newComments = [...x];
                const index = newComments.findIndex((c) => c.id === message.payload.comment.id);
                if (index !== -1)
                    newComments[index] = message.payload.comment;
                else
                    newComments.push(message.payload.comment);
                return newComments;
            });
        }
        if (message.type === "comment-deleted") {
            setComments((x) => x.filter((c) => c.id !== message.payload.commentId));
        }
        if (message.type === "suggested-edit-updated") {
            setSuggestedEdits((x) => {
                const incoming = message.payload.suggestedEdit;
                const updated = [...x];
                const index = updated.findIndex((s) => s.id === incoming.id);
                if (index !== -1) {
                    updated[index] = incoming;
                    return updated;
                }
                return [
                    ...updated.filter((s) => !(s.status === "pending" &&
                        s.mainItemId === incoming.mainItemId &&
                        s.mainItemLanguage === incoming.mainItemLanguage &&
                        s.mainItemVersion === incoming.mainItemVersion &&
                        s.itemId === incoming.itemId &&
                        s.fieldId === incoming.fieldId &&
                        s.author === incoming.author)),
                    incoming,
                ];
            });
        }
        if (message.type === "suggested-edit-deleted") {
            setSuggestedEdits((x) => x.filter((s) => s.id !== message.payload.id));
        }
        if (message.type === "executing-field-action") {
            const payload = message.payload;
            const fieldId = payload.fieldId;
            const item = payload.item;
            const status = payload.status;
            const msg = payload.message;
            const label = payload.label;
            const normalized = status?.toLowerCase?.() ?? "";
            const isSuccess = normalized === "completed" || normalized === "success";
            const isError = normalized === "failed" || normalized === "error";
            // On success, refresh item(s) and dismiss the field action overlay
            if (isSuccess) {
                try {
                    await itemsRepository.refreshItems([item]);
                }
                catch { }
                requestRefresh("immediate");
                setActiveFieldActions((x) => x.filter((action) => !(action.field.fieldId === fieldId &&
                    action.field.item.id === item.id &&
                    action.field.item.language === item.language &&
                    action.field.item.version === item.version)));
                return;
            }
            setActiveFieldActions((x) => {
                let state;
                state = isError ? "error" : "running";
                const existingActionIndex = x.findIndex((action) => action.field.fieldId === fieldId &&
                    action.field.item.id === item.id &&
                    action.field.item.language === item.language &&
                    action.field.item.version === item.version);
                if (existingActionIndex !== -1) {
                    const newActions = [...x];
                    newActions[existingActionIndex].state = state;
                    newActions[existingActionIndex].message = msg;
                    return newActions;
                }
                else {
                    const fieldDescriptor = { fieldId, item };
                    const newAction = {
                        field: fieldDescriptor,
                        state,
                        message: msg,
                        label,
                    };
                    return [...x, newAction];
                }
            });
        }
        if (message.type === "update-quota")
            setQuotaInfo(message.payload);
        if (message.type === "load-item") {
            const itemDescriptor = message.payload;
            if (itemDescriptor)
                loadItem(itemDescriptor, { skipViewChange: false });
        }
        if (message.type === "edit-operation") {
            const op = message.payload;
            const isOwnOperation = !!op.sessionId && !!sessionId && op.sessionId === sessionId;
            if (op.type === "edit-field") {
                const editFieldOperation = op;
                const field = await itemsRepository.getField({
                    item: {
                        ...editFieldOperation.mainItem,
                        id: editFieldOperation.itemId,
                    },
                    fieldId: editFieldOperation.fieldId,
                });
                const fieldId = String(editFieldOperation.fieldId ?? "");
                const isIconField = fieldId === "__Icon" ||
                    fieldId.toLowerCase() === "06d5295c-ed2f-4a54-9bf2-26228d113318";
                if (!field ||
                    isIconField ||
                    (field.type !== "single-line text" &&
                        field.type !== "multi-line text" &&
                        field.type !== "rich text")) {
                    itemsRepository.refreshItems([
                        {
                            ...editFieldOperation.mainItem,
                            id: editFieldOperation.itemId,
                        },
                    ], { properties: true });
                    // Only refresh the page if it is affected
                    const pageDescriptor = currentItemRef.current?.descriptor;
                    const isSamePage = !!pageDescriptor &&
                        !!op.mainItem &&
                        op.mainItem.id === pageDescriptor.id &&
                        op.mainItem.language === pageDescriptor.language &&
                        op.mainItem.version === pageDescriptor.version;
                    const targetItem = {
                        id: editFieldOperation.itemId,
                        language: editFieldOperation.mainItem?.language,
                        version: editFieldOperation.mainItem?.version,
                    };
                    if (isSamePage || isItemUsedInCurrentPage(targetItem)) {
                        if (isOwnOperation) {
                        }
                        else {
                            requestRefresh("immediate");
                        }
                    }
                }
                else {
                    // For our own edit-field ops, skip duplicating local field updates but still continue
                    // so the edit history gets refreshed below.
                    const isOwnNonAiOperation = !!op.sessionId &&
                        op.sessionId === sessionId &&
                        // Treat only non-AI user edits from *this* session as "local" edits
                        // that have already updated the client-side state.
                        !!op.user &&
                        op.user.ai === false &&
                        !op.agentId;
                    if (!isOwnNonAiOperation) {
                        const userForUpdate = editFieldOperation.user ?? {
                            name: "unknown",
                            ai: !!(editFieldOperation.agentId || op.agentId),
                            agentId: editFieldOperation.agentId || op.agentId,
                        };
                        itemsRepository.updateFieldValue({
                            fieldId: editFieldOperation.fieldId,
                            item: {
                                ...editFieldOperation.mainItem,
                                id: editFieldOperation.itemId,
                            },
                        }, userForUpdate, op.executionStatus === "pending", // Set isDirty based on executionStatus
                        editFieldOperation.undone
                            ? editFieldOperation.oldValue
                            : editFieldOperation.value);
                        // In preview mode, the InlineEditor is not active, so we need to refresh the page
                        // to see the updated field values.
                        if (mode === "preview") {
                            const pageDescriptor = currentItemRef.current?.descriptor;
                            const isSamePage = !!pageDescriptor &&
                                !!op.mainItem &&
                                op.mainItem.id === pageDescriptor.id &&
                                op.mainItem.language === pageDescriptor.language &&
                                op.mainItem.version === pageDescriptor.version;
                            const targetItem = {
                                id: editFieldOperation.itemId,
                                language: editFieldOperation.mainItem?.language,
                                version: editFieldOperation.mainItem?.version,
                            };
                            if (isSamePage || isItemUsedInCurrentPage(targetItem)) {
                                requestRefresh("delayed");
                            }
                        }
                    }
                }
            }
            else {
                // For non-field operations, refresh only if the current page matches the operation's main item
                const pageDescriptor = currentItemRef.current?.descriptor;
                const shouldRefresh = !!pageDescriptor &&
                    !!op.mainItem &&
                    op.mainItem.id === pageDescriptor.id &&
                    op.mainItem.language === pageDescriptor.language &&
                    op.mainItem.version === pageDescriptor.version;
                // Use the same AI-aware check as the edit-field branch:
                // Only skip refresh for own non-AI operations (where the UI
                // already reflects the change locally). AI/agent operations
                // from the same session still need a refresh because the
                // frontend has no local optimistic state for them.
                const isOwnNonAiOperation = isOwnOperation &&
                    !!op.user &&
                    op.user.ai === false &&
                    !op.agentId;
                if (shouldRefresh) {
                    if (isOwnNonAiOperation) {
                    }
                    else {
                        requestRefresh("immediate");
                    }
                }
            }
            // Update history directly with the operation from websocket
            // This avoids race conditions with server fetches and respects the current mode scope
            if (updateOperationInHistory) {
                updateOperationInHistory(op);
            }
        }
        // Long-running operation progress messages
        if (message.type === "operation:started") {
            // A new long-running operation has started
            const payload = message.payload;
            const op = {
                id: payload.operationId,
                type: payload.type,
                title: payload.title,
                // Use sessionId from payload if available, otherwise use current session
                sessionId: payload.sessionId || sessionId,
                date: payload.timestamp || new Date().toISOString(),
                mainItem: payload.mainItem,
                executionStatus: "executing",
                progress: 0,
                statusMessage: payload.statusMessage || "Starting...",
                executionStarted: payload.timestamp,
                // Executing operations cannot be undone/redone
                canUndo: false,
                canRedo: false,
            };
            if (updateOperationInHistory) {
                updateOperationInHistory(op);
            }
        }
        if (message.type === "operation:progress") {
            // Update progress on an existing operation
            const payload = message.payload;
            if (updateOperationInHistory) {
                updateOperationInHistory({
                    id: payload.operationId,
                    // Use sessionId from payload if available, otherwise use current session
                    // This ensures the operation matches the scope filter for "my changes"
                    sessionId: payload.sessionId || sessionId,
                    // Include date in case operation doesn't exist in history yet (missed operation:started)
                    date: payload.timestamp || new Date().toISOString(),
                    executionStatus: "executing",
                    progress: payload.progress,
                    statusMessage: payload.statusMessage,
                    // Executing operations cannot be undone/redone
                    canUndo: false,
                    canRedo: false,
                });
            }
        }
        if (message.type === "operation:completed") {
            // Operation completed successfully
            const payload = message.payload;
            const op = {
                id: payload.operationId || payload.id,
                // Use sessionId from payload if available, otherwise use current session
                sessionId: payload.sessionId || sessionId,
                // Include date in case operation doesn't exist in history yet
                date: payload.date || payload.timestamp || new Date().toISOString(),
                type: payload.type,
                title: payload.title,
                description: payload.description,
                executionStatus: "completed",
                progress: 100,
                statusMessage: "Completed",
                executionCompleted: payload.executionCompleted,
                canUndo: payload.canUndo,
                undoSupported: payload.undoSupported,
                undoNotSupportedReason: payload.undoNotSupportedReason,
                mainItem: payload.mainItem,
                createdItemIds: payload.createdItemIds,
                sourceItemIds: payload.sourceItemIds,
                targetParentId: payload.targetParentId,
            };
            if (updateOperationInHistory) {
                updateOperationInHistory(op);
            }
            {
                const oid = payload.operationId || payload.id;
                if (typeof window !== "undefined" && oid) {
                    window.dispatchEvent(new CustomEvent(EDIT_OPERATION_TERMINAL_EVENT, {
                        detail: {
                            operationId: oid,
                            id: oid,
                            createdItemIds: payload.createdItemIds,
                            successCount: payload.successCount,
                            failureCount: payload.failureCount,
                            summary: payload.summary,
                        },
                    }));
                }
            }
            // Remove from executing operations list
            if (markOperationComplete && payload.operationId) {
                markOperationComplete(payload.operationId);
            }
            // Refresh if it affects current page
            const pageDescriptor = currentItemRef.current?.descriptor;
            if (pageDescriptor &&
                payload.mainItem &&
                payload.mainItem.id === pageDescriptor.id) {
                requestRefresh("immediate");
            }
        }
        if (message.type === "operation:failed") {
            // Operation failed
            const payload = message.payload;
            const op = {
                id: payload.operationId || payload.id,
                // Use sessionId from payload if available, otherwise use current session
                sessionId: payload.sessionId || sessionId,
                // Include date in case operation doesn't exist in history yet
                date: payload.date || payload.timestamp || new Date().toISOString(),
                type: payload.type,
                title: payload.title,
                executionStatus: "failed",
                errorMessage: payload.errorMessage,
                statusMessage: "Failed",
                executionCompleted: payload.executionCompleted,
                canUndo: payload.canUndo,
            };
            if (updateOperationInHistory) {
                updateOperationInHistory(op);
            }
            {
                const oid = payload.operationId || payload.id;
                if (typeof window !== "undefined" && oid) {
                    window.dispatchEvent(new CustomEvent(EDIT_OPERATION_TERMINAL_EVENT, {
                        detail: {
                            operationId: oid,
                            id: oid,
                            errorMessage: payload.errorMessage ?? "Operation failed",
                        },
                    }));
                }
            }
            // Remove from executing operations list
            if (markOperationComplete && payload.operationId) {
                markOperationComplete(payload.operationId);
            }
        }
        // Handle agent dialog requests
        if (message.type === "agent-dialog-request") {
            const request = message.payload;
            if (request && request.callbackId) {
                // Dispatch custom event for AgentDialogHandler to pick up
                window.dispatchEvent(new CustomEvent("agent:dialog:request", { detail: request }));
            }
        }
        socketMessageListeners.current.forEach((listener) => listener(message));
    });
    return handleMessage;
}
//# sourceMappingURL=useSocketMessageHandler.js.map