/**
 * Gravity Web — App 초기화 및 WebSocket 관리
 */

(function () {
    // ─── 컴포넌트 초기화 ──────────────────────────────────
    const sessionPanel = new SessionPanel();
    const chatPanel = new ChatPanel();
    const mirrorPanel = new MirrorPanel();

    let ws = null;
    let reconnectTimer = null;
    let currentView = 'chat'; // 'chat' | 'mirror'
    const WS_URL = `ws://${location.host}/ws`;

    // ─── DOM 레퍼런스 ─────────────────────────────────────
    const connectionStatus = document.getElementById('connectionStatus');
    const statusDot = connectionStatus.querySelector('.status-dot');
    const statusText = connectionStatus.querySelector('.status-text');

    const addSessionBtn = document.getElementById('addSessionBtn');
    const addSessionModal = document.getElementById('addSessionModal');
    const closeModal = document.getElementById('closeModal');
    const cancelModal = document.getElementById('cancelModal');
    const confirmAddSession = document.getElementById('confirmAddSession');
    const sessionNameInput = document.getElementById('sessionName');
    const sessionHostInput = document.getElementById('sessionHost');
    const sessionPortInput = document.getElementById('sessionPort');

    const screenshotBtn = document.getElementById('screenshotBtn');
    const reconnectBtn = document.getElementById('reconnectBtn');
    const screenshotOverlay = document.getElementById('screenshotOverlay');
    const screenshotImage = document.getElementById('screenshotImage');
    const closeScreenshot = document.getElementById('closeScreenshot');

    const tabChat = document.getElementById('tabChat');
    const tabMirror = document.getElementById('tabMirror');
    const chatMessagesWrap = document.getElementById('chatMessages');
    const chatInputArea = document.querySelector('.chat-input-area');
    const mirrorHint = document.getElementById('mirrorHint');

    // ─── WebSocket 연결 ───────────────────────────────────
    function connectWebSocket() {
        ws = new WebSocket(WS_URL);

        ws.onopen = () => {
            setConnectionStatus('connected', '서버 연결됨');
            if (reconnectTimer) {
                clearTimeout(reconnectTimer);
                reconnectTimer = null;
            }
            // Bridge 세션 자동 로드
            loadBridgeSessions();
        };

        ws.onclose = () => {
            setConnectionStatus('error', '연결 끊김');
            scheduleReconnect();
        };

        ws.onerror = () => {
            setConnectionStatus('error', '연결 오류');
        };

        ws.onmessage = (event) => {
            try {
                const msg = JSON.parse(event.data);
                handleMessage(msg);
            } catch (e) {
                console.error('WS 메시지 파싱 오류:', e);
            }
        };
    }

    function scheduleReconnect() {
        if (reconnectTimer) return;
        reconnectTimer = setTimeout(() => {
            reconnectTimer = null;
            connectWebSocket();
        }, 3000);
    }

    function sendWs(msg) {
        if (ws && ws.readyState === WebSocket.OPEN) {
            ws.send(JSON.stringify(msg));
        }
    }

    // ─── Bridge 세션 관리 ──────────────────────────────────
    let bridgeSessions = [];  // Bridge에서 가져온 대화 목록
    let activeBridgeSession = null;

    async function loadBridgeSessions() {
        try {
            // 세션 목록 + cascades를 병렬로 가져오기
            const [sessRes, cascRes] = await Promise.all([
                fetch('/api/bridge/sessions'),
                fetch('/api/bridge/cascades'),
            ]);

            const sessData = sessRes.ok ? await sessRes.json() : { sessions: [] };
            let cascadeMap = {};
            if (cascRes.ok) {
                const cascData = await cascRes.json();
                cascadeMap = cascData.cascades || cascData;
            }

            // 세션 → 맵으로 변환 (id 기준)
            const sessionIds = new Set();
            const sessionList = (sessData.sessions || []);

            function buildSession(id, sessionData, cascade) {
                const s = sessionData || {};
                const c = cascade || {};

                // 워크스페이스에서 프로젝트명 추출
                const repoName = c.workspaces?.[0]?.repository?.computedName || '';
                const wsUri = c.workspaces?.[0]?.workspaceFolderAbsoluteUri || '';
                const project = repoName
                    ? repoName.split('/').pop()
                    : (wsUri ? decodeURIComponent(wsUri.split('/').pop()) : '');

                // 대화 이름: cascade summary > task name > title
                const rawTitle = s.title || c.summary || '';
                const isGeneric = /^Conversation \d+$/.test(rawTitle);
                const summary = c.summary || '';
                const taskName = c.latestTaskBoundaryStep?.step?.taskBoundary?.taskName || '';
                const displayName = summary || taskName || (isGeneric ? '' : rawTitle) || id.substring(0, 8);

                const runStatus = c.status || '';
                const isRunning = runStatus.includes('RUNNING');
                return {
                    id: id,
                    name: displayName,
                    host: 'bridge',
                    cdpPort: 0,
                    status: isRunning ? 'running' : 'connected',
                    title: displayName,
                    stepCount: c.stepCount || s.stepCount || 0,
                    lastModified: c.lastModifiedTime || s.lastModifiedTime,
                    project: project,
                    isRunning: isRunning,
                    isBridge: true,
                };
            }

            // 1) Sessions API 기반 목록
            bridgeSessions = sessionList.map(s => {
                sessionIds.add(s.id);
                return buildSession(s.id, s, cascadeMap[s.id]);
            });

            // 2) Cascades에만 있는 대화 추가 (Sessions API 누락분)
            for (const [cascadeId, cascade] of Object.entries(cascadeMap)) {
                if (!sessionIds.has(cascadeId)) {
                    bridgeSessions.push(buildSession(cascadeId, null, cascade));
                }
            }

            // 최근 수정 순 정렬
            bridgeSessions.sort((a, b) => {
                const ta = a.lastModified ? new Date(a.lastModified).getTime() : 0;
                const tb = b.lastModified ? new Date(b.lastModified).getTime() : 0;
                return tb - ta;
            });

            sessionPanel.update(bridgeSessions);
            // 활성 세션 없으면 가장 최근 대화 자동 선택
            if (!sessionPanel.activeSessionId && bridgeSessions.length > 0) {
                selectBridgeSession(bridgeSessions[0].id);
            }
        } catch (e) {
            console.warn('[Bridge] 세션 로드 실패:', e);
        }
    }

    // 채팅 메시지 저장소 (lazy load용)
    let allMessages = [];
    const PAGE_SIZE = 30;

    async function selectBridgeSession(sessionId) {
        activeBridgeSession = sessionId;
        sessionPanel.setActive(sessionId);
        const session = bridgeSessions.find(s => s.id === sessionId);
        if (session) {
            chatPanel.showSession({ name: session.name, status: session.isRunning ? 'running' : 'connected' });
        }
        // lazy load 콜백 등록
        chatPanel.onLoadMore = () => {
            const shown = chatPanel.getShownCount();
            const remaining = allMessages.length - shown;
            if (remaining <= 0) return;
            const batch = allMessages.slice(Math.max(0, remaining - PAGE_SIZE), remaining);
            chatPanel.prependMessages(batch);
        };
        try {
            await loadSessionMessages(sessionId);
        } catch (e) {
            console.warn('[Bridge] 초기 로드 실패:', e);
            chatPanel.updateChat([{ type: 'status', text: '⚠️ 데이터 로드 실패. 잠시 후 자동 재시도...' }], false, true);
        }
    }

    async function loadSessionMessages(sessionId, scrollToBottom = true) {
        try {
            // 1. trajectory (히스토리, 최대 ~341 스텝)
            // 2. 서버 누적 메시지 (trajectory 이후 cascade diff로 캡쳐된 것들)
            const [trajRes, accRes] = await Promise.all([
                fetch(`/api/bridge/trajectory/${sessionId}`),
                fetch(`/api/bridge/messages/${sessionId}`),
            ]);

            allMessages = [];
            let trajLastStepIndex = -1;

            if (trajRes.ok) {
                const trajData = await trajRes.json();
                if (trajData.trajectory?.steps) {
                    const steps = trajData.trajectory.steps;
                    allMessages = parseTrajectoryToMessages(steps);
                    // 마지막 stepIndex 기억
                    for (let i = steps.length - 1; i >= 0; i--) {
                        if (steps[i].metadata?.createdAt) {
                            trajLastStepIndex = i;
                            break;
                        }
                    }
                }
            }

            // 3. 서버 누적 메시지 병합 (trajectory 이후 것만)
            let currentStatus = { isRunning: false, isBlocking: false };
            if (accRes.ok) {
                const accData = await accRes.json();
                currentStatus = { isRunning: accData.isRunning, isBlocking: accData.isBlocking };

                if (accData.messages?.length > 0) {
                    for (const msg of accData.messages) {
                        // trajectory에 이미 있는 메시지는 스킵
                        if (msg.stepIndex !== undefined && msg.stepIndex <= trajLastStepIndex) continue;

                        if (msg.type === 'task') {
                            allMessages.push({
                                type: 'task', title: msg.title || '',
                                summary: msg.summary || '', status: msg.status || '',
                                mode: msg.mode || '', tools: [],
                            });
                        } else if (msg.type === 'text') {
                            allMessages.push({
                                type: 'text', html: simpleMarkdown(msg.content || ''),
                            });
                        } else if (msg.type === 'user_input') {
                            // 사용자 입력 (텍스트 없음, 시간만)
                        }
                    }
                }
            }

            // 4. 상태 표시 (blocking / waiting / running)
            if (currentStatus.isBlocking || currentStatus.isWaiting) {
                allMessages.push({
                    type: 'actions',
                    label: '⚠️ 사용자 승인 대기 중',
                    buttons: [
                        { label: '✅ 진행', action: 'api_call', endpoint: '/api/bridge/approve', body: { type: 'accept' } },
                        { label: '❌ 거절', action: 'api_call', endpoint: '/api/bridge/approve', body: { type: 'reject' }, variant: 'danger' },
                    ],
                });
            } else if (currentStatus.isRunning) {
                allMessages.push({ type: 'status', text: '🔄 AI가 작업 중...' });
            }

            // 5. 화면 표시
            const initial = allMessages.slice(Math.max(0, allMessages.length - PAGE_SIZE));
            chatPanel.updateChat(initial, allMessages.length > PAGE_SIZE, scrollToBottom);
        } catch (e) {
            console.warn('[Bridge] 메시지 로드 실패:', e);
        }
    }

    function formatRelativeTime(isoStr) {
        if (!isoStr) return '';
        const diff = Date.now() - new Date(isoStr).getTime();
        const mins = Math.floor(diff / 60000);
        if (mins < 1) return '방금 전';
        if (mins < 60) return `${mins}분 전`;
        const hrs = Math.floor(mins / 60);
        if (hrs < 24) return `${hrs}시간 전`;
        return `${Math.floor(hrs / 24)}일 전`;
    }

    /**
     * 서버 WS push된 새 메시지를 화면에 즉시 반영
    let _prevStatusKey = ''; // 이전 상태 추적 (상태 변경 시만 DOM 갱신)

    function applyNewMessages(data) {
        if (!activeBridgeSession) return;
        // 버튼 클릭 처리 중이면 DOM 교체 방지
        if (chatPanel.actionInProgress) return;

        const hasNewMessages = data.messages && data.messages.length > 0;

        // 현재 상태 키 계산
        const statusKey = data.isBlocking ? 'blocking' : data.isWaiting ? 'waiting' : data.isRunning ? 'running' : 'idle';
        const statusChanged = statusKey !== _prevStatusKey;
        _prevStatusKey = statusKey;

        // 기존 상태 메시지(status/actions) 제거
        while (allMessages.length > 0) {
            const last = allMessages[allMessages.length - 1];
            if (last.type === 'status' || last.type === 'actions') {
                allMessages.pop();
            } else {
                break;
            }
        }

        // 새 메시지 추가
        if (hasNewMessages) {
            for (const msg of data.messages) {
                if (msg.type === 'task') {
                    const lastTask = allMessages.filter(m => m.type === 'task').pop();
                    if (lastTask && lastTask.title === msg.title) {
                        lastTask.summary = msg.summary || lastTask.summary;
                        lastTask.status = msg.status || lastTask.status;
                    } else {
                        allMessages.push({
                            type: 'task', title: msg.title || '',
                            summary: msg.summary || '', status: msg.status || '',
                            mode: msg.mode || '', tools: [],
                        });
                    }
                } else if (msg.type === 'text') {
                    allMessages.push({
                        type: 'text', html: simpleMarkdown(msg.content || ''),
                    });
                }
            }
        }

        // 상태 표시
        if (data.isBlocking || data.isWaiting) {
            allMessages.push({
                type: 'actions',
                label: '⚠️ 사용자 승인 대기 중',
                buttons: [
                    { label: '✅ 진행', action: 'api_call', endpoint: '/api/bridge/approve', body: { type: 'accept' } },
                    { label: '❌ 거절', action: 'api_call', endpoint: '/api/bridge/approve', body: { type: 'reject' }, variant: 'danger' },
                ],
            });
        } else if (data.isRunning) {
            allMessages.push({ type: 'status', text: '🔄 AI가 작업 중...' });
        }

        // 화면 갱신: 새 메시지 또는 상태 변경 시만 (동일 상태 반복은 건너뛰어 스크롤 점프 방지)
        if (hasNewMessages || statusChanged) {
            const initial = allMessages.slice(Math.max(0, allMessages.length - PAGE_SIZE));
            chatPanel.updateChat(initial, allMessages.length > PAGE_SIZE, false);
        }
    }

    /**
     * Trajectory 스텝을 Antigravity 스타일 대화 흐름으로 변환
     *
     * 규칙:
     * - USER_INPUT → 사용자 턴 구분자 (새 대화 블록 시작)
     * - NOTIFY_USER → AI의 최종 응답 (마크다운)
     * - TASK_BOUNDARY → 작업 카드 (이후 도구 스텝들을 내부 요약으로 포함)
     * - RUN_COMMAND, CODE_ACTION → task 카드 내부 도구 호출 요약
     * - 기타 (PLANNER_RESPONSE, EPHEMERAL, VIEW_FILE 등) → 건너뛰기
     */
    function parseTrajectoryToMessages(steps) {
        const msgs = [];
        let currentTask = null; // 현재 활성 task 카드
        let toolActions = [];   // 현재 task 내 도구 호출들

        function flushTask() {
            if (currentTask) {
                currentTask.tools = [...toolActions];
                msgs.push(currentTask);
                currentTask = null;
                toolActions = [];
            }
        }

        for (const step of steps) {
            switch (step.type) {
                case 'CORTEX_STEP_TYPE_USER_INPUT': {
                    // 이전 task 플러시
                    flushTask();
                    let text = '';
                    const ur = step.userInput?.userResponse || '';
                    const itemText = step.userInput?.items?.[0]?.item?.text
                        || step.userInput?.items?.[0]?.item?.recipe?.title
                        || '';
                    // 시스템 자동승인 메시지 필터
                    if (ur && !ur.includes('system-generated message')) {
                        text = ur;
                    } else if (itemText) {
                        text = itemText;
                    }
                    // 시스템 자동승인만 있는 경우 표시 스킵
                    if (!text || text.includes('system-generated message')) break;
                    msgs.push({
                        type: 'user',
                        text: text,
                        time: step.metadata?.createdAt || '',
                    });
                    break;
                }
                case 'CORTEX_STEP_TYPE_NOTIFY_USER': {
                    // 이전 task 플러시
                    flushTask();
                    const content = step.notifyUser?.notificationContent || '';
                    if (content) {
                        msgs.push({
                            type: 'text',
                            html: simpleMarkdown(content),
                        });
                    }
                    break;
                }
                case 'CORTEX_STEP_TYPE_TASK_BOUNDARY': {
                    const tb = step.taskBoundary || {};
                    if (!tb.taskName) break;
                    // 같은 이름의 task는 업데이트 (중복 카드 방지)
                    if (currentTask && currentTask.title === tb.taskName) {
                        currentTask.summary = tb.taskSummary || currentTask.summary;
                        currentTask.status = tb.taskStatus || currentTask.status;
                        currentTask.mode = tb.mode || currentTask.mode;
                    } else {
                        flushTask();
                        currentTask = {
                            type: 'task',
                            title: tb.taskName,
                            summary: tb.taskSummary || '',
                            status: tb.taskStatus || '',
                            mode: tb.mode || '',
                            tools: [],
                        };
                    }
                    break;
                }
                case 'CORTEX_STEP_TYPE_RUN_COMMAND': {
                    const cmd = step.runCommand?.commandLine || step.runCommand?.command || '';
                    if (cmd) {
                        toolActions.push({
                            icon: '⚡',
                            label: cmd.length > 60 ? cmd.substring(0, 57) + '...' : cmd,
                            done: step.status === 'CORTEX_STEP_STATUS_DONE',
                        });
                    }
                    break;
                }
                case 'CORTEX_STEP_TYPE_CODE_ACTION': {
                    const ca = step.codeAction || {};
                    const file = ca.filePath || ca.targetFile || '';
                    const desc = ca.description || '코드 수정';
                    toolActions.push({
                        icon: '📝',
                        label: file ? file.split(/[\\/]/).pop() + ': ' + desc : desc,
                        done: step.status === 'CORTEX_STEP_STATUS_DONE',
                    });
                    break;
                }
                case 'CORTEX_STEP_TYPE_VIEW_FILE':
                case 'CORTEX_STEP_TYPE_LIST_DIRECTORY': {
                    // 카운트만 — 개별 표시 안 함
                    break;
                }
                // PLANNER_RESPONSE, EPHEMERAL, CHECKPOINT, CONVERSATION_HISTORY, KNOWLEDGE_ARTIFACTS → 건너뛰기
            }
        }
        flushTask();
        return msgs;
    }

    function simpleMarkdown(text) {
        // 코드 블록을 임시 플레이스홀더로 보존
        const codeBlocks = [];
        let processed = text
            .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
            .replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => {
                const idx = codeBlocks.length;
                codeBlocks.push(`<pre class="md-code"><code>${code}</code></pre>`);
                return `___CODE_BLOCK_${idx}___`;
            });
        // 코드 블록 외부만 줄바꿈 변환
        processed = processed
            .replace(/\n/g, '<br>')
            .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
            .replace(/`([^`]+)`/g, '<code>$1</code>')
            .replace(/^## (.*)/gm, '<h3>$1</h3>')
            .replace(/^### (.*)/gm, '<h4>$1</h4>')
            .replace(/^\- (.*)/gm, '<li>$1</li>')
            .replace(/^\d+\. (.*)/gm, '<li>$1</li>');
        // 코드 블록 복원
        for (let i = 0; i < codeBlocks.length; i++) {
            processed = processed.replace(`___CODE_BLOCK_${i}___`, codeBlocks[i]);
        }
        return processed;
    }

    // ─── 서버 메시지 핸들러 ───────────────────────────────
    function handleMessage(msg) {
        switch (msg.type) {
            case 'sessions_list':
                // CDP 세션 (레거시) — Bridge 세션과 병합하지 않음
                break;

            case 'session_switched':
                sessionPanel.setActive(msg.sessionId);
                chatPanel.showSession(msg.session);
                break;

            case 'chat_update':
                if (msg.sessionId === sessionPanel.activeSessionId) {
                    chatPanel.updateChat(msg.messages);
                }
                break;

            case 'message_sent':
                if (!msg.success) {
                    showToast(`전송 실패: ${msg.error}`, 'error');
                }
                break;

            case 'bridge_event':
                // Bridge WS 이벤트 → 세션 목록만 갱신 (메시지는 new_messages로 처리)
                if (msg.type === 'step_changed') {
                    if (!this._lastSessionLoad || Date.now() - this._lastSessionLoad > 10000) {
                        this._lastSessionLoad = Date.now();
                        loadBridgeSessions();
                    }
                } else if (msg.type === 'session_changed' || msg.type === 'new_conversation') {
                    loadBridgeSessions();
                }
                break;

            case 'new_messages':
                // 서버가 push한 누적 메시지 → 즉시 화면 반영
                if (msg.sessionId === activeBridgeSession) {
                    applyNewMessages(msg);
                }
                break;

            case 'screenshot':
                screenshotImage.src = `data:image/jpeg;base64,${msg.data}`;
                screenshotOverlay.style.display = 'flex';
                break;

            case 'screencast_frame':
                if (msg.sessionId === sessionPanel.activeSessionId) {
                    mirrorPanel.updateFrame(msg.data, msg.metadata);
                    if (mirrorHint) mirrorHint.style.display = 'none';
                }
                break;

            case 'error':
                showToast(msg.message, 'error');
                break;
        }
    }



    // ─── 세션 패널 이벤트 ─────────────────────────────────
    sessionPanel.onSessionSelect = (sessionId) => {
        // Bridge 세션인지 확인
        const isBridge = bridgeSessions.some(s => s.id === sessionId);
        if (isBridge) {
            selectBridgeSession(sessionId);
        } else {
            sendWs({ type: 'switch_session', sessionId });
        }
    };

    sessionPanel.onSessionRemove = async (sessionId) => {
        try {
            const res = await fetch(`/api/sessions/${sessionId}`, { method: 'DELETE' });
            if (res.ok) {
                if (sessionPanel.activeSessionId === sessionId) {
                    chatPanel.showEmpty();
                    sessionPanel.setActive(null);
                }
                showToast('세션이 제거되었습니다', 'success');
            }
        } catch (e) {
            showToast('세션 제거 실패', 'error');
        }
    };

    // ─── 채팅 패널 이벤트 ─────────────────────────────────
    chatPanel.onSendMessage = async (text) => {
        if (!sessionPanel.activeSessionId) return;
        // Bridge 세션이면 Bridge API로 전송
        const isBridge = bridgeSessions.some(s => s.id === sessionPanel.activeSessionId);
        if (isBridge) {
            try {
                const res = await fetch('/api/bridge/send', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ message: text, sessionId: sessionPanel.activeSessionId }),
                });
                if (!res.ok) showToast('전송 실패', 'error');
            } catch { showToast('전송 실패', 'error'); }
            return;
        }
        sendWs({
            type: 'send_message',
            sessionId: sessionPanel.activeSessionId,
            text,
        });
    };

    chatPanel.onActionClick = async (button) => {
        // Bridge 세션이면 REST API로 호출
        const isBridge = bridgeSessions.some(s => s.id === sessionPanel.activeSessionId);
        if (isBridge && button.action === 'api_call' && button.endpoint) {
            try {
                const res = await fetch(button.endpoint, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(button.body || {}),
                });
                if (res.ok) {
                    showToast(`"${button.label}" 완료`, 'success');
                } else {
                    showToast(`"${button.label}" 실패`, 'error');
                }
            } catch {
                showToast(`"${button.label}" 오류`, 'error');
            }
            return;
        }
        // CDP 세션: WS로 좌표 클릭
        sendWs({
            type: 'click_action',
            sessionId: sessionPanel.activeSessionId,
            label: button.label,
            x: button.x,
            y: button.y,
        });
        showToast(`"${button.label}" 클릭`, 'success');
    };

    // ─── 미러 패널 이벤트 ─────────────────────────────
    mirrorPanel.onStartScreencast = (sessionId) => {
        sendWs({ type: 'start_screencast', sessionId });
    };

    mirrorPanel.onStopScreencast = (sessionId) => {
        sendWs({ type: 'stop_screencast', sessionId });
    };

    mirrorPanel.onInputEvent = (sessionId, event) => {
        sendWs({ type: 'input_event', sessionId, event });
    };

    // ─── 탭 전환 (Chat ↔ Mirror) ────────────────────────
    function switchView(view) {
        currentView = view;

        // 탭 UI
        tabChat.classList.toggle('active', view === 'chat');
        tabMirror.classList.toggle('active', view === 'mirror');

        // 채팅 영역 표시/숨기기
        chatMessagesWrap.style.display = view === 'chat' ? '' : 'none';
        if (chatInputArea) chatInputArea.style.display = view === 'chat' ? '' : 'none';

        if (view === 'mirror') {
            if (mirrorHint) mirrorHint.style.display = '';
            mirrorPanel.start(sessionPanel.activeSessionId);
        } else {
            mirrorPanel.stop();
        }
    }

    tabChat?.addEventListener('click', () => switchView('chat'));
    tabMirror?.addEventListener('click', () => switchView('mirror'));

    // ─── 모달 ─────────────────────────────────────────────
    function showModal() {
        addSessionModal.style.display = 'flex';
        sessionNameInput.value = '';
        sessionHostInput.value = 'localhost';
        sessionPortInput.value = '9000';
        setTimeout(() => sessionNameInput.focus(), 100);
    }

    function hideModal() {
        addSessionModal.style.display = 'none';
    }

    async function addSession() {
        const name = sessionNameInput.value.trim();
        const host = sessionHostInput.value.trim();
        const port = parseInt(sessionPortInput.value, 10);

        if (!name) {
            sessionNameInput.focus();
            return;
        }

        hideModal();

        try {
            const res = await fetch('/api/sessions', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ name, host, cdpPort: port }),
            });

            const session = await res.json();

            if (session.status === 'connected') {
                showToast(`${name} 연결 성공`, 'success');
            } else {
                showToast(`${name} 연결 실패: ${session.error || '알 수 없는 오류'}`, 'error');
            }
        } catch (e) {
            showToast('세션 추가 실패', 'error');
        }
    }

    addSessionBtn.addEventListener('click', showModal);
    closeModal.addEventListener('click', hideModal);
    cancelModal.addEventListener('click', hideModal);
    confirmAddSession.addEventListener('click', addSession);

    // Enter로 모달 확인
    addSessionModal.addEventListener('keydown', (e) => {
        if (e.key === 'Enter') addSession();
        if (e.key === 'Escape') hideModal();
    });

    // 배경 클릭으로 모달 닫기
    addSessionModal.addEventListener('click', (e) => {
        if (e.target === addSessionModal) hideModal();
    });

    // ─── 스크린샷 ─────────────────────────────────────────
    // ─── 헤더 승인/거절 버튼 ──────────────────────────────────
    const btnApprove = document.getElementById('btnApprove');
    const btnReject = document.getElementById('btnReject');

    async function handleApproval(type, btn) {
        btn.disabled = true;
        const origText = btn.textContent;
        btn.textContent = '처리 중...';
        try {
            const res = await fetch('/api/bridge/approve', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ type }),
            });
            const data = await res.json();
            if (res.ok) {
                btn.textContent = '✓ 완료';
                showToast(`${type === 'accept' ? '승인' : '거절'} 성공`, 'success');
            } else {
                btn.textContent = `실패`;
                showToast(data.error || '요청 실패', 'error');
            }
        } catch (e) {
            btn.textContent = '오류';
            showToast(e.message, 'error');
        }
        setTimeout(() => {
            btn.textContent = origText;
            btn.disabled = false;
        }, 2000);
    }

    btnApprove.addEventListener('click', () => handleApproval('accept', btnApprove));
    btnReject.addEventListener('click', () => handleApproval('reject', btnReject));

    screenshotBtn.addEventListener('click', () => {
        sendWs({ type: 'get_screenshot', sessionId: sessionPanel.activeSessionId });
    });

    closeScreenshot.addEventListener('click', () => {
        screenshotOverlay.style.display = 'none';
    });

    screenshotOverlay.addEventListener('click', (e) => {
        if (e.target === screenshotOverlay) {
            screenshotOverlay.style.display = 'none';
        }
    });

    // ─── 재연결 ───────────────────────────────────────────
    reconnectBtn.addEventListener('click', async () => {
        const id = sessionPanel.activeSessionId;
        if (!id) return;

        try {
            const res = await fetch(`/api/sessions/${id}/reconnect`, { method: 'POST' });
            const result = await res.json();
            if (result.success) {
                showToast('재연결 성공', 'success');
            } else {
                showToast(`재연결 실패: ${result.error}`, 'error');
            }
        } catch {
            showToast('재연결 실패', 'error');
        }
    });

    // ─── 유틸리티 ─────────────────────────────────────────
    function setConnectionStatus(status, text) {
        statusDot.className = `status-dot ${status}`;
        statusText.textContent = text;
    }

    function showToast(message, type = '') {
        const toast = document.createElement('div');
        toast.className = `toast ${type}`;
        toast.textContent = message;
        document.body.appendChild(toast);
        setTimeout(() => toast.remove(), 3000);
    }

    // ─── 시작 ─────────────────────────────────────────────
    connectWebSocket();
})();
