
/* ── P&ID Graph Viewer Logic ────────────────────────────────────────── */
let pidCanvas, pidCtx;
let pidNodeMap = new Map(); // { nodeId: {x, y, label, ...} }
let pidTopology = null;
let pidImpactResult = null;
let currentGraphId = null; // 현재 로드된 도면의 graphId 저장
let pidZoom = 1.0;
let pidOffset = { x: 0, y: 0 };
let pidIsDragging = false;
let pidLastMouse = { x: 0, y: 0 };

function pidHandleError(error, context = '작업') {
    console.error(`[${context}] Error:`, error);
    const statusTxt = document.getElementById('pid-status-txt');
    const progWrap = document.getElementById('pid-progress-wrap');
    
    let msg = '알 수 없는 오류가 발생했습니다.';
    if (error && typeof error === 'object') {
        msg = error.message || error.error || error.details || (error instanceof Error ? error.message : msg);
    } else if (typeof error === 'string') {
        msg = error;
    }

    if (statusTxt) statusTxt.textContent = `오류 발생: ${msg}`;
    if (progWrap) progWrap.classList.add('hidden');
    setGlobal('err', `${context} 실패: ${msg}`);
}

async function pidInit() {
    pidCanvas = document.getElementById('pid-canvas');
    if (!pidCanvas) return;
    pidCtx = pidCanvas.getContext('2d');
    
    window.addEventListener('resize', pidResize);
    pidResize();

    // 마우스 이벤트 설정
    pidCanvas.addEventListener('mousedown', pidOnMouseDown);
    pidCanvas.addEventListener('mousemove', pidOnMouseMove);
    pidCanvas.addEventListener('mouseup', pidOnMouseUp);
    pidCanvas.addEventListener('wheel', pidOnWheel);
    pidCanvas.addEventListener('click', pidOnClick);

    // 페이지 재진입 시 진행 중인 작업 복구
    await pidRestoreBuildStatus();
}

function pidResize() {
    const wrap = pidCanvas.parentElement;
    pidCanvas.width = wrap.clientWidth;
    pidCanvas.height = wrap.clientHeight;
    pidRender();
}

async function pidRestoreBuildStatus() {
    const savedTaskId = localStorage.getItem('pid_build_task_id');
    const savedStartTime = localStorage.getItem('pid_build_start_time');
    
    if (!savedTaskId || !savedStartTime) return;

    const statusTxt = document.getElementById('pid-status-txt');
    const progWrap = document.getElementById('pid-progress-wrap');
    const progBar = document.getElementById('pid-progress-bar');
    
    try {
        const statusRes = await api('GET', `/api/pidgraph/status/${savedTaskId}`);
        
        if (statusRes.status === 'Processing' || statusRes.status === 'Starting') {
            progWrap.classList.remove('hidden');
            
            const startTime = parseInt(savedStartTime);
            const updateTimer = () => {
                const elapsed = Math.floor((Date.now() - startTime) / 1000);
                const mins = String(Math.floor(elapsed / 60)).padStart(2, '0');
                const secs = String(elapsed % 60).padStart(2, '0');
                const timeStr = `[${mins}:${secs}] `;
                
                const currentMsg = statusTxt.textContent;
                // 시간 표시 부분([00:00])을 제외한 실제 메시지만 추출
                const cleanMsg = currentMsg.startsWith('[') && currentMsg.includes(']')
                    ? currentMsg.substring(currentMsg.indexOf(']') + 1).trim()
                    : currentMsg;
                
                statusTxt.textContent = timeStr + cleanMsg;
            };

            const startSse = (taskId) => {
                const eventSource = new EventSource(`/api/pidgraph/status/stream/${taskId}`);
                
                eventSource.onmessage = (event) => {
                    const res = JSON.parse(event.data);
                    progBar.style.width = `${res.progress}%`;
                    statusTxt.textContent = res.message;
                    updateTimer();

                    if (res.status === 'Completed') {
                        statusTxt.textContent = '추출이 완료되었습니다.';
                        progWrap.classList.add('hidden');
                        localStorage.removeItem('pid_build_task_id');
                        localStorage.removeItem('pid_build_start_time');
                        eventSource.close();
                    } else if (res.status === 'Failed') {
                        statusTxt.textContent = `오류 발생: ${res.message}`;
                        progWrap.classList.add('hidden');
                        localStorage.removeItem('pid_build_task_id');
                        localStorage.removeItem('pid_build_start_time');
                        eventSource.close();
                    }
                };

                eventSource.onerror = (e) => {
                    console.error('SSE error during restore:', e);
                    eventSource.close();
                };

                return eventSource;
            };

            setInterval(updateTimer, 1000);
            startSse(savedTaskId);
        } else if (statusRes.status === 'Completed') {
            localStorage.removeItem('pid_build_task_id');
            localStorage.removeItem('pid_build_start_time');
        }
    } catch (e) {
        pidHandleError(e, '상태 복구');
        localStorage.removeItem('pid_build_task_id');
        localStorage.removeItem('pid_build_start_time');
    }
}

async function pidLoadDrawing() {
    setGlobal('busy', '도면 데이터 로드 중');
    try {
        // 1. 기하학적 데이터 로드 (Phase 1 결과물)
        const geoRes = await api('GET', '/api/pid/geometry'); // 가상 엔드포인트 (필요시 구현)
        // 실제로는 파일에서 직접 읽거나 API를 통해 가져옴.
        // 여기서는 예시로 shared_geo_data.json 형태의 데이터를 가정
        const geoData = await (await fetch('/futurePlan/End-to-End P&ID Graph Pipeline/shared_geo_data.json')).json();
        
        pidNodeMap.clear();
        geoData.nodes.forEach(n => {
            pidNodeMap.set(n.id, n);
        });

        // 2. 위상 데이터 로드 (Phase 2 결과물)
        const topoData = await (await fetch('/futurePlan/End-to-End P&ID Graph Pipeline/pid_graph_topology.json')).json();
        pidTopology = topoData;
        
        // topoData 내에 graphId가 있다면 저장, 없다면 파일명 기반으로 추정
        currentGraphId = topoData.graphId || "No-10_Plant_PID_graph.json";

        document.getElementById('pid-status-txt').textContent = `도면 로드 완료: ${pidNodeMap.size}개 노드`;
        pidRender();
        setGlobal('ok', '로드 완료');
    } catch (e) {
        pidHandleError(e, '도면 로드');
    }
}

async function pidBuildGraph(filepath) {
    const statusTxt = document.getElementById('pid-status-txt');
    const progWrap = document.getElementById('pid-progress-wrap');
    const progBar = document.getElementById('pid-progress-bar');
    
    let startTime = Date.now();
    let timerInterval = null;

    const updateTimer = () => {
        const elapsed = Math.floor((Date.now() - startTime) / 1000);
        const mins = String(Math.floor(elapsed / 60)).padStart(2, '0');
        const secs = String(elapsed % 60).padStart(2, '0');
        const timeStr = `[${mins}:${secs}] `;
        
        // 현재 메시지에서 시간 표시 부분([00:00])을 제외한 실제 메시지만 추출
        const currentMsg = statusTxt.textContent;
        const cleanMsg = currentMsg.startsWith('[') && currentMsg.includes(']')
            ? currentMsg.substring(currentMsg.indexOf(']') + 1).trim()
            : currentMsg;
        
        statusTxt.textContent = timeStr + cleanMsg;
    };

    try {
        progWrap.classList.remove('hidden');
        progBar.style.width = '0%';
        
        // 타이머 시작
        timerInterval = setInterval(updateTimer, 1000);
        updateTimer();

        // 1. 빌드 요청
        statusTxt.textContent = '추출 요청 중...';
        const res = await api('POST', '/api/pidgraph/build', { filepath });
        const taskId = res.taskId;

        // 상태 복구를 위해 localStorage에 저장
        localStorage.setItem('pid_build_task_id', taskId);
        localStorage.setItem('pid_build_start_time', Date.now().toString());

        // 2. SSE 연결 시작
        await new Promise((resolve, reject) => {
            const eventSource = new EventSource(`/api/pidgraph/status/stream/${taskId}`);
            
            eventSource.onmessage = (event) => {
                const statusRes = JSON.parse(event.data);
                progBar.style.width = `${statusRes.progress}%`;
                statusTxt.textContent = statusRes.message;
                updateTimer();

                if (statusRes.status === 'Completed') {
                    eventSource.close();
                    setGlobal('ok', '추출 완료');
                    resolve();
                } else if (statusRes.status === 'Failed') {
                    eventSource.close();
                    reject(new Error(statusRes.message));
                }
            };

            eventSource.onerror = (e) => {
                eventSource.close();
                reject(new Error('SSE connection lost'));
            };
        });

        statusTxt.textContent = '추출이 성공적으로 완료되었습니다.';
        progWrap.classList.add('hidden');
    } catch (e) {
        pidHandleError(e, '그래프 추출');
    } finally {
        if (timerInterval) clearInterval(timerInterval);
    }
}

function pidRender() {
    if (!pidCtx) return;
    pidCtx.clearRect(0, 0, pidCanvas.width, pidCanvas.height);
    pidCtx.save();
    pidCtx.translate(pidOffset.x, pidOffset.y);
    pidCtx.scale(pidZoom, pidZoom);

    // 1. 엣지(배관) 렌더링
    if (pidTopology && pidTopology.edges) {
        pidCtx.strokeStyle = '#555';
        pidCtx.lineWidth = 1 / pidZoom;
        pidCtx.beginPath();
        pidTopology.edges.forEach(edge => {
            const s = pidNodeMap.get(edge.source);
            const t = pidNodeMap.get(edge.target);
            if (s && t) {
                // 영향도 분석 결과에 포함된 경로라면 하이라이트
                if (pidImpactResult && pidImpactResult.paths?.some(p => p.includes(edge.source) && p.includes(edge.target))) {
                    pidCtx.strokeStyle = '#ff4444';
                    pidCtx.lineWidth = 3 / pidZoom;
                } else {
                    pidCtx.strokeStyle = '#555';
                    pidCtx.lineWidth = 1 / pidZoom;
                }
                pidCtx.moveTo(s.x, s.y);
                pidCtx.lineTo(t.x, t.y);
            }
        });
        pidCtx.stroke();
    }

    // 2. 노드(설비) 렌더링
    pidNodeMap.forEach((node, id) => {
        const isImpacted = pidImpactResult?.impactedNodes?.[id] !== undefined;
        const depth = pidImpactResult?.impactedNodes?.[id] || 0;

        pidCtx.fillStyle = isImpacted ? `rgba(255, ${Math.max(0, 255 - depth * 50)}, 0, 0.8)` : '#aaa';
        pidCtx.beginPath();
        pidCtx.arc(node.x, node.y, 4 / pidZoom, 0, Math.PI * 2);
        pidCtx.fill();

        if (pidZoom > 1.5) {
            pidCtx.fillStyle = '#fff';
            pidCtx.font = `${10 / pidZoom}px Arial`;
            pidCtx.fillText(node.label || id, node.x + 5 / pidZoom, node.y - 5 / pidZoom);
        }
    });

    pidCtx.restore();
}

// --- 인터랙션 이벤트 ---

function pidOnMouseDown(e) {
    pidIsDragging = true;
    pidLastMouse = { x: e.clientX, y: e.clientY };
}

function pidOnMouseMove(e) {
    if (pidIsDragging) {
        pidOffset.x += e.clientX - pidLastMouse.x;
        pidOffset.y += e.clientY - pidLastMouse.y;
        pidLastMouse = { x: e.clientX, y: e.clientY };
        pidRender();
    }

    // 툴팁 처리
    const rect = pidCanvas.getBoundingClientRect();
    const worldX = (e.clientX - rect.left - pidOffset.x) / pidZoom;
    const worldY = (e.clientY - rect.top - pidOffset.y) / pidZoom;
    
    let found = null;
    pidNodeMap.forEach((node, id) => {
        const dist = Math.hypot(node.x - worldX, node.y - worldY);
        if (dist < 10 / pidZoom) found = { id, ...node };
    });

    const tooltip = document.getElementById('pid-tooltip');
    if (found) {
        tooltip.classList.remove('hidden');
        tooltip.style.left = (e.clientX - rect.left + 10) + 'px';
        tooltip.style.top = (e.clientY - rect.top + 10) + 'px';
        tooltip.innerHTML = `<strong>${found.label || found.id}</strong><br>ID: ${found.id}`;
    } else {
        tooltip.classList.add('hidden');
    }
}

function pidOnMouseUp() {
    pidIsDragging = false;
}

function pidOnWheel(e) {
    e.preventDefault();
    const delta = e.deltaY > 0 ? 0.9 : 1.1;
    pidZoom *= delta;
    pidZoom = Math.min(Math.max(pidZoom, 0.1), 10);
    pidRender();
}

async function pidOnClick(e) {
    const rect = pidCanvas.getBoundingClientRect();
    const worldX = (e.clientX - rect.left - pidOffset.x) / pidZoom;
    const worldY = (e.clientY - rect.top - pidOffset.y) / pidZoom;
    
    let clickedNode = null;
    pidNodeMap.forEach((node, id) => {
        const dist = Math.hypot(node.x - worldX, node.y - worldY);
        if (dist < 10 / pidZoom) clickedNode = id;
    });

    if (clickedNode) {
        const node = pidNodeMap.get(clickedNode);
        document.getElementById('pid-node-info').innerHTML = `
            <strong>노드 ID:</strong> ${clickedNode}<br>
            <strong>라벨:</strong> ${node.label || '-'}<br>
            <strong>좌표:</strong> (${node.x}, ${node.y})
        `;
        await pidRequestImpactAnalysis(clickedNode);
    }
}

async function pidRequestImpactAnalysis(nodeId) {
    const statusTxt = document.getElementById('pid-status-txt');
    const progWrap = document.getElementById('pid-progress-wrap');
    const progBar = document.getElementById('pid-progress-bar');
    
    statusTxt.textContent = `분석 요청 중: ${nodeId}...`;
    progWrap.classList.remove('hidden');
    progBar.style.width = '0%';

    try {
        // 1. 분석 시작 요청 (현재는 graphId가 필요하므로, 로드된 topoData의 ID나 파일명을 사용해야 함)
        // 여기서는 단순화를 위해 현재 로드된 도면의 graphId를 가정하거나,
        // 실제로는 pidLoadDrawing 시점에 graphId를 저장해두어야 함.
        if (!currentGraphId) {
            alert('도면이 로드되지 않았습니다.');
            return;
        }
        const startRes = await api('GET', `/api/pidgraph/impact/${currentGraphId}/${nodeId}`);

        // 2. 결과 처리 (이제 API가 즉시 결과를 반환하므로 폴링 불필요)
        pidImpactResult = startRes;
        pidRender();
        pidRenderImpactList(startRes);
        statusTxt.textContent = '분석 완료';
        progWrap.classList.add('hidden');
    } catch (e) {
        pidHandleError(e, '영향도 분석');
    }
}

function pidRenderImpactList(result) {
    const list = document.getElementById('pid-impact-items');
    list.innerHTML = '';
    
    const sortedNodes = Object.entries(result.impactedNodes)
        .sort((a, b) => a[1] - b[1]);

    sortedNodes.forEach(([id, depth]) => {
        const li = document.createElement('li');
        li.innerHTML = `<span>${id}</span><span class="mut">Depth: ${depth}</span>`;
        li.onclick = () => {
            const node = pidNodeMap.get(id);
            if (node) {
                pidOffset.x = pidCanvas.width/2 - node.x * pidZoom;
                pidOffset.y = pidCanvas.height/2 - node.y * pidZoom;
                pidRender();
            }
        };
        list.appendChild(li);
    });
}

function pidClearAnalysis() {
    pidImpactResult = null;
    document.getElementById('pid-impact-items').innerHTML = '';
    document.getElementById('pid-node-info').textContent = '노드를 클릭하면 상세 정보가 표시됩니다.';
    pidRender();
}
