/**
 * Cast Conductor Proprietary License v5
 * SPDX-License-Identifier: LicenseRef-CastConductor-Proprietary-v5
 * 
 * Layer Grouping Module
 * 
 * Features:
 * - Multi-select layers (Shift+click, marquee drag)
 * - Group/Ungroup selected layers (G shortcut, context menu)
 * - Move group = move all children
 * - Corner drag = proportional resize all children
 * - Collapse/expand in layer panel
 */

/**
 * Generate a unique group ID
 */
function generateGroupId() {
  return `group_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
}

/**
 * LayerGroupingManager handles multi-selection and grouping
 */
export class LayerGroupingManager {
  constructor(editor) {
    this.editor = editor;
    this.selectedLayerIds = new Set();
    this.marquee = null;
    this._boundHandlers = {};
    this._initialized = false;
  }

  /**
   * Initialize the grouping manager
   */
  init() {
    if (this._initialized) return;
    this._initialized = true;

    // Ensure layerGroups exists in config
    if (this.editor.currentConfig && !this.editor.currentConfig.layerGroups) {
      this.editor.currentConfig.layerGroups = {};
    }

    this._bindKeyboardShortcuts();
    this._bindMarqueeSelection();
    
    console.log('[CCVE][LayerGrouping] Initialized');
  }

  /**
   * Get the stage element
   */
  get stage() {
    return document.getElementById('block-stage');
  }

  /**
   * Check if a layer is currently selected
   */
  isSelected(layerId) {
    return this.selectedLayerIds.has(layerId);
  }

  /**
   * Get all selected layer IDs
   */
  getSelectedIds() {
    return Array.from(this.selectedLayerIds);
  }

  /**
   * Get selected layer objects
   */
  getSelectedLayers() {
    const layers = this.editor.currentConfig?.layers || [];
    return layers.filter(l => l && this.selectedLayerIds.has(l.id));
  }

  /**
   * Get the label for a group
   */
  getGroupLabel(groupId) {
    const groups = this.editor.currentConfig?.layerGroups || {};
    return groups[groupId]?.label || null;
  }

  /**
   * Select all layers in a group
   */
  selectGroup(groupId) {
    const groups = this.editor.currentConfig?.layerGroups || {};
    const group = groups[groupId];
    
    if (!group) {
      console.warn('[CCVE][LayerGrouping] Group not found:', groupId);
      return;
    }

    this.selectLayers(group.memberIds, false);
  }

  /**
   * Clear all selections
   */
  clearSelection() {
    this.selectedLayerIds.clear();
    this._updateSelectionVisuals();
    this._emitSelectionChange();
    // Hide the badge
    const badge = document.getElementById('ccve-selection-badge');
    if (badge) badge.style.display = 'none';
  }

  /**
   * Select a single layer (replaces current selection)
   * If the layer is part of a group, auto-select all group members
   */
  selectLayer(layerId, addToSelection = false) {
    if (!addToSelection) {
      this.selectedLayerIds.clear();
    }
    
    if (layerId) {
      if (this.selectedLayerIds.has(layerId) && addToSelection) {
        // Toggle off if already selected and using Shift
        this.selectedLayerIds.delete(layerId);
      } else {
        this.selectedLayerIds.add(layerId);
        
        // Check if this layer is in a group - auto-select all group members
        const layer = (this.editor.currentConfig?.layers || []).find(l => l.id === layerId);
        if (layer?.groupId) {
          const group = this.getGroup(layer.groupId);
          if (group?.memberIds) {
            group.memberIds.forEach(id => this.selectedLayerIds.add(id));
          }
        }
      }
    }
    
    this._updateSelectionVisuals();
    this._emitSelectionChange();
  }

  /**
   * Select multiple layers
   */
  selectLayers(layerIds, addToSelection = false) {
    if (!addToSelection) {
      this.selectedLayerIds.clear();
    }
    
    layerIds.forEach(id => this.selectedLayerIds.add(id));
    this._updateSelectionVisuals();
    this._emitSelectionChange();
  }

  /**
   * Select all layers within a bounding rectangle
   */
  selectLayersInRect(rect) {
    const stage = this.stage;
    if (!stage) return;

    const selectedIds = [];
    const layers = stage.querySelectorAll('.unified-layer');
    
    layers.forEach(layerEl => {
      const layerRect = layerEl.getBoundingClientRect();
      const stageRect = stage.getBoundingClientRect();
      
      // Convert to relative coordinates
      const relRect = {
        left: layerRect.left - stageRect.left,
        top: layerRect.top - stageRect.top,
        right: layerRect.right - stageRect.left,
        bottom: layerRect.bottom - stageRect.top
      };
      
      // Check intersection
      if (this._rectsIntersect(rect, relRect)) {
        const id = layerEl.getAttribute('data-layer-id');
        if (id) selectedIds.push(id);
      }
    });

    this.selectLayers(selectedIds, false);
  }

  /**
   * Check if two rectangles intersect
   */
  _rectsIntersect(r1, r2) {
    return !(r2.left > r1.right || 
             r2.right < r1.left || 
             r2.top > r1.bottom ||
             r2.bottom < r1.top);
  }

  /**
   * Update visual indicators for selected layers
   */
  _updateSelectionVisuals() {
    const stage = this.stage;
    if (!stage) return;

    // Update layer elements
    stage.querySelectorAll('.unified-layer').forEach(el => {
      const id = el.getAttribute('data-layer-id');
      if (this.selectedLayerIds.has(id)) {
        el.classList.add('unified-layer--selected');
        el.classList.add('unified-layer--multi-selected');
      } else {
        el.classList.remove('unified-layer--selected');
        el.classList.remove('unified-layer--multi-selected');
      }
    });

    // Update layer panel items
    const layerList = document.getElementById('ccve-layers-list');
    if (layerList) {
      layerList.querySelectorAll('li').forEach(li => {
        const id = li.getAttribute('data-layer-id');
        if (this.selectedLayerIds.has(id)) {
          li.classList.add('active');
          li.classList.add('multi-selected');
        } else {
          li.classList.remove('active');
          li.classList.remove('multi-selected');
        }
      });
    }

    // Show selection count badge if multiple selected
    this._updateSelectionBadge();
  }

  /**
   * Update selection count badge - shows group info if selected layers are in a group
   */
  _updateSelectionBadge() {
    const selectedIds = this.getSelectedIds();
    const count = selectedIds.length;
    
    if (count === 0) {
      const badge = document.getElementById('ccve-selection-badge');
      if (badge) badge.style.display = 'none';
      return;
    }
    
    // Check if all selected layers are in the same group
    const layers = this.editor.currentConfig?.layers || [];
    const groupIds = new Set();
    
    selectedIds.forEach(id => {
      const layer = layers.find(l => l.id === id);
      if (layer?.groupId) {
        groupIds.add(layer.groupId);
      }
    });
    
    if (groupIds.size === 1) {
      // All selected are in one group - show group info (works for single or multiple)
      const groupId = [...groupIds][0];
      const group = this.getGroup(groupId);
      const label = group?.label || 'Group';
      const memberCount = group?.memberIds?.length || count;
      if (count === 1) {
        // Single layer clicked that's in a group - show info with fade
        this._showBadge(`Layer in group "${label}" (${memberCount} members) • G to ungroup`, 'ingroup');
      } else {
        // Multiple layers selected, all in same group
        this._showBadge(`${count} layers in "${label}" • G to ungroup`, 'grouped');
      }
    } else if (groupIds.size > 1) {
      // Mixed groups
      this._showBadge(`${count} layers selected (multiple groups)`, 'selected');
    } else if (count === 1) {
      // Single layer, not in a group - no badge needed
      const badge = document.getElementById('ccve-selection-badge');
      if (badge) badge.style.display = 'none';
    } else {
      // Multiple layers, no groups
      this._showBadge(`${count} layers selected • G to group`, 'selected');
    }
  }

  /**
   * Show a status message on the badge
   * @param {string} message - The message to display
   * @param {string} type - 'selected' | 'grouped' | 'ungrouped' | 'ingroup'
   * @param {number} duration - How long to show (ms), 0 = until next update
   */
  _showBadge(message, type = 'selected', duration = 0) {
    // Clear any pending fade timer
    if (this._badgeFadeTimer) {
      clearTimeout(this._badgeFadeTimer);
      this._badgeFadeTimer = null;
    }

    let badge = document.getElementById('ccve-selection-badge');
    const count = this.selectedLayerIds.size;

    // Show badge for: 2+ layers, status messages, or single layer in a group
    const shouldShow = count > 1 || type !== 'selected' || message.includes('in group');
    if (shouldShow) {
      if (!badge) {
        badge = document.createElement('div');
        badge.id = 'ccve-selection-badge';
        document.body.appendChild(badge);
      }
      badge.className = 'ccve-selection-badge';
      if (type === 'grouped' || type === 'ingroup') badge.classList.add('grouped');
      if (type === 'ungrouped') badge.classList.add('ungrouped');
      badge.classList.remove('fade-out');
      badge.textContent = message;
      badge.style.display = 'block';

      // Auto-fade: status messages fade after 2.5s, single-layer group info after 3s, multi-select stays
      let fadeDelay = duration;
      if (fadeDelay === 0) {
        if (type === 'ingroup') fadeDelay = 3000;
        else if (type !== 'selected') fadeDelay = 2500;
      }
      if (fadeDelay > 0) {
        this._badgeFadeTimer = setTimeout(() => {
          badge.classList.add('fade-out');
          setTimeout(() => {
            if (badge.classList.contains('fade-out')) {
              badge.style.display = 'none';
            }
          }, 300);
        }, fadeDelay);
      }
    } else if (badge) {
      badge.style.display = 'none';
    }
  }

  /**
   * Emit selection change event
   */
  _emitSelectionChange() {
    try {
      window.ccveBus?.emit('layer:selection-changed', {
        selectedIds: this.getSelectedIds(),
        count: this.selectedLayerIds.size
      });
    } catch (e) {}
    
    // Update the layer panel to show/hide the group action bar
    try {
      this.editor.buildLayersPanel?.();
    } catch (e) {}
  }

  /**
   * Bind keyboard shortcuts (G for group/ungroup)
   */
  _bindKeyboardShortcuts() {
    const handler = (e) => {
      // Skip if typing in input/textarea
      if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) {
        return;
      }

      // G key - Group/Ungroup
      if (e.key.toLowerCase() === 'g' && !e.ctrlKey && !e.metaKey && !e.altKey) {
        // Don't trigger if user is typing in an input/textarea
        const activeEl = document.activeElement;
        if (activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA' || activeEl.isContentEditable)) {
          return;
        }
        console.log('[CCVE][LayerGrouping] G key pressed, selected:', this.getSelectedIds());
        e.preventDefault();
        this.toggleGroup();
      }

      // Escape - Clear selection
      if (e.key === 'Escape') {
        this.clearSelection();
      }

      // Ctrl/Cmd + A - Select all layers
      if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'a') {
        const layers = this.editor.currentConfig?.layers || [];
        console.log('[CCVE][LayerGrouping] Cmd+A pressed, found', layers.length, 'layers');
        if (layers.length > 0) {
          e.preventDefault();
          this.selectLayers(layers.map(l => l.id));
          console.log('[CCVE][LayerGrouping] Selected all:', this.getSelectedIds());
        }
      }
    };

    this._boundHandlers.keydown = handler;
    document.addEventListener('keydown', handler);
  }

  /**
   * Bind marquee (box) selection on the canvas
   */
  _bindMarqueeSelection() {
    const stage = this.stage;
    if (!stage) return;

    let marqueeEl = null;
    let startX = 0, startY = 0;
    let isMarqueeActive = false;

    const onMouseDown = (e) => {
      // Only start marquee on empty canvas area (not on layers)
      const isLayerClick = e.target.closest('.unified-layer');
      const isHandle = e.target.closest('.ccve-layer-handle');
      
      if (isLayerClick) {
        // Handle Shift+click for multi-select
        if (e.shiftKey && !isHandle) {
          const layerId = isLayerClick.getAttribute('data-layer-id');
          if (layerId) {
            e.preventDefault();
            e.stopPropagation();
            this.selectLayer(layerId, true); // Add to selection
          }
        }
        return;
      }

      // Start marquee selection on empty canvas
      if (e.button !== 0) return; // Left click only
      
      const stageRect = stage.getBoundingClientRect();
      startX = e.clientX - stageRect.left;
      startY = e.clientY - stageRect.top;
      isMarqueeActive = true;

      // Clear existing selection unless Shift is held
      if (!e.shiftKey) {
        this.clearSelection();
      }

      // Create marquee element
      marqueeEl = document.createElement('div');
      marqueeEl.className = 'ccve-marquee-selection';
      marqueeEl.style.cssText = `
        position: absolute;
        left: ${startX}px;
        top: ${startY}px;
        width: 0;
        height: 0;
        border: 1px dashed #38bdf8;
        background: rgba(56, 189, 248, 0.1);
        pointer-events: none;
        z-index: 9999;
      `;
      stage.appendChild(marqueeEl);

      document.addEventListener('mousemove', onMouseMove);
      document.addEventListener('mouseup', onMouseUp);
      e.preventDefault();
    };

    const onMouseMove = (e) => {
      if (!isMarqueeActive || !marqueeEl) return;

      const stageRect = stage.getBoundingClientRect();
      const currentX = e.clientX - stageRect.left;
      const currentY = e.clientY - stageRect.top;

      const left = Math.min(startX, currentX);
      const top = Math.min(startY, currentY);
      const width = Math.abs(currentX - startX);
      const height = Math.abs(currentY - startY);

      marqueeEl.style.left = left + 'px';
      marqueeEl.style.top = top + 'px';
      marqueeEl.style.width = width + 'px';
      marqueeEl.style.height = height + 'px';
    };

    const onMouseUp = (e) => {
      if (!isMarqueeActive) return;
      isMarqueeActive = false;

      if (marqueeEl) {
        const stageRect = stage.getBoundingClientRect();
        const currentX = e.clientX - stageRect.left;
        const currentY = e.clientY - stageRect.top;

        // Only select if marquee is larger than a small threshold (5px)
        const width = Math.abs(currentX - startX);
        const height = Math.abs(currentY - startY);
        
        if (width > 5 || height > 5) {
          const rect = {
            left: Math.min(startX, currentX),
            top: Math.min(startY, currentY),
            right: Math.max(startX, currentX),
            bottom: Math.max(startY, currentY)
          };
          this.selectLayersInRect(rect);
        }

        marqueeEl.remove();
        marqueeEl = null;
      }

      document.removeEventListener('mousemove', onMouseMove);
      document.removeEventListener('mouseup', onMouseUp);
    };

    this._boundHandlers.marqueeMouseDown = onMouseDown;
    stage.addEventListener('mousedown', onMouseDown);
  }

  /**
   * Group selected layers
   */
  groupSelectedLayers() {
    const selectedIds = this.getSelectedIds();
    if (selectedIds.length < 2) {
      console.log('[CCVE][LayerGrouping] Need at least 2 layers to create a group');
      return null;
    }

    // Check if any selected layers are already in a group
    const layers = this.editor.currentConfig?.layers || [];
    const groups = this.editor.currentConfig?.layerGroups || {};
    
    for (const id of selectedIds) {
      const layer = layers.find(l => l.id === id);
      if (layer?.groupId) {
        console.log('[CCVE][LayerGrouping] Layer already in group:', id, layer.groupId);
        // Could offer to merge groups here
      }
    }

    // Create new group
    const groupId = generateGroupId();
    const groupLabel = `Group ${Object.keys(groups).length + 1}`;
    
    // Update config
    if (!this.editor.currentConfig.layerGroups) {
      this.editor.currentConfig.layerGroups = {};
    }
    
    this.editor.currentConfig.layerGroups[groupId] = {
      id: groupId,
      label: groupLabel,
      collapsed: false,
      memberIds: [...selectedIds]
    };

    // Update layer objects with groupId
    selectedIds.forEach(id => {
      const layer = layers.find(l => l.id === id);
      if (layer) {
        layer.groupId = groupId;
      }
    });

    // Mark unsaved changes
    this.editor.unsavedChanges = true;
    
    // Rebuild layers panel to show group
    try {
      this.editor.buildLayersPanel?.();
    } catch (e) {}

    console.log('[CCVE][LayerGrouping] Created group:', groupId, 'with', selectedIds.length, 'layers');
    
    // Emit event
    try {
      window.ccveBus?.emit('layer:grouped', { groupId, memberIds: selectedIds });
    } catch (e) {}

    return groupId;
  }

  /**
   * Ungroup layers - dissolve group but keep layers
   */
  ungroupLayers(groupId) {
    const groups = this.editor.currentConfig?.layerGroups;
    const layers = this.editor.currentConfig?.layers || [];
    
    if (!groups || !groups[groupId]) {
      console.warn('[CCVE][LayerGrouping] Group not found:', groupId);
      return;
    }

    const group = groups[groupId];
    
    // Remove groupId from member layers
    group.memberIds.forEach(id => {
      const layer = layers.find(l => l.id === id);
      if (layer) {
        delete layer.groupId;
      }
    });

    // Delete the group
    delete groups[groupId];

    // Mark unsaved changes
    this.editor.unsavedChanges = true;
    
    // Rebuild layers panel
    try {
      this.editor.buildLayersPanel?.();
    } catch (e) {}

    console.log('[CCVE][LayerGrouping] Ungrouped:', groupId);
    
    // Emit event
    try {
      window.ccveBus?.emit('layer:ungrouped', { groupId });
    } catch (e) {}
  }

  /**
   * Toggle group/ungroup for selected layers
   */
  toggleGroup() {
    const selectedIds = this.getSelectedIds();
    console.log('[CCVE][LayerGrouping] toggleGroup called, selectedIds:', selectedIds);
    
    if (selectedIds.length === 0) {
      console.log('[CCVE][LayerGrouping] No layers selected');
      return;
    }

    // Check if all selected layers are in the same group
    const layers = this.editor.currentConfig?.layers || [];
    const groupIds = new Set();
    
    selectedIds.forEach(id => {
      const layer = layers.find(l => l.id === id);
      if (layer?.groupId) {
        groupIds.add(layer.groupId);
      }
    });

    if (groupIds.size === 1) {
      // All selected are in the same group - ungroup
      const groupId = [...groupIds][0];
      const count = selectedIds.length;
      this.ungroupLayers(groupId);
      this._showBadge(`${count} layers ungrouped`, 'ungrouped', 2500);
    } else if (selectedIds.length >= 2) {
      // Not in a group or in different groups - create new group
      const groupId = this.groupSelectedLayers();
      if (groupId) {
        this._showBadge(`${selectedIds.length} layers grouped`, 'grouped', 2500);
      }
    } else {
      console.log('[CCVE][LayerGrouping] Need 2+ layers to group');
      this._showBadge('Select 2+ layers to group', 'selected', 2000);
    }
  }

  /**
   * Get group by ID
   */
  getGroup(groupId) {
    return this.editor.currentConfig?.layerGroups?.[groupId] || null;
  }

  /**
   * Get all groups
   */
  getAllGroups() {
    return this.editor.currentConfig?.layerGroups || {};
  }

  /**
   * Get group for a layer
   */
  getLayerGroup(layerId) {
    const layer = (this.editor.currentConfig?.layers || []).find(l => l.id === layerId);
    if (!layer?.groupId) return null;
    return this.getGroup(layer.groupId);
  }

  /**
   * Get all layer IDs in a group
   */
  getGroupMemberIds(groupId) {
    const group = this.getGroup(groupId);
    return group?.memberIds || [];
  }

  /**
   * Get bounding box of a group (in container-relative coords)
   */
  getGroupBounds(groupId) {
    const memberIds = this.getGroupMemberIds(groupId);
    const layers = this.editor.currentConfig?.layers || [];
    
    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
    
    memberIds.forEach(id => {
      const layer = layers.find(l => l.id === id);
      if (!layer) return;
      
      const x = layer.x || 0;
      const y = layer.y || 0;
      const w = layer.width || 100;
      const h = layer.height || 100;
      
      minX = Math.min(minX, x);
      minY = Math.min(minY, y);
      maxX = Math.max(maxX, x + w);
      maxY = Math.max(maxY, y + h);
    });

    if (minX === Infinity) return null;

    return {
      x: minX,
      y: minY,
      width: maxX - minX,
      height: maxY - minY
    };
  }

  /**
   * Move all layers in a group by delta
   */
  moveGroup(groupId, dx, dy) {
    const memberIds = this.getGroupMemberIds(groupId);
    const layers = this.editor.currentConfig?.layers || [];
    
    memberIds.forEach(id => {
      const layer = layers.find(l => l.id === id);
      if (!layer) return;
      
      layer.x = (layer.x || 0) + dx;
      layer.y = (layer.y || 0) + dy;
    });

    this.editor.unsavedChanges = true;
  }

  /**
   * Proportionally resize all layers in a group
   * @param {string} groupId - Group to resize
   * @param {number} scaleX - Horizontal scale factor
   * @param {number} scaleY - Vertical scale factor  
   * @param {object} anchor - Anchor point { x, y } for scaling (default: group top-left)
   */
  resizeGroup(groupId, scaleX, scaleY, anchor = null) {
    const memberIds = this.getGroupMemberIds(groupId);
    const layers = this.editor.currentConfig?.layers || [];
    const bounds = this.getGroupBounds(groupId);
    
    if (!bounds) return;
    
    // Default anchor is top-left of group
    const anchorX = anchor?.x ?? bounds.x;
    const anchorY = anchor?.y ?? bounds.y;

    memberIds.forEach(id => {
      const layer = layers.find(l => l.id === id);
      if (!layer) return;

      // Calculate relative position from anchor
      const relX = (layer.x || 0) - anchorX;
      const relY = (layer.y || 0) - anchorY;
      
      // Scale position relative to anchor
      layer.x = anchorX + (relX * scaleX);
      layer.y = anchorY + (relY * scaleY);
      
      // Scale dimensions
      layer.width = (layer.width || 100) * scaleX;
      layer.height = (layer.height || 100) * scaleY;
      
      // Scale font size for text layers
      if (['token-text', 'static-text'].includes(layer.kind)) {
        const style = layer.style || {};
        const currentSize = style.font_size || layer.font_size || 24;
        const avgScale = (scaleX + scaleY) / 2;
        const newSize = Math.round(currentSize * avgScale);
        if (!layer.style) layer.style = {};
        layer.style.font_size = Math.max(8, Math.min(200, newSize));
      }
    });

    this.editor.unsavedChanges = true;
  }

  /**
   * Toggle collapsed state of a group in layer panel
   */
  toggleGroupCollapsed(groupId) {
    const group = this.getGroup(groupId);
    if (!group) return;
    
    group.collapsed = !group.collapsed;
    
    // Rebuild layers panel
    try {
      this.editor.buildLayersPanel?.();
    } catch (e) {}
  }

  /**
   * Rename a group
   */
  renameGroup(groupId, newLabel) {
    const group = this.getGroup(groupId);
    if (!group) return;
    
    group.label = newLabel;
    this.editor.unsavedChanges = true;
    
    // Rebuild layers panel
    try {
      this.editor.buildLayersPanel?.();
    } catch (e) {}
  }

  /**
   * Cleanup and remove event listeners
   */
  destroy() {
    if (this._boundHandlers.keydown) {
      document.removeEventListener('keydown', this._boundHandlers.keydown);
    }
    
    const stage = this.stage;
    if (stage && this._boundHandlers.marqueeMouseDown) {
      stage.removeEventListener('mousedown', this._boundHandlers.marqueeMouseDown);
    }

    // Remove selection badge
    const badge = document.getElementById('ccve-selection-badge');
    if (badge) badge.remove();

    this._initialized = false;
    console.log('[CCVE][LayerGrouping] Destroyed');
  }
}

/**
 * Export singleton factory
 */
let _instance = null;
export function getLayerGroupingManager(editor) {
  if (!_instance || _instance.editor !== editor) {
    if (_instance) _instance.destroy();
    _instance = new LayerGroupingManager(editor);
  }
  return _instance;
}

export default { LayerGroupingManager, getLayerGroupingManager };
