/**
 * @web/mindmap
 * 类 Xmind 体验的思维导图引擎
 */

export class Mindmap {
  constructor(options = {}) {
    this.container = options.container;
    this.data = options.data || { id: 'root', text: 'Root', children: [] };
    this.nodeWidth = options.nodeWidth || 120;
    this.nodeHeight = options.nodeHeight || 40;
    this.hGap = options.hGap || 60;
    this.vGap = options.vGap || 20;
    
    this.nodes = [];
    this.links = [];
    
    this.init();
  }

  init() {
    if (!this.container) return;
    this.container.style.position = 'relative';
    this.container.style.overflow = 'hidden';
    this.container.style.backgroundColor = '#f5f5f5';
    
    // Viewport for panning and zooming
    this.viewport = document.createElement('div');
    this.viewport.style.position = 'absolute';
    this.viewport.style.width = '10000px';
    this.viewport.style.height = '10000px';
    this.viewport.style.transformOrigin = '0 0';
    this.container.appendChild(this.viewport);
    
    this.scale = 1;
    this.offsetX = this.container.clientWidth / 2 - 100;
    this.offsetY = this.container.clientHeight / 2 - 50;
    
    this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    this.svg.style.position = 'absolute';
    this.svg.style.top = '0';
    this.svg.style.left = '0';
    this.svg.style.width = '100%';
    this.svg.style.height = '100%';
    this.svg.style.pointerEvents = 'none';
    this.viewport.appendChild(this.svg);
    
    this.initEvents();
    this.updateViewport();
    this.render();
  }

  initEvents() {
    let isDragging = false;
    let lastX, lastY;
    
    this.container.onmousedown = (e) => {
      if (e.target.classList.contains('mindmap-node')) return;
      isDragging = true;
      lastX = e.clientX;
      lastY = e.clientY;
    };
    
    window.onmousemove = (e) => {
      if (!isDragging) return;
      this.offsetX += (e.clientX - lastX);
      this.offsetY += (e.clientY - lastY);
      lastX = e.clientX;
      lastY = e.clientY;
      this.updateViewport();
    };
    
    window.onmouseup = () => {
      isDragging = false;
    };
    
    this.container.onwheel = (e) => {
      e.preventDefault();
      const delta = e.deltaY > 0 ? 0.9 : 1.1;
      const newScale = this.scale * delta;
      if (newScale < 0.1 || newScale > 5) return;
      
      // Zoom relative to mouse position
      const rect = this.container.getBoundingClientRect();
      const mouseX = e.clientX - rect.left;
      const mouseY = e.clientY - rect.top;
      
      this.offsetX = mouseX - (mouseX - this.offsetX) * delta;
      this.offsetY = mouseY - (mouseY - this.offsetY) * delta;
      
      this.scale = newScale;
      this.updateViewport();
    };
  }

  updateViewport() {
    this.viewport.style.transform = `translate(${this.offsetX}px, ${this.offsetY}px) scale(${this.scale})`;
  }

  layout() {
    this.nodes = [];
    this.links = [];
    
    const compute = (node, x, startY) => {
      const isCollapsed = node._collapsed;
      const childrenHeight = (node.children && node.children.length > 0 && !isCollapsed)
        ? node.children.reduce((acc, child) => acc + this.getTotalHeight(child), 0)
        : this.nodeHeight;
      
      const y = startY + (childrenHeight / 2) - (this.nodeHeight / 2);
      
      const nodeInfo = {
        x,
        y,
        text: node.text,
        id: node.id || Math.random().toString(36).substr(2, 9),
        raw: node
      };
      this.nodes.push(nodeInfo);
      
      if (node.children && !isCollapsed) {
        let currentY = startY;
        node.children.forEach(child => {
          const childInfo = compute(child, x + this.nodeWidth + this.hGap, currentY);
          this.links.push({
            x1: x + this.nodeWidth,
            y1: y + this.nodeHeight / 2,
            x2: childInfo.x,
            y2: childInfo.y + this.nodeHeight / 2
          });
          currentY += this.getTotalHeight(child);
        });
      }
      return nodeInfo;
    };
    
    compute(this.data, 0, 0); // Start from (0,0) relative to viewport
  }

  getTotalHeight(node) {
    if (!node.children || node.children.length === 0 || node._collapsed) {
      return this.nodeHeight + this.vGap;
    }
    return node.children.reduce((acc, child) => acc + this.getTotalHeight(child), 0);
  }

  render() {
    this.layout();
    
    // Clear previous
    this.svg.innerHTML = '';
    const existingNodes = this.viewport.querySelectorAll('.mindmap-node');
    existingNodes.forEach(n => n.remove());
    
    // Render links
    this.links.forEach(link => {
      const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
      const d = `M ${link.x1} ${link.y1} C ${link.x1 + this.hGap/2} ${link.y1}, ${link.x2 - this.hGap/2} ${link.y2}, ${link.x2} ${link.y2}`;
      path.setAttribute('d', d);
      path.setAttribute('stroke', '#4A90E2');
      path.setAttribute('fill', 'none');
      path.setAttribute('stroke-width', '2');
      this.svg.appendChild(path);
    });
    
    // Render nodes
    this.nodes.forEach(node => {
      const el = document.createElement('div');
      el.className = 'mindmap-node';
      if (node.raw._collapsed) el.classList.add('collapsed');
      el.style.position = 'absolute';
      el.style.left = `${node.x}px`;
      el.style.top = `${node.y}px`;
      el.style.width = `${this.nodeWidth}px`;
      el.style.height = `${this.nodeHeight}px`;
      el.style.border = '2px solid #4A90E2';
      el.style.borderRadius = '8px';
      el.style.backgroundColor = '#fff';
      el.style.display = 'flex';
      el.style.alignItems = 'center';
      el.style.justifyContent = 'center';
      el.style.fontSize = '14px';
      el.style.fontWeight = 'bold';
      el.style.color = '#333';
      el.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
      el.style.cursor = 'pointer';
      el.style.userSelect = 'none';
      el.style.transition = 'transform 0.2s';
      el.innerText = node.text;
      
      if (node.raw._collapsed) {
        el.style.backgroundColor = '#f0f0f0';
        el.style.borderStyle = 'dashed';
      }

      el.onmouseenter = () => el.style.transform = 'scale(1.05)';
      el.onmouseleave = () => el.style.transform = 'scale(1)';

      el.onclick = (e) => {
        e.stopPropagation();
        node.raw._collapsed = !node.raw._collapsed;
        this.render();
      };
      
      this.viewport.appendChild(el);
    });
  }

  addNode(parentId, text) {
    const findAndAdd = (node) => {
      if (node.id === parentId || (node.text === parentId)) { // Fallback to text for easy testing
        if (!node.children) node.children = [];
        node.children.push({ text, id: Math.random().toString(36).substr(2, 9) });
        return true;
      }
      if (node.children) {
        for (const child of node.children) {
          if (findAndAdd(child)) return true;
        }
      }
      return false;
    };
    findAndAdd(this.data);
    this.render();
  }

  removeNode(nodeId) {
    const remove = (node) => {
      if (!node.children) return false;
      const index = node.children.findIndex(child => child.id === nodeId || child.text === nodeId);
      if (index !== -1) {
        node.children.splice(index, 1);
        return true;
      }
      for (const child of node.children) {
        if (remove(child)) return true;
      }
      return false;
    };
    if (this.data.id === nodeId || this.data.text === nodeId) {
      console.warn('Cannot remove root node');
      return;
    }
    remove(this.data);
    this.render();
  }
}
