import { useState, useMemo, useEffect, useRef, useCallback } from 'react';

const ENGINE_OPTIONS = [
  { value: null, label: 'Auto' },
  { value: 'druid', label: 'Druid' },
  { value: 'trino', label: 'Trino' },
];

// MIME type used to carry a dimension name during drag-to-filter.
const DIM_DRAG_MIME = 'application/x-dj-dimension';

// Append a filter expression onto an in-progress one, ANDing if non-empty.
const andAppend = (prev, expr) =>
  (prev.trim() ? prev.trimEnd() + ' AND ' : '') + expr;

/**
 * Label that clips the START under truncation, keeping the END visible:
 * "…erience_f__cart_initiation". Metric/dimension names share long common
 * prefixes, so a normal right-side ellipsis hides the only part that differs;
 * clipping the start surfaces the distinguishing tail. The text stays a single
 * node (the CSS direction trick puts the ellipsis on the left), so the full
 * value remains in the title tooltip and queryable by tests.
 */
function TailLabel({ text }) {
  return (
    <span className="chip-label chip-label-clip" title={text}>
      <bdi className="chip-label-inner">{text}</bdi>
    </span>
  );
}

/**
 * SelectionPanel - Browse and select metrics and dimensions
 * Features selected items as chips at the top for visibility
 * Includes cube preset loading for quick configuration
 */
export function SelectionPanel({
  metrics,
  selectedMetrics,
  onMetricsChange,
  dimensions,
  selectedDimensions,
  onDimensionsChange,
  loading,
  cubes = [],
  onLoadCubePreset,
  loadedCubeName = null, // Managed by parent for URL persistence
  onClearSelection,
  filters = [],
  onFiltersChange,
  selectedEngine = null,
  onEngineChange,
  onRunQuery,
  canRunQuery = false,
  queryLoading = false,
  compatibleMetrics = null, // Set<string> of compatible metric names, or null if no filter
}) {
  const [metricsSearch, setMetricsSearch] = useState('');
  const [dimensionsSearch, setDimensionsSearch] = useState('');
  const [expandedNamespaces, setExpandedNamespaces] = useState(new Set());
  const [expandedDimGroups, setExpandedDimGroups] = useState(new Set());
  const [expandedRolePaths, setExpandedRolePaths] = useState(new Set());
  const [showCubeDropdown, setShowCubeDropdown] = useState(false);
  const [cubeSearch, setCubeSearch] = useState('');
  const [metricsChipsExpanded, setMetricsChipsExpanded] = useState(false);
  const [dimensionsChipsExpanded, setDimensionsChipsExpanded] = useState(false);
  const [filterInput, setFilterInput] = useState('');
  // Highlights the Filters section while a dimension is dragged over it.
  const [filterDropActive, setFilterDropActive] = useState(false);
  const [split1, setSplit1] = useState(35); // metrics / dims boundary (%)
  const [split2, setSplit2] = useState(65); // dims / filters boundary (%)
  const [split3, setSplit3] = useState(85); // filters / engine+run boundary (%)
  const prevSearchRef = useRef('');
  const cubeDropdownRef = useRef(null);
  const metricsSearchRef = useRef(null);
  const dimensionsSearchRef = useRef(null);
  const filterInputRef = useRef(null);
  const sectionsRef = useRef(null);
  const dragRef = useRef(null);
  const splitRef = useRef({ split1: 35, split2: 65, split3: 85 });
  useEffect(() => {
    splitRef.current = { split1, split2, split3 };
  }, [split1, split2, split3]);

  // Threshold for showing expand/collapse button
  const CHIPS_COLLAPSE_THRESHOLD = 8;

  // Find the loaded cube object from the name
  const loadedCube = useMemo(() => {
    if (!loadedCubeName) return null;
    return (
      cubes.find(c => c.name === loadedCubeName) || { name: loadedCubeName }
    );
  }, [loadedCubeName, cubes]);

  // Close cube dropdown when clicking outside
  useEffect(() => {
    const handleClickOutside = event => {
      if (
        cubeDropdownRef.current &&
        !cubeDropdownRef.current.contains(event.target)
      ) {
        setShowCubeDropdown(false);
      }
    };
    document.addEventListener('mousedown', handleClickOutside);
    return () => document.removeEventListener('mousedown', handleClickOutside);
  }, []);

  // Filter cubes by search (GraphQL returns display_name)
  const filteredCubes = useMemo(() => {
    const search = cubeSearch.toLowerCase().trim();
    if (!search) return cubes;
    return cubes.filter(cube => {
      const name = cube.name || '';
      const displayName = cube.display_name || '';
      return (
        name.toLowerCase().includes(search) ||
        displayName.toLowerCase().includes(search)
      );
    });
  }, [cubes, cubeSearch]);

  // Get short name from full metric name
  const getShortName = fullName => {
    const parts = fullName.split('.');
    return parts[parts.length - 1];
  };

  // Get namespace from full metric name
  const getNamespace = fullName => {
    const parts = fullName.split('.');
    return parts.length > 1 ? parts.slice(0, -1).join('.') : 'default';
  };

  // Group metrics by namespace (e.g., "default.cube" -> "default")
  const groupedMetrics = useMemo(() => {
    const groups = {};
    metrics.forEach(metric => {
      const namespace = getNamespace(metric);
      if (!groups[namespace]) {
        groups[namespace] = [];
      }
      groups[namespace].push(metric);
    });
    return groups;
  }, [metrics]);

  // Filter and sort namespaces/metrics by search relevance
  const { filteredGroups, sortedNamespaces } = useMemo(() => {
    const search = metricsSearch.trim().toLowerCase();

    if (!search) {
      const namespaces = Object.keys(groupedMetrics).sort();
      return { filteredGroups: groupedMetrics, sortedNamespaces: namespaces };
    }

    const filtered = {};
    Object.entries(groupedMetrics).forEach(([namespace, items]) => {
      const matchingItems = items.filter(m => m.toLowerCase().includes(search));
      if (matchingItems.length > 0) {
        matchingItems.sort((a, b) => {
          const aShort = getShortName(a).toLowerCase();
          const bShort = getShortName(b).toLowerCase();
          const aPrefix = aShort.startsWith(search);
          const bPrefix = bShort.startsWith(search);
          if (aPrefix && !bPrefix) return -1;
          if (!aPrefix && bPrefix) return 1;
          return aShort.localeCompare(bShort);
        });
        filtered[namespace] = matchingItems;
      }
    });

    const namespaces = Object.keys(filtered).sort((a, b) => {
      const aLower = a.toLowerCase();
      const bLower = b.toLowerCase();
      const aPrefix = aLower.startsWith(search);
      const bPrefix = bLower.startsWith(search);
      if (aPrefix && !bPrefix) return -1;
      if (!aPrefix && bPrefix) return 1;
      const aContains = aLower.includes(search);
      const bContains = bLower.includes(search);
      if (aContains && !bContains) return -1;
      if (!aContains && bContains) return 1;
      const aCount = filtered[a].length;
      const bCount = filtered[b].length;
      if (aCount !== bCount) return bCount - aCount;
      return aLower.localeCompare(bLower);
    });

    return { filteredGroups: filtered, sortedNamespaces: namespaces };
  }, [groupedMetrics, metricsSearch]);

  // Auto-expand all matching namespaces when search changes
  useEffect(() => {
    const currentSearch = metricsSearch.trim();
    const prevSearch = prevSearchRef.current;
    if (currentSearch && currentSearch !== prevSearch) {
      setExpandedNamespaces(new Set(sortedNamespaces));
    }
    prevSearchRef.current = currentSearch;
  }, [metricsSearch, sortedNamespaces]);

  // Dedupe dimensions by name, keeping shortest path per name
  const dedupedDimensions = useMemo(() => {
    const byName = new Map();
    dimensions.forEach(d => {
      if (!d.name) return;
      const existing = byName.get(d.name);
      if (
        !existing ||
        (d.path?.length || 0) < (existing.path?.length || Infinity)
      ) {
        byName.set(d.name, d);
      }
    });
    return Array.from(byName.values());
  }, [dimensions]);

  // Extract role path from a dimension name, e.g. "foo.bar[a->b->c]" → "a->b->c" (or null)
  const getRolePath = name => {
    const match = name.match(/\[([^\]]+)\]$/);
    return match ? match[1] : null;
  };

  // Format a role path for display: "a->b->c" → "via a → b → c"
  const formatRolePath = roleKey =>
    roleKey ? 'via ' + roleKey.replace(/->/g, ' → ') : 'direct';

  // Count hops in a role path string
  const rolePathHops = roleKey => (roleKey ? roleKey.split('->').length : 0);

  // Group dimensions by node, then by role path within each node
  const groupedDimensions = useMemo(() => {
    const search = dimensionsSearch.trim().toLowerCase();
    const nodeMap = new Map();

    dedupedDimensions.forEach(d => {
      if (!d.name) return;
      const nodeKey =
        d.path?.length > 0
          ? d.path[d.path.length - 1]
          : d.name.split('.').slice(0, -1).join('.');
      const distance = Math.max(0, d.path ? d.path.length - 1 : 0);
      const roleKey = getRolePath(d.name); // e.g. "title_deal_window->title" or null

      if (!nodeMap.has(nodeKey)) {
        nodeMap.set(nodeKey, {
          nodeKey,
          minDistance: distance,
          rolePathMap: new Map(),
        });
      }
      const node = nodeMap.get(nodeKey);
      node.minDistance = Math.min(node.minDistance, distance);

      if (!node.rolePathMap.has(roleKey)) {
        node.rolePathMap.set(roleKey, { roleKey, dimensions: [] });
      }
      node.rolePathMap.get(roleKey).dimensions.push(d);
    });

    let groupsArray = Array.from(nodeMap.values()).map(node => {
      // Sort role paths: direct (null) first, then by hop count, then alphabetically
      const rolePaths = Array.from(node.rolePathMap.values()).sort(
        (a, b) =>
          rolePathHops(a.roleKey) - rolePathHops(b.roleKey) ||
          (a.roleKey || '').localeCompare(b.roleKey || ''),
      );
      // Sort dims within each role path alphabetically
      rolePaths.forEach(rp => {
        rp.dimensions.sort((a, b) => a.name.localeCompare(b.name));
      });
      return {
        nodeKey: node.nodeKey,
        minDistance: node.minDistance,
        rolePaths,
        totalCount: rolePaths.reduce((n, rp) => n + rp.dimensions.length, 0),
      };
    });

    // Apply search: filter within role paths, prune empty role paths and nodes
    if (search) {
      groupsArray = groupsArray
        .map(group => ({
          ...group,
          rolePaths: group.rolePaths
            .map(rp => ({
              ...rp,
              dimensions: rp.dimensions.filter(d => {
                const lower = d.name.toLowerCase();
                return (
                  lower.includes(search) ||
                  d.name.split('.').pop().toLowerCase().includes(search)
                );
              }),
            }))
            .filter(
              rp =>
                rp.dimensions.length > 0 ||
                (rp.roleKey || '').toLowerCase().includes(search),
            ),
        }))
        .filter(
          group =>
            group.rolePaths.length > 0 ||
            group.nodeKey.toLowerCase().includes(search),
        )
        .map(group => ({
          ...group,
          totalCount: group.rolePaths.reduce(
            (n, rp) => n + rp.dimensions.length,
            0,
          ),
        }));
    }

    // Sort node groups by distance, then alphabetically
    groupsArray.sort(
      (a, b) =>
        a.minDistance - b.minDistance || a.nodeKey.localeCompare(b.nodeKey),
    );

    return groupsArray;
  }, [dedupedDimensions, dimensionsSearch]);

  // Auto-expand on first load and when searching
  useEffect(() => {
    if (groupedDimensions.length === 0) return;
    if (dimensionsSearch.trim()) {
      // Expand everything with matches
      setExpandedDimGroups(new Set(groupedDimensions.map(g => g.nodeKey)));
      setExpandedRolePaths(
        new Set(
          groupedDimensions.flatMap(g =>
            g.rolePaths.map(rp => `${g.nodeKey}::${rp.roleKey}`),
          ),
        ),
      );
    } else {
      // On first load: expand distance-0 node groups and their direct (null) role paths
      setExpandedDimGroups(prev => {
        if (prev.size > 0) return prev;
        return new Set(
          groupedDimensions
            .filter(g => g.minDistance === 0)
            .map(g => g.nodeKey),
        );
      });
      setExpandedRolePaths(prev => {
        if (prev.size > 0) return prev;
        return new Set(
          groupedDimensions
            .filter(g => g.minDistance === 0)
            .map(g => `${g.nodeKey}::null`),
        );
      });
    }
  }, [groupedDimensions, dimensionsSearch]);

  // Get display name for dimension (last 2 segments)
  const getDimDisplayName = fullName => {
    const parts = (fullName || '').split('.');
    return parts.slice(-2).join('.');
  };

  const toggleNamespace = namespace => {
    setExpandedNamespaces(prev => {
      const next = new Set(prev);
      if (next.has(namespace)) {
        next.delete(namespace);
      } else {
        next.add(namespace);
      }
      return next;
    });
  };

  const toggleDimGroup = nodeKey => {
    setExpandedDimGroups(prev => {
      const next = new Set(prev);
      if (next.has(nodeKey)) {
        next.delete(nodeKey);
      } else {
        next.add(nodeKey);
      }
      return next;
    });
  };

  const toggleRolePath = (nodeKey, roleKey) => {
    const key = `${nodeKey}::${roleKey}`;
    setExpandedRolePaths(prev => {
      const next = new Set(prev);
      if (next.has(key)) {
        next.delete(key);
      } else {
        next.add(key);
      }
      return next;
    });
  };

  const getDimGroupShortName = nodeKey => nodeKey.split('.').pop();

  const handleDividerMouseDown = useCallback((e, divider) => {
    e.preventDefault();
    const startSplit =
      divider === 1
        ? splitRef.current.split1
        : divider === 2
        ? splitRef.current.split2
        : splitRef.current.split3;
    dragRef.current = { divider, startY: e.clientY, startSplit };
  }, []);

  useEffect(() => {
    const onMouseMove = e => {
      if (!dragRef.current || !sectionsRef.current) return;
      const height = sectionsRef.current.getBoundingClientRect().height;
      const deltaPct = ((e.clientY - dragRef.current.startY) / height) * 100;
      const { divider, startSplit } = dragRef.current;
      const { split1, split2, split3 } = splitRef.current;
      if (divider === 1) {
        setSplit1(Math.max(10, Math.min(split2 - 15, startSplit + deltaPct)));
      } else if (divider === 2) {
        setSplit2(
          Math.max(split1 + 15, Math.min(split3 - 10, startSplit + deltaPct)),
        );
      } else {
        setSplit3(Math.max(split2 + 10, Math.min(95, startSplit + deltaPct)));
      }
    };
    const onMouseUp = () => {
      dragRef.current = null;
    };
    window.addEventListener('mousemove', onMouseMove);
    window.addEventListener('mouseup', onMouseUp);
    return () => {
      window.removeEventListener('mousemove', onMouseMove);
      window.removeEventListener('mouseup', onMouseUp);
    };
  }, []);

  const toggleMetric = metric => {
    if (selectedMetrics.includes(metric)) {
      onMetricsChange(selectedMetrics.filter(m => m !== metric));
    } else {
      onMetricsChange([...selectedMetrics, metric]);
    }
  };

  const removeMetric = metric => {
    onMetricsChange(selectedMetrics.filter(m => m !== metric));
  };

  const toggleDimension = dimName => {
    if (selectedDimensions.includes(dimName)) {
      onDimensionsChange(selectedDimensions.filter(d => d !== dimName));
    } else {
      onDimensionsChange([...selectedDimensions, dimName]);
    }
  };

  const removeDimension = dimName => {
    onDimensionsChange(selectedDimensions.filter(d => d !== dimName));
  };

  const selectAllInNamespace = (namespace, items) => {
    const newSelection = [...new Set([...selectedMetrics, ...items])];
    onMetricsChange(newSelection);
  };

  const deselectAllInNamespace = (namespace, items) => {
    onMetricsChange(selectedMetrics.filter(m => !items.includes(m)));
  };

  const handleCubeSelect = cube => {
    if (onLoadCubePreset) {
      onLoadCubePreset(cube.name);
    }
    // loadedCubeName is now managed by parent via onLoadCubePreset
    setShowCubeDropdown(false);
    setCubeSearch('');
  };

  const clearSelection = () => {
    if (onClearSelection) {
      onClearSelection(); // Parent handles clearing metrics, dimensions, and cube
    } else {
      onMetricsChange([]);
      onDimensionsChange([]);
    }
  };

  const handleAddFilter = () => {
    const trimmed = filterInput.trim();
    if (trimmed && !filters.includes(trimmed) && onFiltersChange) {
      onFiltersChange([...filters, trimmed]);
      setFilterInput('');
    }
  };

  const handleFilterKeyDown = e => {
    if (e.key === 'Enter') {
      e.preventDefault();
      handleAddFilter();
    }
  };

  const handleRemoveFilter = filterToRemove => {
    if (onFiltersChange) {
      onFiltersChange(filters.filter(f => f !== filterToRemove));
    }
  };

  // Click a filter chip to edit it: pull it back into the input (preserving any
  // in-progress text by ANDing onto it) and remove the chip.
  const handleEditFilter = filter => {
    setFilterInput(prev => andAppend(prev, filter));
    if (onFiltersChange) {
      onFiltersChange(filters.filter(f => f !== filter));
    }
    filterInputRef.current?.focus();
  };

  // Append a dimension to the filter input (ANDing onto any existing expression)
  // then focus so the user can type the operator/value. Functional update keeps
  // it correct whether called from a click or a drag-and-drop handler.
  const appendDimToFilterInput = dimName => {
    setFilterInput(prev => andAppend(prev, dimName + ' '));
    filterInputRef.current?.focus();
  };

  const addDimAsFilter = (e, dimName) => {
    e.preventDefault();
    e.stopPropagation();
    appendDimToFilterInput(dimName);
  };

  // Drag a dimension chip/row onto the Filters section to start filtering on it.
  const handleDimDragStart = (e, dimName) => {
    e.dataTransfer.setData(DIM_DRAG_MIME, dimName);
    // Plain-text fallback so the drag has a sensible payload everywhere.
    e.dataTransfer.setData('text/plain', dimName);
    e.dataTransfer.effectAllowed = 'copy';
  };

  const handleFilterDragOver = e => {
    if (e.dataTransfer.types.includes(DIM_DRAG_MIME)) {
      e.preventDefault(); // allow drop
      e.dataTransfer.dropEffect = 'copy';
      if (!filterDropActive) setFilterDropActive(true);
    }
  };

  const handleFilterDragLeave = e => {
    // Ignore leaves into child elements of the filters section.
    if (!e.currentTarget.contains(e.relatedTarget)) {
      setFilterDropActive(false);
    }
  };

  const handleFilterDrop = e => {
    const dimName = e.dataTransfer.getData(DIM_DRAG_MIME);
    if (dimName) {
      e.preventDefault();
      appendDimToFilterInput(dimName);
    }
    setFilterDropActive(false);
  };

  return (
    <div className="selection-panel">
      {/* Cube Preset Dropdown */}
      {cubes.length > 0 && (
        <div className="cube-preset-section" ref={cubeDropdownRef}>
          <div className="preset-row">
            <button
              className={`preset-button ${loadedCube ? 'has-preset' : ''}`}
              onClick={() => setShowCubeDropdown(!showCubeDropdown)}
            >
              <span className="preset-icon">{loadedCube ? '📦' : '📂'}</span>
              <span className="preset-label">
                {loadedCube
                  ? loadedCube.display_name ||
                    (loadedCube.name
                      ? loadedCube.name.split('.').pop()
                      : 'Cube')
                  : 'Load from Cube'}
              </span>
              <span className="dropdown-arrow">
                {showCubeDropdown ? '▲' : '▼'}
              </span>
            </button>
            {(selectedMetrics.length > 0 || selectedDimensions.length > 0) && (
              <button className="clear-all-btn" onClick={clearSelection}>
                Clear
              </button>
            )}
          </div>

          {showCubeDropdown && (
            <div className="cube-dropdown">
              <input
                type="text"
                className="cube-search"
                placeholder="Search cubes..."
                value={cubeSearch}
                onChange={e => setCubeSearch(e.target.value)}
                autoFocus
              />
              <div className="cube-list">
                {filteredCubes.length === 0 ? (
                  <div className="cube-empty">
                    {cubeSearch
                      ? 'No cubes match your search'
                      : 'No cubes available'}
                  </div>
                ) : (
                  filteredCubes.map(cube => (
                    <button
                      key={cube.name}
                      className={`cube-option ${
                        loadedCubeName === cube.name ? 'selected' : ''
                      }`}
                      onClick={() => handleCubeSelect(cube)}
                    >
                      <span className="cube-name">
                        {cube.display_name ||
                          (cube.name ? cube.name.split('.').pop() : 'Unknown')}
                      </span>
                      <span className="cube-info">{cube.name}</span>
                      {loadedCubeName === cube.name && (
                        <span className="cube-selected-icon">✓</span>
                      )}
                    </button>
                  ))
                )}
              </div>
            </div>
          )}
        </div>
      )}

      {/* Resizable sections */}
      <div className="resizable-sections" ref={sectionsRef}>
        {/* Metrics Section */}
        <div className="selection-section" style={{ flex: split1 }}>
          <div className="section-header">
            <h3>Metrics</h3>
            <span className="selection-count">
              {selectedMetrics.length} selected
            </span>
          </div>

          {/* Combined Chips + Search Input */}
          <div
            className="combobox-input"
            onClick={() => metricsSearchRef.current?.focus()}
          >
            {selectedMetrics.length > 0 && (
              <div
                className={`combobox-chips ${
                  selectedMetrics.length > CHIPS_COLLAPSE_THRESHOLD
                    ? metricsChipsExpanded
                      ? 'expanded'
                      : 'collapsed'
                    : ''
                }`}
              >
                {selectedMetrics.map(metric => (
                  <span key={metric} className="selected-chip metric-chip">
                    <TailLabel text={getShortName(metric)} />
                    <button
                      className="chip-remove"
                      onClick={e => {
                        e.stopPropagation();
                        removeMetric(metric);
                      }}
                      title={`Remove ${getShortName(metric)}`}
                    >
                      ×
                    </button>
                  </span>
                ))}
              </div>
            )}
            <div className="combobox-input-row">
              <input
                ref={metricsSearchRef}
                type="text"
                className="combobox-search"
                placeholder="Search metrics..."
                value={metricsSearch}
                onChange={e => setMetricsSearch(e.target.value)}
                onClick={e => e.stopPropagation()}
              />
              {selectedMetrics.length > CHIPS_COLLAPSE_THRESHOLD && (
                <button
                  className="combobox-action"
                  onClick={e => {
                    e.stopPropagation();
                    setMetricsChipsExpanded(!metricsChipsExpanded);
                  }}
                >
                  {metricsChipsExpanded ? 'Show less' : 'Show all'}
                </button>
              )}
              {selectedMetrics.length > 0 && (
                <button
                  className="combobox-action"
                  onClick={e => {
                    e.stopPropagation();
                    onMetricsChange([]);
                  }}
                >
                  Clear
                </button>
              )}
            </div>
          </div>

          <div className="selection-list">
            {sortedNamespaces.map(namespace => {
              const items = filteredGroups[namespace];
              const isExpanded = expandedNamespaces.has(namespace);
              const selectedInNamespace = items.filter(m =>
                selectedMetrics.includes(m),
              ).length;

              return (
                <div key={namespace} className="namespace-group">
                  <div
                    className="namespace-header"
                    onClick={() => toggleNamespace(namespace)}
                  >
                    <span className="expand-icon">
                      {isExpanded ? '▼' : '▶'}
                    </span>
                    <span className="namespace-name">{namespace}</span>
                    <span className="namespace-count">
                      {selectedInNamespace > 0 && (
                        <span className="selected-badge">
                          {selectedInNamespace}
                        </span>
                      )}
                      {items.length}
                    </span>
                  </div>

                  {isExpanded && (
                    <div className="namespace-items">
                      <div className="namespace-actions">
                        <button
                          type="button"
                          className="select-all-btn"
                          onClick={() => selectAllInNamespace(namespace, items)}
                        >
                          Select all
                        </button>
                        <button
                          type="button"
                          className="select-all-btn"
                          onClick={() =>
                            deselectAllInNamespace(namespace, items)
                          }
                        >
                          Clear
                        </button>
                      </div>
                      {items.map(metric => {
                        const isIncompatible =
                          compatibleMetrics !== null &&
                          !compatibleMetrics.has(metric) &&
                          !selectedMetrics.includes(metric);
                        return (
                          <label
                            key={metric}
                            className={`selection-item${
                              isIncompatible ? ' metric-incompatible' : ''
                            }`}
                            title={
                              isIncompatible
                                ? 'Not compatible with selected dimensions'
                                : metric
                            }
                          >
                            <input
                              type="checkbox"
                              checked={selectedMetrics.includes(metric)}
                              onChange={() => toggleMetric(metric)}
                            />
                            <span className="item-name">
                              {getShortName(metric)}
                            </span>
                            {compatibleMetrics !== null &&
                              compatibleMetrics.has(metric) &&
                              !selectedMetrics.includes(metric) && (
                                <span
                                  className="metric-compatible-badge"
                                  title="Compatible with selected dimensions"
                                >
                                  ✓
                                </span>
                              )}
                          </label>
                        );
                      })}
                    </div>
                  )}
                </div>
              );
            })}

            {sortedNamespaces.length === 0 && (
              <div className="empty-list">
                {metricsSearch
                  ? 'No metrics match your search'
                  : 'No metrics available'}
              </div>
            )}
          </div>
        </div>

        {/* Draggable Divider 1: metrics / dims */}
        <div
          className="section-divider draggable-divider"
          onMouseDown={e => handleDividerMouseDown(e, 1)}
        />

        {/* Dimensions Section */}
        <div className="selection-section" style={{ flex: split2 - split1 }}>
          <div className="section-header">
            <h3>Dimensions</h3>
            <span className="selection-count">
              {selectedDimensions.length} selected
              {dimensions.length > 0 && ` / ${dimensions.length} available`}
            </span>
          </div>

          {selectedMetrics.length === 0 ? (
            <div className="empty-list hint">
              Select metrics to see available dimensions
            </div>
          ) : loading ? (
            <div className="empty-list">Loading dimensions...</div>
          ) : (
            <>
              {/* Combined Chips + Search Input */}
              <div
                className="combobox-input"
                onClick={() => dimensionsSearchRef.current?.focus()}
              >
                {selectedDimensions.length > 0 && (
                  <div
                    className={`combobox-chips ${
                      selectedDimensions.length > CHIPS_COLLAPSE_THRESHOLD
                        ? dimensionsChipsExpanded
                          ? 'expanded'
                          : 'collapsed'
                        : ''
                    }`}
                  >
                    {selectedDimensions.map(dimName => (
                      <span
                        key={dimName}
                        className="selected-chip dimension-chip draggable-dim"
                        title={`${dimName} — drag to Filters to filter on it`}
                        draggable
                        onDragStart={e => handleDimDragStart(e, dimName)}
                      >
                        <TailLabel text={getDimDisplayName(dimName)} />
                        <button
                          className="chip-remove"
                          onClick={e => {
                            e.stopPropagation();
                            removeDimension(dimName);
                          }}
                          title={`Remove ${getDimDisplayName(dimName)}`}
                        >
                          ×
                        </button>
                      </span>
                    ))}
                  </div>
                )}
                <div className="combobox-input-row">
                  <input
                    ref={dimensionsSearchRef}
                    type="text"
                    className="combobox-search"
                    placeholder="Search dimensions..."
                    value={dimensionsSearch}
                    onChange={e => setDimensionsSearch(e.target.value)}
                    onClick={e => e.stopPropagation()}
                  />
                  {selectedDimensions.length > CHIPS_COLLAPSE_THRESHOLD && (
                    <button
                      className="combobox-action"
                      onClick={e => {
                        e.stopPropagation();
                        setDimensionsChipsExpanded(!dimensionsChipsExpanded);
                      }}
                    >
                      {dimensionsChipsExpanded ? 'Show less' : 'Show all'}
                    </button>
                  )}
                  {selectedDimensions.length > 0 && (
                    <button
                      className="combobox-action"
                      onClick={e => {
                        e.stopPropagation();
                        onDimensionsChange([]);
                      }}
                    >
                      Clear
                    </button>
                  )}
                </div>
              </div>

              <div className="selection-list dimensions-list">
                {groupedDimensions.map(group => {
                  const nodeExpanded = expandedDimGroups.has(group.nodeKey);
                  return (
                    <div key={group.nodeKey} className="dim-group">
                      <div
                        className="dim-group-header"
                        onClick={() => toggleDimGroup(group.nodeKey)}
                        title={group.nodeKey}
                      >
                        <span className="expand-icon">
                          {nodeExpanded ? '▼' : '▶'}
                        </span>
                        <span className="dim-group-name">
                          {getDimGroupShortName(group.nodeKey)}
                        </span>
                        <span className="dim-group-count">
                          {group.totalCount}
                        </span>
                      </div>
                      {nodeExpanded &&
                        group.rolePaths.map(rp => {
                          const rpKey = `${group.nodeKey}::${rp.roleKey}`;
                          const rpExpanded = expandedRolePaths.has(rpKey);
                          const hops = rolePathHops(rp.roleKey);
                          const hopsLabel =
                            hops === 0
                              ? 'direct'
                              : `${hops} hop${hops > 1 ? 's' : ''}`;
                          return (
                            <div key={rpKey} className="dim-role-group">
                              <div
                                className="dim-role-header"
                                onClick={() =>
                                  toggleRolePath(group.nodeKey, rp.roleKey)
                                }
                              >
                                <span className="expand-icon">
                                  {rpExpanded ? '▼' : '▶'}
                                </span>
                                <span className="dim-role-label">
                                  {formatRolePath(rp.roleKey)}
                                </span>
                                <span className="dim-group-meta">
                                  {hopsLabel}
                                </span>
                                <span className="dim-group-count">
                                  {rp.dimensions.length}
                                </span>
                              </div>
                              {rpExpanded &&
                                rp.dimensions.map(dim => (
                                  <label
                                    key={dim.name}
                                    className="selection-item dimension-item dim-role-item draggable-dim"
                                    title={`${dim.name} — drag to Filters to filter on it`}
                                    draggable
                                    onDragStart={e =>
                                      handleDimDragStart(e, dim.name)
                                    }
                                  >
                                    <input
                                      type="checkbox"
                                      checked={selectedDimensions.includes(
                                        dim.name,
                                      )}
                                      onChange={() => toggleDimension(dim.name)}
                                    />
                                    <div className="dimension-info">
                                      <span className="item-name">
                                        {getDimDisplayName(
                                          dim.name.replace(/\[[^\]]*\]$/, ''),
                                        )}
                                      </span>
                                    </div>
                                    <button
                                      className="dim-filter-btn"
                                      title={`Add "${dim.name}" to filters`}
                                      onClick={e => addDimAsFilter(e, dim.name)}
                                    >
                                      + filter
                                    </button>
                                  </label>
                                ))}
                            </div>
                          );
                        })}
                    </div>
                  );
                })}

                {groupedDimensions.length === 0 && (
                  <div className="empty-list">
                    {dimensionsSearch
                      ? 'No dimensions match your search'
                      : 'No shared dimensions'}
                  </div>
                )}
              </div>
            </>
          )}
        </div>

        {/* Draggable Divider 2: dims / filters */}
        <div
          className="section-divider draggable-divider"
          onMouseDown={e => handleDividerMouseDown(e, 2)}
        />

        {/* Filters Section (drop target for dragged dimensions) */}
        <div
          className={`selection-section filters-section${
            filterDropActive ? ' drop-active' : ''
          }`}
          style={{ flex: split3 - split2 }}
          onDragOver={handleFilterDragOver}
          onDragLeave={handleFilterDragLeave}
          onDrop={handleFilterDrop}
        >
          <div className="section-header">
            <h3>Filters</h3>
            <span className="selection-count">{filters.length} applied</span>
          </div>
          {filterDropActive && (
            <div className="filter-drop-hint">
              Drop to filter on this dimension
            </div>
          )}

          {/* Filter chips */}
          {filters.length > 0 && (
            <div className="filter-chips-container">
              {filters.map((filter, idx) => (
                <span key={idx} className="filter-chip">
                  <button
                    type="button"
                    className="filter-chip-text filter-chip-edit"
                    onClick={() => handleEditFilter(filter)}
                    title={`${filter} — click to edit`}
                  >
                    {filter}
                  </button>
                  <button
                    className="filter-chip-remove"
                    onClick={() => handleRemoveFilter(filter)}
                    title="Remove filter"
                  >
                    ×
                  </button>
                </span>
              ))}
            </div>
          )}

          {/* Filter input */}
          <div className="filter-input-container">
            <input
              ref={filterInputRef}
              type="text"
              className="filter-input"
              placeholder="e.g. v3.date.date_id >= '2024-01-01'"
              value={filterInput}
              onChange={e => setFilterInput(e.target.value)}
              onKeyDown={handleFilterKeyDown}
            />
            <button
              className="filter-add-btn"
              onClick={handleAddFilter}
              disabled={!filterInput.trim()}
            >
              Add
            </button>
          </div>
        </div>

        {/* Draggable Divider 3: filters / engine+run */}
        <div
          className="section-divider draggable-divider"
          onMouseDown={e => handleDividerMouseDown(e, 3)}
        />

        {/* Engine + Run Query Section */}
        <div
          className="selection-section engine-run-section"
          style={{ flex: 100 - split3 }}
        >
          {/* Engine Selection */}
          <div className="engine-section">
            <span className="engine-label">Engine</span>
            <div className="engine-pills">
              {ENGINE_OPTIONS.map(({ value, label }) => (
                <button
                  key={label}
                  className={`engine-pill${
                    selectedEngine === value ? ' active' : ''
                  }`}
                  onClick={() => onEngineChange && onEngineChange(value)}
                >
                  {label}
                </button>
              ))}
            </div>
          </div>

          {/* Run Query Section */}
          <div className="run-query-section">
            <button
              className="run-query-btn"
              onClick={onRunQuery}
              disabled={!canRunQuery || queryLoading}
            >
              {queryLoading ? (
                <>
                  <span className="spinner small" />
                  Running...
                </>
              ) : (
                <>
                  <span className="run-icon">▶</span>
                  Run Query
                </>
              )}
            </button>
            {!canRunQuery && (
              <span className="run-hint">
                Select at least one metric to run a query
              </span>
            )}
          </div>
        </div>
      </div>
      {/* end resizable-sections */}
    </div>
  );
}

export default SelectionPanel;
