"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getChatConnectionSnapshot = getChatConnectionSnapshot;
exports.subscribeChatConnection = subscribeChatConnection;
exports.getSharedChatRuntimeSnapshot = getSharedChatRuntimeSnapshot;
exports.setSharedChatRuntimeSnapshot = setSharedChatRuntimeSnapshot;
exports.useChatConnection = useChatConnection;
var react_1 = require("react");
var chatUtils_1 = require("./chatUtils");
var tokenAccess_1 = require("../tokenAccess");
var DISCONNECT_UI_DELAY_MS = 1500;
var PRESENCE_PING_INTERVAL_MS = 20000;
var sharedChatConnection = {
    connectionState: 'connecting',
    connectionErrorDetail: '',
    runtimeSnapshot: null,
    socketRef: { current: null },
    reconnectTimerId: null,
    disconnectUiTimerId: null,
    connectTimeoutId: null,
    sessionId: '',
    currentContext: null,
    setMessages: null,
    onMessageEvent: undefined,
    onJobEvent: undefined,
    onRuntimeEvent: undefined,
    onRuntimeDetailEvent: undefined,
    onActivityEvent: undefined,
    lastEventId: 0,
    websocketUrl: '',
    subscribers: new Set(),
    pingSubscriberCount: 0,
    consumerCount: 0,
    pingIntervalId: null,
    visibilityHandlerInstalled: false,
    pageShowHandlerInstalled: false,
    focusHandlerInstalled: false,
    blurHandlerInstalled: false,
    onlineHandlerInstalled: false,
    hasConnectedOnce: false,
    suppressDisconnectNotification: false,
    lastBackgroundAt: null,
};
function emitSharedState() {
    sharedChatConnection.subscribers.forEach(function (listener) {
        listener();
    });
}
function getSnapshot() {
    return {
        connectionState: sharedChatConnection.connectionState,
        connectionErrorDetail: sharedChatConnection.connectionErrorDetail,
        runtimeSnapshot: sharedChatConnection.runtimeSnapshot,
    };
}
function getChatConnectionSnapshot() {
    return getSnapshot();
}
function subscribeChatConnection(listener) {
    sharedChatConnection.subscribers.add(listener);
    return function () {
        sharedChatConnection.subscribers.delete(listener);
    };
}
function getSharedChatRuntimeSnapshot() {
    return sharedChatConnection.runtimeSnapshot;
}
function setSharedChatRuntimeSnapshot(snapshot) {
    if (sharedChatConnection.runtimeSnapshot === snapshot) {
        return;
    }
    sharedChatConnection.runtimeSnapshot = snapshot;
    emitSharedState();
}
function setSharedConnectionState(nextState) {
    if (sharedChatConnection.connectionState === nextState) {
        return;
    }
    sharedChatConnection.connectionState = nextState;
    emitSharedState();
}
function setSharedConnectionError(detail) {
    if (sharedChatConnection.connectionErrorDetail === detail) {
        return;
    }
    sharedChatConnection.connectionErrorDetail = detail;
    emitSharedState();
}
function clearReconnectTimer() {
    if (sharedChatConnection.reconnectTimerId !== null) {
        window.clearTimeout(sharedChatConnection.reconnectTimerId);
        sharedChatConnection.reconnectTimerId = null;
    }
}
function clearDisconnectUiTimer() {
    if (sharedChatConnection.disconnectUiTimerId !== null) {
        window.clearTimeout(sharedChatConnection.disconnectUiTimerId);
        sharedChatConnection.disconnectUiTimerId = null;
    }
}
function clearConnectTimeout() {
    if (sharedChatConnection.connectTimeoutId !== null) {
        window.clearTimeout(sharedChatConnection.connectTimeoutId);
        sharedChatConnection.connectTimeoutId = null;
    }
}
function sendContextUpdate(context) {
    if (context === void 0) { context = sharedChatConnection.currentContext; }
    var socket = sharedChatConnection.socketRef.current;
    if (!socket || socket.readyState !== WebSocket.OPEN || !context) {
        return;
    }
    var liveVisibilityState = typeof document !== 'undefined' && document.visibilityState === 'hidden' ? 'hidden' : 'visible';
    var liveFocusState = typeof document !== 'undefined' && typeof document.hasFocus === 'function' && !document.hasFocus()
        ? 'blurred'
        : 'focused';
    var livePageUrl = typeof window !== 'undefined' ? window.location.href : context.pageUrl;
    socket.send(JSON.stringify({
        type: 'context:update',
        payload: {
            pageId: context.pageId,
            pageTitle: context.pageTitle,
            topMenu: context.topMenu,
            focusedComponentId: context.focusedComponentId,
            pageUrl: livePageUrl,
            isStandaloneMode: context.isStandaloneMode,
            pageVisibilityState: liveVisibilityState,
            pageFocusState: liveFocusState,
            chatTypeId: context.chatTypeId,
            chatTypeLabel: context.chatTypeLabel,
            chatTypeDescription: context.chatTypeDescription,
        },
    }));
}
function sendPresencePing() {
    var socket = sharedChatConnection.socketRef.current;
    if (!socket || socket.readyState !== WebSocket.OPEN) {
        return;
    }
    socket.send(JSON.stringify({
        type: 'presence:ping',
        payload: {
            at: Date.now(),
        },
    }));
}
function ensureSharedSocket() {
    var socket = sharedChatConnection.socketRef.current;
    if (socket && (socket.readyState === WebSocket.OPEN || socket.readyState === WebSocket.CONNECTING)) {
        return;
    }
    connectSharedSocket();
}
function sendEventReceived(eventId) {
    var socket = sharedChatConnection.socketRef.current;
    if (!socket || socket.readyState !== WebSocket.OPEN || !Number.isFinite(eventId) || eventId <= 0) {
        return;
    }
    socket.send(JSON.stringify({
        type: 'event:received',
        payload: {
            eventId: eventId,
        },
    }));
}
function stopPresenceMonitoring() {
    if (sharedChatConnection.pingIntervalId !== null) {
        window.clearInterval(sharedChatConnection.pingIntervalId);
        sharedChatConnection.pingIntervalId = null;
    }
    if (sharedChatConnection.visibilityHandlerInstalled) {
        window.removeEventListener('visibilitychange', handleVisibilityChange);
        sharedChatConnection.visibilityHandlerInstalled = false;
    }
    if (sharedChatConnection.pageShowHandlerInstalled) {
        window.removeEventListener('pageshow', handlePageShow);
        sharedChatConnection.pageShowHandlerInstalled = false;
    }
    if (sharedChatConnection.focusHandlerInstalled) {
        window.removeEventListener('focus', handleWindowFocus);
        sharedChatConnection.focusHandlerInstalled = false;
    }
    if (sharedChatConnection.blurHandlerInstalled) {
        window.removeEventListener('blur', handleWindowBlur);
        sharedChatConnection.blurHandlerInstalled = false;
    }
    if (sharedChatConnection.onlineHandlerInstalled) {
        window.removeEventListener('online', handleWindowOnline);
        sharedChatConnection.onlineHandlerInstalled = false;
    }
}
function handleVisibilityChange() {
    if (typeof document !== 'undefined' && document.visibilityState === 'hidden') {
        sharedChatConnection.lastBackgroundAt = Date.now();
        sendContextUpdate(sharedChatConnection.currentContext);
        sendPresencePing();
        return;
    }
    ensureSharedSocket();
    sendPresencePing();
    sendContextUpdate(sharedChatConnection.currentContext);
}
function handlePageShow() {
    ensureSharedSocket();
    sendPresencePing();
    sendContextUpdate(sharedChatConnection.currentContext);
}
function handleWindowFocus() {
    ensureSharedSocket();
    sendPresencePing();
    sendContextUpdate(sharedChatConnection.currentContext);
}
function handleWindowBlur() {
    sendContextUpdate(sharedChatConnection.currentContext);
    sendPresencePing();
}
function handleWindowOnline() {
    ensureSharedSocket();
}
function startPresenceMonitoring() {
    if (sharedChatConnection.pingSubscriberCount <= 0 || sharedChatConnection.connectionState !== 'connected') {
        stopPresenceMonitoring();
        return;
    }
    sharedChatConnection.lastBackgroundAt = null;
    sendPresencePing();
    if (sharedChatConnection.pingIntervalId === null) {
        sharedChatConnection.pingIntervalId = window.setInterval(function () {
            sendPresencePing();
        }, PRESENCE_PING_INTERVAL_MS);
    }
    if (!sharedChatConnection.visibilityHandlerInstalled) {
        window.addEventListener('visibilitychange', handleVisibilityChange);
        sharedChatConnection.visibilityHandlerInstalled = true;
    }
    if (!sharedChatConnection.pageShowHandlerInstalled) {
        window.addEventListener('pageshow', handlePageShow);
        sharedChatConnection.pageShowHandlerInstalled = true;
    }
    if (!sharedChatConnection.focusHandlerInstalled) {
        window.addEventListener('focus', handleWindowFocus);
        sharedChatConnection.focusHandlerInstalled = true;
    }
    if (!sharedChatConnection.blurHandlerInstalled) {
        window.addEventListener('blur', handleWindowBlur);
        sharedChatConnection.blurHandlerInstalled = true;
    }
    if (!sharedChatConnection.onlineHandlerInstalled) {
        window.addEventListener('online', handleWindowOnline);
        sharedChatConnection.onlineHandlerInstalled = true;
    }
}
function scheduleReconnect() {
    if (sharedChatConnection.reconnectTimerId !== null || !sharedChatConnection.sessionId) {
        return;
    }
    sharedChatConnection.reconnectTimerId = window.setTimeout(function () {
        sharedChatConnection.reconnectTimerId = null;
        connectSharedSocket();
    }, chatUtils_1.CHAT_CONNECTION.reconnectDelayMs);
}
function handleSharedDisconnect(message, detail) {
    setSharedConnectionError(detail !== null && detail !== void 0 ? detail : '');
    clearDisconnectUiTimer();
    if (sharedChatConnection.connectionState !== 'connected') {
        setSharedConnectionState('disconnected');
    }
    else {
        sharedChatConnection.disconnectUiTimerId = window.setTimeout(function () {
            var _a;
            sharedChatConnection.disconnectUiTimerId = null;
            if (((_a = sharedChatConnection.socketRef.current) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
                return;
            }
            setSharedConnectionState('disconnected');
        }, DISCONNECT_UI_DELAY_MS);
    }
    if (message) {
        scheduleReconnect();
    }
}
function disconnectSharedSocket() {
    clearReconnectTimer();
    clearConnectTimeout();
    clearDisconnectUiTimer();
    stopPresenceMonitoring();
    var socket = sharedChatConnection.socketRef.current;
    sharedChatConnection.suppressDisconnectNotification = true;
    sharedChatConnection.socketRef.current = null;
    socket === null || socket === void 0 ? void 0 : socket.close();
}
function releaseSharedConnectionConsumer() {
    sharedChatConnection.consumerCount = Math.max(0, sharedChatConnection.consumerCount - 1);
    if (sharedChatConnection.consumerCount > 0) {
        return;
    }
    sharedChatConnection.currentContext = null;
    sharedChatConnection.setMessages = null;
    sharedChatConnection.onMessageEvent = undefined;
    sharedChatConnection.onJobEvent = undefined;
    sharedChatConnection.onRuntimeEvent = undefined;
    sharedChatConnection.onRuntimeDetailEvent = undefined;
    setSharedChatRuntimeSnapshot(null);
    disconnectSharedSocket();
    setSharedConnectionError('');
    setSharedConnectionState('disconnected');
}
function connectSharedSocket() {
    if (!sharedChatConnection.sessionId || !sharedChatConnection.setMessages) {
        return;
    }
    if (!(0, tokenAccess_1.hasRegisteredAccessTokenAccess)()) {
        clearReconnectTimer();
        clearConnectTimeout();
        clearDisconnectUiTimer();
        stopPresenceMonitoring();
        setSharedConnectionError('등록된 접근 토큰이 없어 채팅 연결을 시작하지 않았습니다.');
        setSharedConnectionState('disconnected');
        return;
    }
    var currentSocket = sharedChatConnection.socketRef.current;
    if (currentSocket && (currentSocket.readyState === WebSocket.OPEN || currentSocket.readyState === WebSocket.CONNECTING)) {
        return;
    }
    clearReconnectTimer();
    clearConnectTimeout();
    clearDisconnectUiTimer();
    if (sharedChatConnection.connectionState !== 'connected') {
        setSharedConnectionState('connecting');
    }
    sharedChatConnection.websocketUrl = (0, chatUtils_1.resolveChatWebSocketUrl)(sharedChatConnection.sessionId, sharedChatConnection.lastEventId);
    var socket;
    try {
        socket = new WebSocket(sharedChatConnection.websocketUrl);
    }
    catch (_a) {
        handleSharedDisconnect("\uC6CC\uD06C\uC11C\uBC84 WebSocket \uC8FC\uC18C\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB300\uC0C1: ".concat(sharedChatConnection.websocketUrl || '/ws/chat', " \uC790\uB3D9\uC73C\uB85C \uB2E4\uC2DC \uC5F0\uACB0\uD569\uB2C8\uB2E4."), 'WebSocket 객체를 생성하지 못했습니다. 대상 주소 형식과 환경변수를 확인해 주세요.');
        return;
    }
    sharedChatConnection.socketRef.current = socket;
    sharedChatConnection.suppressDisconnectNotification = false;
    var disconnectHandled = false;
    var reportDisconnect = function (message, closeEvent) {
        if (disconnectHandled) {
            return;
        }
        disconnectHandled = true;
        var wasSuppressed = sharedChatConnection.suppressDisconnectNotification;
        if (sharedChatConnection.socketRef.current === socket) {
            sharedChatConnection.socketRef.current = null;
        }
        sharedChatConnection.suppressDisconnectNotification = false;
        if (wasSuppressed) {
            setSharedConnectionError('');
            return;
        }
        if ((closeEvent === null || closeEvent === void 0 ? void 0 : closeEvent.code) === 1008) {
            clearReconnectTimer();
            setSharedConnectionError('등록된 접근 토큰이 없어 채팅 연결이 차단되었습니다.');
            setSharedConnectionState('disconnected');
            return;
        }
        if ((closeEvent === null || closeEvent === void 0 ? void 0 : closeEvent.code) === 1000 && !message) {
            setSharedConnectionError('');
            handleSharedDisconnect();
            scheduleReconnect();
            return;
        }
        void (0, chatUtils_1.diagnoseConnectionFailure)(sharedChatConnection.websocketUrl, closeEvent).then(function (detail) {
            handleSharedDisconnect(message, detail);
        });
    };
    sharedChatConnection.connectTimeoutId = window.setTimeout(function () {
        if (sharedChatConnection.socketRef.current !== socket || socket.readyState === WebSocket.OPEN) {
            return;
        }
        sharedChatConnection.socketRef.current = null;
        socket.close();
        reportDisconnect("\uC6CC\uD06C\uC11C\uBC84 \uC5F0\uACB0 \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB300\uC0C1: ".concat(sharedChatConnection.websocketUrl || '/ws/chat', " \uC790\uB3D9\uC73C\uB85C \uB2E4\uC2DC \uC5F0\uACB0\uD569\uB2C8\uB2E4."));
    }, chatUtils_1.CHAT_CONNECTION.connectTimeoutMs);
    socket.addEventListener('open', function () {
        clearConnectTimeout();
        clearDisconnectUiTimer();
        sharedChatConnection.hasConnectedOnce = true;
        sharedChatConnection.suppressDisconnectNotification = false;
        setSharedConnectionState('connected');
        setSharedConnectionError('');
        sendContextUpdate(sharedChatConnection.currentContext);
        startPresenceMonitoring();
    });
    socket.addEventListener('message', function (event) {
        var _a, _b;
        var setMessages = sharedChatConnection.setMessages;
        if (!setMessages) {
            return;
        }
        void (0, chatUtils_1.handleChatServerEvent)({
            eventData: String(event.data),
            currentPageUrl: (_b = (_a = sharedChatConnection.currentContext) === null || _a === void 0 ? void 0 : _a.pageUrl) !== null && _b !== void 0 ? _b : '',
            expectedSessionId: sharedChatConnection.sessionId,
            setMessages: setMessages,
            onMessageEvent: sharedChatConnection.onMessageEvent,
            onJobEvent: sharedChatConnection.onJobEvent,
            onRuntimeEvent: sharedChatConnection.onRuntimeEvent,
            onRuntimeDetailEvent: sharedChatConnection.onRuntimeDetailEvent,
            onActivityEvent: sharedChatConnection.onActivityEvent,
            onEventReceived: function (eventId) {
                sharedChatConnection.lastEventId = eventId;
                (0, chatUtils_1.persistLastReceivedChatEventId)(sharedChatConnection.sessionId, eventId);
                sendEventReceived(eventId);
            },
        });
        try {
            var parsedEvent = JSON.parse(String(event.data));
            if ((parsedEvent === null || parsedEvent === void 0 ? void 0 : parsedEvent.type) === 'chat:runtime') {
                setSharedChatRuntimeSnapshot(parsedEvent.payload);
            }
        }
        catch (_c) {
            // ignore malformed payloads here; detailed parsing is already handled downstream
        }
    });
    socket.addEventListener('close', function (event) {
        clearConnectTimeout();
        stopPresenceMonitoring();
        reportDisconnect(event.code === 1000 ? undefined : '워크서버 연결이 끊어졌습니다. 자동으로 다시 연결합니다.', event);
    });
    socket.addEventListener('error', function () {
        clearConnectTimeout();
        stopPresenceMonitoring();
        reportDisconnect('워크서버 WebSocket 연결에 실패했습니다. 자동으로 다시 연결합니다.');
    });
}
function ensureSharedConnection(options) {
    var sessionChanged = sharedChatConnection.sessionId !== options.sessionId;
    sharedChatConnection.currentContext = options.currentContext;
    sharedChatConnection.setMessages = options.setMessages;
    sharedChatConnection.onMessageEvent = options.onMessageEvent;
    sharedChatConnection.onJobEvent = options.onJobEvent;
    sharedChatConnection.onRuntimeEvent = options.onRuntimeEvent;
    sharedChatConnection.onRuntimeDetailEvent = options.onRuntimeDetailEvent;
    sharedChatConnection.onActivityEvent = options.onActivityEvent;
    if (sessionChanged) {
        sharedChatConnection.sessionId = options.sessionId;
        sharedChatConnection.lastEventId = (0, chatUtils_1.getLastReceivedChatEventId)(options.sessionId);
        sharedChatConnection.hasConnectedOnce = false;
        disconnectSharedSocket();
    }
    connectSharedSocket();
}
function useChatConnection(_a) {
    var sessionId = _a.sessionId, currentContext = _a.currentContext, setMessages = _a.setMessages, onMessageEvent = _a.onMessageEvent, onJobEvent = _a.onJobEvent, onRuntimeEvent = _a.onRuntimeEvent, onRuntimeDetailEvent = _a.onRuntimeDetailEvent, onActivityEvent = _a.onActivityEvent;
    var _b = (0, react_1.useState)(function () { return getSnapshot(); }), snapshot = _b[0], setSnapshot = _b[1];
    (0, react_1.useEffect)(function () {
        sharedChatConnection.consumerCount += 1;
        return function () {
            releaseSharedConnectionConsumer();
        };
    }, []);
    (0, react_1.useEffect)(function () {
        var handleSnapshotChange = function () {
            setSnapshot(getSnapshot());
        };
        var unsubscribe = subscribeChatConnection(handleSnapshotChange);
        ensureSharedConnection({
            sessionId: sessionId,
            currentContext: currentContext,
            setMessages: setMessages,
            onMessageEvent: onMessageEvent,
            onJobEvent: onJobEvent,
            onRuntimeEvent: onRuntimeEvent,
            onRuntimeDetailEvent: onRuntimeDetailEvent,
            onActivityEvent: onActivityEvent,
        });
        handleSnapshotChange();
        return function () {
            unsubscribe();
        };
    }, [sessionId, setMessages]);
    (0, react_1.useEffect)(function () {
        sharedChatConnection.currentContext = currentContext;
        sharedChatConnection.setMessages = setMessages;
        sharedChatConnection.onMessageEvent = onMessageEvent;
        sharedChatConnection.onJobEvent = onJobEvent;
        sharedChatConnection.onRuntimeEvent = onRuntimeEvent;
        sharedChatConnection.onRuntimeDetailEvent = onRuntimeDetailEvent;
        sharedChatConnection.onActivityEvent = onActivityEvent;
        sendContextUpdate(currentContext);
    }, [
        currentContext,
        onMessageEvent,
        onJobEvent,
        onRuntimeEvent,
        onRuntimeDetailEvent,
        onActivityEvent,
        setMessages,
    ]);
    (0, react_1.useEffect)(function () {
        sharedChatConnection.pingSubscriberCount += 1;
        startPresenceMonitoring();
        return function () {
            sharedChatConnection.pingSubscriberCount = Math.max(0, sharedChatConnection.pingSubscriberCount - 1);
            if (sharedChatConnection.pingSubscriberCount === 0) {
                stopPresenceMonitoring();
            }
        };
    }, []);
    return {
        connectionState: snapshot.connectionState,
        connectionErrorDetail: snapshot.connectionErrorDetail,
        socketRef: sharedChatConnection.socketRef,
    };
}
