{% extends "base.html" %}
{% from "components/node_macro.html" import render_node %}

{% block content %}
<div class="noir-layout" x-data="{ 
    aiOpen: false, 
    zoom: 1, 
    panX: 0, 
    panY: 0,
    isPanning: false,
    startX: 0,
    startY: 0,
    spaceDown: false
}" @keydown.window="if($event.code === 'Space') spaceDown = true"
    @keyup.window="if($event.code === 'Space') spaceDown = false">

    <!-- 顶部统一操作栏 -->
    <div class="unified-toolbar">
        <button class="btn-queue" onclick="queueWorkflow()">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
                <path d="M8 5v14l11-7z" />
            </svg>
            Queue Prompt (启动任务)
        </button>
        <div class="divider" style="width: 1px; background: var(--border-color); margin: 0 4px;"></div>
        <button class="tool-btn" title="清理画布" onclick="clearCanvas()">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2" />
            </svg>
        </button>
    </div>

    <!-- 悬浮品牌 -->
    <div class="floating-brand">
        <h1 class="brand-logo">NoirFlow</h1>
        <p class="brand-tagline">暗流 · 节点生成系统</p>
    </div>

    <!-- 右键菜单 -->
    <div id="context-menu" class="context-menu" @click.away="closeContext()" style="display: none;">
        <div class="section-title" style="padding: 4px 12px; margin-bottom: 4px; font-size: 10px; opacity: 0.5;">新建节点
        </div>
        {% for config in node_configs %}
        <div class="context-menu-item" @click="createNodeAtMouse('{{ config.id }}')">
            <span class="icon">▣</span>
            <span class="label">{{ config.name }}</span>
        </div>
        {% endfor %}
        <div style="height: 1px; background: var(--border-color); margin: 4px 8px;"></div>
        <div class="context-menu-item" @click="clearCanvas(); closeContext();">
            <span class="icon">🗑</span>
            <span class="label">清空画布</span>
        </div>
    </div>

    <!-- AI 助手悬浮按钮 -->
    <div class="ai-toggle-btn" @click="aiOpen = !aiOpen">
        <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" />
        </svg>
    </div>

    <div class="ai-modal" x-show="aiOpen" x-transition x-cloak>
        <div class="sidebar-header"
            style="padding: 16px; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center;">
            <h3 class="section-title" style="margin: 0;">AI 助手</h3>
            <button @click="aiOpen = false"
                style="background:none; border:none; color:var(--text-secondary); cursor:pointer; font-size: 20px;">×</button>
        </div>
        <div class="sidebar-section chat-section"
            style="padding: 16px; flex: 1; display: flex; flex-direction: column;">
            <div id="chat-messages" class="chat-box" style="flex: 1; overflow-y: auto;">
                <div class="msg system">你好！我是 NoirFlow 助手。您可以问我如何构建工作流。</div>
            </div>
            <div class="chat-input-wrapper" style="margin-top: 12px;">
                <form hx-post="/api/chat" hx-target="#chat-messages" hx-swap="beforeend">
                    <input type="text" name="message" placeholder="与 AI 对话..." class="chat-input">
                    <button type="submit" class="chat-send">
                        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor"
                            stroke-width="2">
                            <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" />
                        </svg>
                    </button>
                </form>
            </div>
        </div>
    </div>

    <!-- 主画布 -->
    <main class="canvas-area" id="canvas" @contextmenu.prevent="showContext($event)" @wheel.passive="handleZoom($event)"
        @mousedown="startPan($event)" @mousemove="doPan($event)" @mouseup="stopPan()" @mouseleave="stopPan()">

        <div id="canvas-transform" :style="`transform: translate(${panX}px, ${panY}px) scale(${zoom})`"
            style="transform-origin: 0 0; width: 100%; height: 100%;">
            <div class="canvas-grid"></div>

            <!-- 连线层 -->
            <svg id="connection-svg" class="connection-layer">
                <defs>
                    <marker id="arrowhead-image" markerWidth="5" markerHeight="5" refX="4" refY="2.5" orient="auto">
                        <polygon points="0 0, 5 2.5, 0 5" fill="#4ade80" />
                    </marker>
                    <marker id="arrowhead-text" markerWidth="5" markerHeight="5" refX="4" refY="2.5" orient="auto">
                        <polygon points="0 0, 5 2.5, 0 5" fill="#38bdf8" />
                    </marker>
                    <marker id="arrowhead-file" markerWidth="5" markerHeight="5" refX="4" refY="2.5" orient="auto">
                        <polygon points="0 0, 5 2.5, 0 5" fill="#facc15" />
                    </marker>
                    <marker id="arrowhead-default" markerWidth="5" markerHeight="5" refX="4" refY="2.5" orient="auto">
                        <polygon points="0 0, 5 2.5, 0 5" fill="#ffffff" />
                    </marker>
                </defs>
            </svg>

            <div id="node-container">
                <!-- 初始预览节点 -->
            </div>
        </div>

        <!-- 顶级拖拽预览层 (不受缩放和平移影响，使用屏幕坐标) -->
        <svg id="drag-layer"
            style="position:fixed; top:0; left:0; width:100vw; height:100vh; z-index:9999; pointer-events:none; fill:none;"></svg>
    </main>
</div>

<script>
    // NoirConnect 工业级连线引擎：全局状态
    window.connections = [];
    window.selectedNodeId = null;
    let tempPathEl = null;
    window.currentZoom = 1;

    // --- 核心坐标投影工具 (工业级方案) ---
    function projectToCanvas(screenX, screenY) {
        const svg = document.getElementById('connection-svg');
        if (!svg) return { x: screenX, y: screenY };

        try {
            const pt = svg.createSVGPoint();
            pt.x = screenX;
            pt.y = screenY;
            const ctm = svg.getScreenCTM();
            if (ctm) {
                const localPt = pt.matrixTransform(ctm.inverse());
                return { x: localPt.x, y: localPt.y };
            }
        } catch (e) {
            console.error("CTM Error:", e);
        }

        // 极致降级方案：手动算 (仅在 CTM 失效时)
        const rect = svg.getBoundingClientRect();
        const alpine = getAlpineData();
        const zoom = alpine.zoom || 1;
        return {
            x: (screenX - rect.left) / zoom,
            y: (screenY - rect.top) / zoom
        };
    }

    function getAlpineData() {
        const el = document.querySelector('.noir-layout');
        if (!el) return {};
        if (window.Alpine) return Alpine.$data(el);
        return el.__x ? el.__x.$data : {};
    }

    // --- 画布操作逻辑 (Zoom & Pan) ---
    let lastMouseX = 0, lastMouseY = 0;
    let spaceDown = false;
    window.addEventListener('keydown', (e) => { if (e.code === 'Space') spaceDown = true; });
    window.addEventListener('keyup', (e) => { if (e.code === 'Space') spaceDown = false; });

    function handleZoom(e) {
        const alpine = getAlpineData();
        const delta = e.deltaY > 0 ? 0.9 : 1.1;
        const currentZoom = alpine.zoom || 1;
        const newZoom = Math.min(Math.max(0.1, currentZoom * delta), 3);
        alpine.zoom = newZoom;
        window.currentZoom = newZoom;
    }

    function startPan(e) {
        const alpine = getAlpineData();
        if (e.button === 1 || (e.button === 0 && (alpine.spaceDown || spaceDown))) {
            alpine.isPanning = true;
            alpine.startX = e.clientX - (alpine.panX || 0);
            alpine.startY = e.clientY - (alpine.panY || 0);
            document.body.style.cursor = 'grabbing';
            e.preventDefault();
        }
    }

    function doPan(e) {
        const alpine = getAlpineData();
        if (alpine.isPanning) {
            alpine.panX = e.clientX - (alpine.startX || 0);
            alpine.panY = e.clientY - (alpine.startY || 0);
        }
    }

    function stopPan() {
        const alpine = getAlpineData();
        if (alpine) alpine.isPanning = false;
        document.body.style.cursor = 'default';
    }

    // --- 右键菜单逻辑 ---
    let menuX = 0, menuY = 0;
    function showContext(e) {
        const menu = document.getElementById('context-menu');
        menu.style.display = 'block';
        menu.style.left = e.clientX + 'px';
        menu.style.top = e.clientY + 'px';

        const alpine = getAlpineData();
        const canvas = document.getElementById('canvas');
        const rect = canvas.getBoundingClientRect();

        menuX = (e.clientX - rect.left - (alpine.panX || 0)) / (alpine.zoom || 1);
        menuY = (e.clientY - rect.top - (alpine.panY || 0)) / (alpine.zoom || 1);
    }

    function closeContext() {
        const menu = document.getElementById('context-menu');
        if (menu) menu.style.display = 'none';
    }

    function createNodeAtMouse(typeId) {
        const params = new URLSearchParams({ type: typeId, x: Math.round(menuX), y: Math.round(menuY) });
        fetch(`/api/create_node?${params.toString()}`, { method: 'POST' })
            .then(res => res.text())
            .then(html => {
                const wrapper = document.createElement('div');
                wrapper.innerHTML = html.trim();
                const newNode = wrapper.firstElementChild;
                document.getElementById('node-container').appendChild(newNode);
                htmx.process(newNode);
                closeContext();
            });
    }

    // --- 统一执行逻辑 ---
    window.queueWorkflow = function () {
        console.log("Queueing workflow...");
        // 找到所有非 input 类型的节点并触发执行 (简单版本)
        const nodes = document.querySelectorAll('.node-box');
        nodes.forEach(node => {
            const btn = node.querySelector('.btn-run');
            // 如果节点需要执行逻辑（如生成器），模拟点击
            // 注意：我们之前移除了节点上的按钮，这里需要通过 HTMX 手动触发
            const nodeId = node.id;
            const nodeType = node.getAttribute('data-node-type');
            if (nodeType === 'nano_banana' || nodeType === 'image_preview' || nodeType === 'sys_dict_api') {
                htmx.ajax('POST', `/api/run_node/${nodeId}`, {
                    target: `#content-${nodeId}`,
                    values: collectNodeInputs(nodeId)
                });
            }
        });
    };

    function collectNodeInputs(nodeId) {
        const node = document.getElementById(nodeId);
        const inputs = {};
        node.querySelectorAll('.node-input-field').forEach(field => {
            inputs[field.name] = field.value;
        });
        return inputs;
    }

    // --- 连线引擎 ---
    window.updateLines = () => {
        window.connections.forEach(conn => {
            updatePathPosition(conn, conn.startSlotId, conn.endSlotId);
        });
    };

    function updatePathPosition(conn, startId, endId) {
        const sEl = document.getElementById(startId);
        const eEl = document.getElementById(endId);
        if (!sEl || !eEl) return;

        const sDot = sEl.querySelector('.slot-dot') || sEl;
        const eDot = eEl.querySelector('.slot-dot') || eEl;

        const sRect = sDot.getBoundingClientRect();
        const eRect = eDot.getBoundingClientRect();

        const p1 = projectToCanvas(sRect.left + sRect.width / 2, sRect.top + sRect.height / 2);
        const p2 = projectToCanvas(eRect.left + eRect.width / 2, eRect.top + eRect.height / 2);
        if (isNaN(p1.x) || isNaN(p1.y) || isNaN(p2.x) || isNaN(p2.y)) return;

        const dx = Math.abs(p1.x - p2.x) * 0.5;
        const d = `M ${p1.x} ${p1.y} C ${p1.x + dx} ${p1.y}, ${p2.x - dx} ${p2.y}, ${p2.x} ${p2.y}`;

        conn.pathEl.setAttribute('d', d);
        if (conn.hitEl) conn.hitEl.setAttribute('d', d);
    }

    function createConnection(sId, eId) {
        const sEl = document.getElementById(sId);
        const eEl = document.getElementById(eId);
        // 智能类型兼容性检查
        const t1 = sEl.dataset.dataType || 'any';
        const t2 = eEl.dataset.dataType || 'any';
        const isCompatible = (t1 === t2) ||
            ((t1 === 'text' || t1 === 'string') && (t2 === 'text' || t2 === 'string'));

        if (!isCompatible) {
            console.warn(`[NoirFlow] 类型不匹配: ${t1} vs ${t2}`);
            return;
        }

        const outId = sEl.dataset.slotType === 'output' ? sId : eId;
        const inId = sEl.dataset.slotType === 'output' ? eId : sId;

        if (window.connections.find(c => c.startSlotId === outId && c.endSlotId === inId)) return;

        const activeType = sEl.dataset.slotType === 'output' ? t1 : t2;
        const colors = { 'image': '#4ade80', 'text': '#38bdf8', 'string': '#38bdf8', 'dict': '#a855f7', 'file': '#facc15' };
        const activeColor = colors[activeType] || '#ffffff';

        const hit = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        hit.setAttribute('class', 'connection-hit-area');
        hit.style.cssText = 'fill:none; stroke:transparent; stroke-width:15px; cursor:pointer; pointer-events:stroke;';

        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path.setAttribute('class', 'connection-path');
        const markerMap = { 'image': 'arrowhead-image', 'text': 'arrowhead-text', 'string': 'arrowhead-text', 'file': 'arrowhead-file' };
        path.setAttribute('marker-end', `url(#${markerMap[activeType] || 'arrowhead-default'})`);
        path.style.stroke = activeColor;
        path.style.filter = `drop-shadow(0 0 5px ${activeColor}66)`;

        const conn = { startSlotId: outId, endSlotId: inId, pathEl: path, hitEl: hit };

        hit.onclick = (e) => {
            e.stopPropagation();
            path.remove(); hit.remove();
            window.connections = window.connections.filter(c => c !== conn);
        };

        const svg = document.getElementById('connection-svg');
        svg.appendChild(hit);
        svg.appendChild(path);
        window.connections.push(conn);
        updatePathPosition(conn, outId, inId);
    }

    window.currentZoom = 1;

    function startConnectionDrag(e, el) {
        if (e.button !== 0) return;
        e.preventDefault(); e.stopPropagation();

        const startSlot = el;
        const dragLayer = document.getElementById('drag-layer');
        if (!dragLayer) return;

        // 获取屏幕中心点
        const sDot = startSlot.querySelector('.slot-dot') || startSlot;
        const sRect = sDot.getBoundingClientRect();
        const p1 = { x: sRect.left + sRect.width / 2, y: sRect.top + sRect.height / 2 };

        sDot.classList.add('active');
        let currentTarget = null;

        const tempPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        tempPath.style.fill = 'none';
        tempPath.style.stroke = '#00f7ff';
        tempPath.style.strokeWidth = '4px';
        tempPath.style.strokeDasharray = '8,4';
        tempPath.style.pointerEvents = 'none';
        dragLayer.appendChild(tempPath);

        const findNearestSlot = (x, y) => {
            let best = null;
            let minDist = 40;
            document.querySelectorAll('.slot').forEach(slot => {
                if (slot === startSlot || slot.dataset.slotType === startSlot.dataset.slotType) return;
                const dot = slot.querySelector('.slot-dot') || slot;
                const dRect = dot.getBoundingClientRect();
                const dist = Math.hypot(x - (dRect.left + dRect.width / 2), y - (dRect.top + dRect.height / 2));
                if (dist < minDist) { minDist = dist; best = slot; }
            });
            return best;
        };

        const onMouseMove = (moveEvent) => {
            const p2 = { x: moveEvent.clientX, y: moveEvent.clientY };
            const dx = Math.abs(p1.x - p2.x) * 0.5;
            tempPath.setAttribute('d', `M ${p1.x} ${p1.y} C ${p1.x + dx} ${p1.y}, ${p2.x - dx} ${p2.y}, ${p2.x} ${p2.y}`);

            const newTarget = findNearestSlot(moveEvent.clientX, moveEvent.clientY);

            if (newTarget !== currentTarget) {
                if (currentTarget) currentTarget.querySelector('.slot-dot')?.classList.remove('hover');
                currentTarget = newTarget;
                if (currentTarget) currentTarget.querySelector('.slot-dot')?.classList.add('hover');
            }
        };

        const onMouseUp = (upEvent) => {
            window.removeEventListener('mousemove', onMouseMove);
            window.removeEventListener('mouseup', onMouseUp);
            tempPath.remove();
            sDot.classList.remove('active');
            if (currentTarget) currentTarget.querySelector('.slot-dot')?.classList.remove('hover');

            // 最终落点检测
            const target = findNearestSlot(upEvent.clientX, upEvent.clientY);

            console.log("[NoirFlow] 释放鼠标，命中的目标:", target?.id || '无');

            if (target && target !== startSlot) {
                if (target.dataset.slotType !== startSlot.dataset.slotType) {
                    createConnection(startSlot.id, target.id);
                } else {
                    console.warn("[NoirFlow] 无法连接相同类型的插槽");
                }
            }
        };

        window.addEventListener('mousemove', onMouseMove);
        window.addEventListener('mouseup', onMouseUp);
    }

    window.removeNode = (nodeId) => {
        window.connections.forEach(c => {
            const s = document.getElementById(c.startSlotId);
            const e = document.getElementById(c.endSlotId);
            if ((s && s.dataset.nodeId === nodeId) || (e && e.dataset.nodeId === nodeId)) {
                c.pathEl.remove(); c.hitEl.remove();
            }
        });
        window.connections = window.connections.filter(c => {
            const s = document.getElementById(c.startSlotId), e = document.getElementById(c.endSlotId);
            return !((s && s.dataset.nodeId === nodeId) || (e && e.dataset.nodeId === nodeId));
        });
        document.getElementById(nodeId)?.remove();
    };

    window.addEventListener('mousedown', (e) => {
        if (!e.target.closest('.context-menu')) closeContext();
        const node = e.target.closest('.node-box');
        document.querySelectorAll('.node-box').forEach(n => n.classList.remove('selected'));
        if (node) {
            node.classList.add('selected');
            window.selectedNodeId = node.id;
        }
    });

    window.clearCanvas = () => {
        document.getElementById('node-container').innerHTML = '';
        window.connections.forEach(c => { c.pathEl.remove(); c.hitEl.remove(); });
        window.connections = [];
    };

    // 初始清空示例，改为根据配置生成
    document.addEventListener('DOMContentLoaded', () => { closeContext(); });

    // 全局粘贴与点击逻辑保持不变...
    window.addEventListener('paste', function (e) {
        if (['INPUT', 'TEXTAREA'].includes(e.target.tagName)) return;
        const items = (e.clipboardData || e.originalEvent.clipboardData).items;
        let file = null;
        for (let i = 0; i < items.length; i++) {
            if (items[i].type.indexOf('image') !== -1) {
                file = items[i].getAsFile();
                break;
            }
        }
        if (file) {
            e.preventDefault();
            let selectedNode = window.selectedNodeId ? document.getElementById(window.selectedNodeId) : null;
            if (selectedNode && (selectedNode.getAttribute('data-node-type') === 'image_upload' || selectedNode.id.includes('image_upload'))) {
                window.uploadToNode(window.selectedNodeId, file);
            } else {
                const params = new URLSearchParams({ type: 'image_upload', x: 250, y: 250 });
                fetch(`/api/create_node?${params.toString()}`, { method: 'POST' })
                    .then(res => res.text())
                    .then(html => {
                        const wrapper = document.createElement('div');
                        wrapper.innerHTML = html.trim();
                        const newNode = wrapper.firstElementChild;
                        document.getElementById('node-container').appendChild(newNode);
                        htmx.process(newNode);
                        setTimeout(() => {
                            document.querySelectorAll('.node-box').forEach(n => n.classList.remove('selected'));
                            newNode.classList.add('selected');
                            window.selectedNodeId = newNode.id;
                            window.uploadToNode(newNode.id, file);
                        }, 10);
                    });
            }
        }
    }, true);

    window.uploadToNode = function (nodeId, file) {
        const formData = new FormData();
        formData.append('file', file);
        const contentEl = document.querySelector(`#${nodeId} .node-content`);
        if (contentEl) contentEl.innerHTML = '<div class="placeholder"><div class="loader-ripple"></div><span>正在上传图片...</span></div>';
        fetch('/api/upload_asset', { method: 'POST', body: formData })
            .then(res => res.text())
            .then(html => { if (contentEl) { contentEl.innerHTML = html; htmx.process(contentEl); } });
    };
</script>
{% endblock %}