let nodes = [], edges = [], selId = null, dragId = null, dOffX, dOffY;
let isPan = false, pSX, pSY, pOffX = 0, pOffY = 0, zoom = 1;
let connFrom = null, tmpLine = null, nIdCnt = 0;
let undoStk = [], redoStk = [];
const wrap = document.getElementById('wrap'), vp = document.getElementById('viewport');
const cv = document.getElementById('canvas'), svg = document.getElementById('svg');
const props = document.getElementById('props');

function sn(v) { return Math.round(v / 20) * 20; }

function buildPalette() {
  const p = document.getElementById('palette');
  CATS.forEach(([cat, types]) => {
    p.innerHTML += `<div class="cat">${cat}</div>`;
    types.forEach(t => {
      const info = NODE_TYPES[t];
      p.innerHTML += `<div class="p-item" draggable="true" data-type="${t}">
        <span class="p-icon" style="background:${info.color}33;color:${info.color}">${t.split('-')[0].substring(0,2).toUpperCase()}</span>
        ${info.label}</div>`;
    });
  });
  document.querySelectorAll('.p-item').forEach(el => {
    el.addEventListener('dragstart', e => { e.dataTransfer.setData('text/plain', el.dataset.type); });
  });
}

function addNode(type, x, y, id, label) {
  const def = NODE_TYPES[type]; if (!def) return null;
  const nid = id || (++nIdCnt);
  const el = document.createElement('div');
  el.className = 'node'; el.dataset.id = nid;
  el.style.cssText = `left:${sn(x)}px;top:${sn(y)}px;width:130px;border-color:${def.color}`;
  el.innerHTML = `<div class="rm" onclick="rmNode(${nid})">×</div>
    <div class="port port-in" data-n="${nid}"></div>
    <div class="nl">${label||def.label}</div>
    <div class="ns">${def.label}</div>
    <div class="port port-out" data-n="${nid}"></div>`;
  el.addEventListener('mousedown', e => {
    if (e.target.closest('.rm,.port')) return;
    e.stopPropagation(); selNode(nid);
    const r = el.getBoundingClientRect();
    dragId = nid; dOffX = e.clientX - r.left; dOffY = e.clientY - r.top;
    el.style.zIndex = 20; el.style.cursor = 'grabbing';
    saveSt();
  });
  el.querySelectorAll('.port').forEach(p => {
    p.addEventListener('mousedown', e => {
      e.stopPropagation(); e.preventDefault();
      if (p.classList.contains('port-out')) {
        connFrom = { id: +p.dataset.n, el: p };
        const r = p.getBoundingClientRect(), wr = vp.getBoundingClientRect();
        tmpLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
        tmpLine.setAttribute('stroke', '#58a6ff'); tmpLine.setAttribute('stroke-width', '2');
        tmpLine.setAttribute('stroke-dasharray', '6,4');
        tmpLine.setAttribute('x1', (r.left - wr.left) / zoom);
        tmpLine.setAttribute('y1', (r.top - wr.top + 4) / zoom);
        tmpLine.setAttribute('x2', (e.clientX - wr.left) / zoom);
        tmpLine.setAttribute('y2', (e.clientY - wr.top) / zoom);
        svg.appendChild(tmpLine);
      }
    });
  });
  cv.appendChild(el);
  nodes.push({ id: nid, type, x: sn(x), y: sn(y), label: label || def.label });
  updStats(); return nid;
}

function rmNode(id) {
  const el = cv.querySelector(`.node[data-id="${id}"]`);
  if (el) el.remove();
  nodes = nodes.filter(n => n.id !== id);
  edges = edges.filter(e => e.from !== id && e.to !== id);
  renEdges(); updStats(); if (selId === id) { selId = null; props.classList.remove('open'); }
  saveSt();
}

function selNode(id) {
  cv.querySelectorAll('.node').forEach(n => n.classList.remove('selected'));
  const el = cv.querySelector(`.node[data-id="${id}"]`);
  if (el) el.classList.add('selected');
  selId = id; showProps(id);
}

function showProps(id) {
  const n = nodes.find(x => x.id === id); if (!n) return;
  props.classList.add('open');
  document.getElementById('pLabel').value = n.label;
  document.getElementById('pSub').value = n.sub || '';
  document.getElementById('pWidth').value = 130;
  const sel = document.getElementById('pType');
  sel.innerHTML = '';
  Object.keys(NODE_TYPES).forEach(k => {
    const o = document.createElement('option');
    o.value = k; o.textContent = NODE_TYPES[k].label;
    if (k === n.type) o.selected = true;
    sel.appendChild(o);
  });
}

function up(f, v) {
  const n = nodes.find(x => x.id === selId); if (!n) return;
  const el = cv.querySelector(`.node[data-id="${selId}"]`); if (!el) return;
  if (f === 'label') { n.label = v; el.querySelector('.nl').textContent = v; }
  else if (f === 'type') {
    const def = NODE_TYPES[v]; if (!def) return;
    n.type = v; n.label = def.label;
    el.style.borderColor = def.color;
    el.querySelector('.nl').textContent = def.label;
    el.querySelector('.ns').textContent = def.label;
    document.getElementById('pLabel').value = def.label;
  }
  else if (f === 'w') { el.style.width = v + 'px'; }
  renEdges(); saveSt();
}

function delNode() { if (selId !== null) rmNode(selId); }

function addEdge(from, to) {
  if (from === to || edges.some(e => e.from === from && e.to === to)) return;
  edges.push({ from, to }); renEdges(); updStats(); saveSt();
}

function renEdges() {
  svg.querySelectorAll('.eg').forEach(g => g.remove());
  const wr = vp.getBoundingClientRect();
  edges.forEach((e, i) => {
    const fEl = cv.querySelector(`.node[data-id="${e.from}"]`);
    const tEl = cv.querySelector(`.node[data-id="${e.to}"]`);
    if (!fEl || !tEl) return;
    const fr = fEl.getBoundingClientRect(), tr = tEl.getBoundingClientRect();
    const x1 = (fr.right - wr.left - 8) / zoom, y1 = (fr.top + fr.height/2 - wr.top) / zoom;
    const x2 = (tr.left - wr.left + 8) / zoom, y2 = (tr.top + tr.height/2 - wr.top) / zoom;
    const cp = Math.max(40, Math.abs(x2 - x1) * 0.4);
    const d = `M${x1} ${y1} C${x1+cp} ${y1},${x2-cp} ${y2},${x2} ${y2}`;
    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.classList.add('eg');
    // glow
    const gl = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    gl.setAttribute('d', d); gl.setAttribute('stroke', '#58a6ff');
    gl.setAttribute('stroke-width', '5'); gl.setAttribute('fill', 'none');
    gl.setAttribute('opacity', '0.1');
    g.appendChild(gl);
    // main
    const m = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    m.setAttribute('d', d); m.setAttribute('stroke', '#58a6ff');
    m.setAttribute('stroke-width', '2'); m.setAttribute('fill', 'none');
    m.setAttribute('stroke-linecap', 'round');
    m.addEventListener('dblclick', () => { edges.splice(i,1); renEdges(); updStats(); saveSt(); });
    g.appendChild(m);
    // pulse
    for (let j = 0; j < 2; j++) {
      const p = document.createElementNS('http://www.w3.org/2000/svg', 'path');
      p.setAttribute('d', d); p.setAttribute('stroke', '#79c0ff');
      p.setAttribute('stroke-width', '1.5'); p.setAttribute('fill', 'none');
      p.setAttribute('stroke-dasharray', '6,14');
      p.style.animation = `pulseF ${0.8 + j*0.1}s linear infinite`;
      p.style.animationDelay = `-${j * 0.4}s`;
      g.appendChild(p);
    }
    // arrow
    const ang = Math.atan2(y2 - y1, x2 - x1);
    const ax = x2 - 4, ay = y2 - 2;
    const ar = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
    ar.setAttribute('points', `${ax},${ay} ${ax-8*Math.cos(ang-.4)},${ay-8*Math.sin(ang-.4)} ${ax-8*Math.cos(ang+.4)},${ay-8*Math.sin(ang+.4)}`);
    ar.setAttribute('fill', '#58a6ff');
    g.appendChild(ar);
    svg.appendChild(g);
  });
}

document.addEventListener('mousemove', e => {
  if (dragId !== null) {
    const el = cv.querySelector(`.node[data-id="${dragId}"]`); if (!el) return;
    const wr = vp.getBoundingClientRect();
    let x = (e.clientX - wr.left - dOffX) / zoom, y = (e.clientY - wr.top - dOffY) / zoom;
    x = sn(Math.max(0,x)); y = sn(Math.max(0,y));
    el.style.left = x + 'px'; el.style.top = y + 'px';
    const n = nodes.find(nn => nn.id === dragId); if (n) { n.x = x; n.y = y; }
    renEdges();
  }
  if (tmpLine) {
    const wr = vp.getBoundingClientRect();
    tmpLine.setAttribute('x2', (e.clientX - wr.left)/zoom);
    tmpLine.setAttribute('y2', (e.clientY - wr.top)/zoom);
  }
  if (isPan) {
    pOffX += e.clientX - pSX; pOffY += e.clientY - pSY;
    pSX = e.clientX; pSY = e.clientY;
    vp.style.transform = `translate(${pOffX}px,${pOffY}px) scale(${zoom})`;
  }
});

document.addEventListener('mouseup', e => {
  if (dragId !== null) {
    const el = cv.querySelector(`.node[data-id="${dragId}"]`);
    if (el) el.style.cursor = 'move';
    dragId = null;
  }
  if (tmpLine) {
    tmpLine.remove(); tmpLine = null;
    const t = e.target.closest('.port-in');
    if (t && connFrom) addEdge(connFrom.id, +t.dataset.n);
    connFrom = null;
  }
  if (isPan) { isPan = false; wrap.style.cursor = 'grab'; }
});

wrap.addEventListener('mousedown', e => {
  if (e.target.closest('.node')) return;
  cv.querySelectorAll('.node').forEach(n => n.classList.remove('selected'));
  selId = null; props.classList.remove('open');
  isPan = true; pSX = e.clientX; pSY = e.clientY; wrap.style.cursor = 'grabbing';
});

wrap.addEventListener('wheel', e => {
  e.preventDefault();
  const d = e.deltaY > 0 ? 0.9 : 1.1, nz = Math.max(0.2, Math.min(3, zoom * d));
  const wr = wrap.getBoundingClientRect();
  const vx = (e.clientX - wr.left - pOffX) / zoom, vy = (e.clientY - wr.top - pOffY) / zoom;
  zoom = nz;
  pOffX = e.clientX - wr.left - vx * zoom; pOffY = e.clientY - wr.top - vy * zoom;
  vp.style.transform = `translate(${pOffX}px,${pOffY}px) scale(${zoom})`;
  document.getElementById('zoom').textContent = Math.round(zoom*100) + '%';
}, { passive: false });

cv.addEventListener('dragover', e => e.preventDefault());
cv.addEventListener('drop', e => {
  e.preventDefault();
  const type = e.dataTransfer.getData('text/plain');
  if (!type || !NODE_TYPES[type]) return;
  const wr = vp.getBoundingClientRect();
  let x = (e.clientX - wr.left)/zoom - 65, y = (e.clientY - wr.top)/zoom - 20;
  addNode(type, sn(Math.max(0,x)), sn(Math.max(0,y)));
  saveSt();
});

function saveSt() {
  undoStk.push(JSON.stringify({ nodes: nodes.map(n => ({...n})), edges: [...edges] }));
  if (undoStk.length > 50) undoStk.shift();
  redoStk = [];
}

function undo() {
  if (undoStk.length < 2) return;
  redoStk.push(undoStk.pop());
  loadSt(JSON.parse(undoStk[undoStk.length-1]));
}

function redo() {
  if (!redoStk.length) return;
  undoStk.push(redoStk.pop());
  loadSt(JSON.parse(undoStk[undoStk.length-1]));
}

function loadSt(st) {
  cv.querySelectorAll('.node').forEach(el => el.remove());
  svg.querySelectorAll('.eg').forEach(g => g.remove());
  nodes = []; edges = st.edges || [];
  (st.nodes || []).forEach(n => addNode(n.type, n.x, n.y, n.id, n.label));
  renEdges(); updStats();
}

function updStats() {
  document.getElementById('stats').textContent = `${nodes.length} componenten · ${edges.length} connecties`;
}

function fit() {
  if (!nodes.length) return;
  const mx = Math.min(...nodes.map(n => n.x)), my = Math.min(...nodes.map(n => n.y));
  const Mx = Math.max(...nodes.map(n => n.x + 130)), My = Math.max(...nodes.map(n => n.y + 44));
  const wr = wrap.getBoundingClientRect();
  zoom = Math.max(0.2, Math.min(wr.width/(Mx-mx+150), (wr.height-60)/(My-my+80), 1.5));
  pOffX = -mx * zoom + 50; pOffY = -my * zoom + 30;
  vp.style.transform = `translate(${pOffX}px,${pOffY}px) scale(${zoom})`;
  document.getElementById('zoom').textContent = Math.round(zoom*100)+'%';
}

function filterPalette(q) {
  q = q.toLowerCase();
  document.querySelectorAll('.p-item').forEach(el => el.style.display = el.textContent.toLowerCase().includes(q) ? 'flex' : 'none');
  document.querySelectorAll('.cat').forEach(el => {
    let sib = el.nextElementSibling, has = false;
    while (sib && sib.classList.contains('p-item')) { if (sib.style.display !== 'none') has = true; sib = sib.nextElementSibling; }
    el.style.display = has ? 'block' : 'none';
  });
}

async function save() {
  const name = document.getElementById('diagramName').value;
  const client_id = document.getElementById('clientSelect').value;
  if (!name || !client_id) { setStatus('Naam en cliënt verplicht', 'err'); return; }
  const data = { id: DIAGRAM_ID || null, client_id: +client_id, name,
    nodes: nodes.map(n => ({...n})), edges: [...edges] };
  try {
    const r = await fetch('/python-editor/api/save', {
      method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(data)
    });
    const d = await r.json();
    setStatus('Opgeslagen ✓', 'ok');
    if (d.id && !DIAGRAM_ID) history.replaceState(null, '', '/python-editor/?id=' + d.id);
  } catch(e) { setStatus('Fout bij opslaan', 'err'); }
}

function setStatus(msg, t) {
  const el = document.getElementById('status');
  el.textContent = msg; el.style.color = t === 'err' ? '#f85149' : '#3fb950';
  setTimeout(() => { el.textContent = ''; }, 3000);
}

document.addEventListener('keydown', e => {
  if (e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT') return;
  if (e.key === 'Delete' || e.key === 'Backspace') { delNode(); e.preventDefault(); }
  if ((e.ctrlKey||e.metaKey) && e.key === 'z') { e.shiftKey ? redo() : undo(); e.preventDefault(); }
  if ((e.ctrlKey||e.metaKey) && e.key === 's') { save(); e.preventDefault(); }
});

const style = document.createElement('style');
style.textContent = `@keyframes pulseF{0%{stroke-dashoffset:20}100%{stroke-dashoffset:0}}`;
document.head.appendChild(style);

buildPalette();
fetch('/python-editor/api/clients').then(r=>r.json()).then(clients => {
  const sel = document.getElementById('clientSelect');
  clients.forEach(c => { sel.innerHTML += `<option value="${c.id}">${c.name}</option>`; });
});
if (NODES_JSON && NODES_JSON.length) {
  NODES_JSON.forEach(n => addNode(n.type, n.x, n.y, n.id, n.label));
  edges = EDGES_JSON || [];
  renEdges(); updStats(); saveSt();
  setTimeout(fit, 100);
}
nIdCnt = nodes.length;
updStats(); saveSt();
