import {
  render,
  screen,
  fireEvent,
  waitFor,
  act,
} from '@testing-library/react';
import DJClientContext from '../../../providers/djclient';
import { QueryPlannerPage } from '../index';
import { MemoryRouter, Routes, Route, useLocation } from 'react-router-dom';
import React from 'react';

// Mock the MetricFlowGraph component to avoid dagre dependency issues
vi.mock('../MetricFlowGraph', () => ({
  __esModule: true,
  default: ({ grainGroups, metricFormulas, selectedNode, onNodeSelect }) => {
    if (!grainGroups?.length || !metricFormulas?.length) {
      return <div data-testid="graph-empty">Select metrics and dimensions</div>;
    }
    return (
      <div data-testid="metric-flow-graph">
        <span className="graph-stats">
          {grainGroups.length} pre-aggregations → {metricFormulas.length}{' '}
          metrics
        </span>
        <button
          data-testid="select-preagg"
          onClick={() =>
            onNodeSelect?.({
              type: 'preagg',
              index: 0,
              data: grainGroups[0],
            })
          }
        >
          Select Pre-agg
        </button>
        <button
          data-testid="select-metric"
          onClick={() =>
            onNodeSelect?.({
              type: 'metric',
              index: 0,
              data: metricFormulas[0],
            })
          }
        >
          Select Metric
        </button>
      </div>
    );
  },
}));

const mockDjClient = {
  metrics: vi.fn(),
  commonDimensions: vi.fn(),
  commonMetrics: vi.fn(),
  measuresV3: vi.fn(),
  metricsV3: vi.fn(),
  listCubesForPreset: vi.fn(),
  cubeForPlanner: vi.fn(),
  planPreaggs: vi.fn(),
  updatePreaggConfig: vi.fn(),
  materializePreagg: vi.fn(),
  runPreaggBackfill: vi.fn(),
  deactivatePreaggWorkflow: vi.fn(),
  deactivateCubeWorkflow: vi.fn(),
  createCube: vi.fn(),
  materializeCubeV2: vi.fn(),
  refreshCubeWorkflow: vi.fn(),
  runCubeBackfill: vi.fn(),
  listPreaggs: vi.fn(),
  getNodeColumnsWithPartitions: vi.fn(),
  setPartition: vi.fn(),
};

const mockMetrics = [
  'default.num_repair_orders',
  'default.avg_repair_price',
  'default.total_repair_cost',
  'sales.revenue',
  'sales.order_count',
];

const mockCommonDimensions = [
  {
    name: 'default.date_dim.dateint',
    type: 'timestamp',
    node_name: 'default.date_dim',
    node_display_name: 'Date',
    properties: [],
    path: ['default.date_dim'],
  },
  {
    name: 'default.date_dim.month',
    type: 'int',
    node_name: 'default.date_dim',
    node_display_name: 'Date',
    properties: [],
    path: ['default.date_dim'],
  },
  {
    name: 'default.hard_hat.country',
    type: 'string',
    node_name: 'default.hard_hat',
    node_display_name: 'Hard Hat',
    properties: [],
    path: ['default.hard_hat'],
  },
];

const mockMeasuresResult = {
  grain_groups: [
    {
      parent_name: 'default.repair_orders',
      aggregability: 'FULL',
      grain: ['date_id', 'customer_id'],
      components: [
        {
          name: 'sum_revenue',
          expression: 'SUM(revenue)',
          aggregation: 'SUM',
          merge: 'SUM',
        },
        {
          name: 'count_orders',
          expression: 'COUNT(*)',
          aggregation: 'COUNT',
          merge: 'SUM',
        },
      ],
      sql: 'SELECT date_id, customer_id, SUM(revenue) FROM orders GROUP BY 1, 2',
    },
  ],
  metric_formulas: [
    {
      name: 'default.num_repair_orders',
      short_name: 'num_repair_orders',
      combiner: 'SUM(count_orders)',
      is_derived: false,
      components: ['count_orders'],
    },
    {
      name: 'default.avg_repair_price',
      short_name: 'avg_repair_price',
      combiner: 'SUM(sum_revenue) / SUM(count_orders)',
      is_derived: true,
      components: ['sum_revenue', 'count_orders'],
    },
  ],
  requested_dimensions: ['default.date_dim.dateint'],
};

const mockMetricsResult = {
  sql: 'SELECT date_id, SUM(revenue) as total_revenue FROM orders GROUP BY 1',
};

const mockCubes = [
  { name: 'default.test_cube', display_name: 'Test Cube' },
  { name: 'sales.revenue_cube', display_name: 'Revenue Cube' },
];

const mockCubeData = {
  cube_node_metrics: ['default.num_repair_orders', 'default.avg_repair_price'],
  cube_node_dimensions: ['default.date_dim.dateint'],
  cube_dimension_objects: [
    {
      name: 'default.date_dim.dateint',
      type: 'timestamp',
      attribute: 'dateint',
      role: '',
      properties: [],
    },
  ],
  cubeMaterialization: {
    schedule: '0 6 * * *',
    strategy: 'incremental_time',
    lookbackWindow: '1 DAY',
    workflowUrls: ['http://workflow.example.com/1'],
  },
};

const renderPage = (initialEntries = ['/planner']) => {
  return render(
    <MemoryRouter initialEntries={initialEntries}>
      <Routes>
        <Route
          path="/planner"
          element={
            <DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
              <QueryPlannerPage />
            </DJClientContext.Provider>
          }
        />
      </Routes>
    </MemoryRouter>,
  );
};

describe('QueryPlannerPage', () => {
  beforeEach(() => {
    mockDjClient.metrics.mockResolvedValue(mockMetrics);
    mockDjClient.commonDimensions.mockResolvedValue(mockCommonDimensions);
    mockDjClient.commonMetrics.mockResolvedValue([]);
    mockDjClient.measuresV3.mockResolvedValue(mockMeasuresResult);
    mockDjClient.metricsV3.mockResolvedValue(mockMetricsResult);
    mockDjClient.listCubesForPreset.mockResolvedValue(mockCubes);
    mockDjClient.cubeForPlanner.mockResolvedValue(null);
    mockDjClient.listPreaggs.mockResolvedValue({ items: [] });
    mockDjClient.getNodeColumnsWithPartitions.mockResolvedValue({
      columns: [{ name: 'date_id', type: 'int' }],
      temporalPartitions: [],
    });
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  describe('Initial Render', () => {
    it('renders the page header', async () => {
      renderPage();
      // Page has "Query Planner" text in multiple places (header and empty state)
      await waitFor(() => {
        expect(screen.getAllByText('Query Planner').length).toBeGreaterThan(0);
      });
    });

    it('renders the metrics section', async () => {
      renderPage();
      await waitFor(() => {
        expect(screen.getByText('Metrics')).toBeInTheDocument();
      });
    });

    it('renders the dimensions section', async () => {
      renderPage();
      await waitFor(() => {
        expect(screen.getByText('Dimensions')).toBeInTheDocument();
      });
    });

    it('fetches metrics on mount', async () => {
      renderPage();
      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('fetches cube list on mount', async () => {
      renderPage();
      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('shows empty state when no metrics/dimensions selected', async () => {
      renderPage();
      await waitFor(() => {
        expect(
          screen.getByText('Select Metrics & Dimensions'),
        ).toBeInTheDocument();
      });
    });
  });

  describe('Metric Selection', () => {
    it('displays metrics grouped by namespace', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Check namespace headers are present
      expect(screen.getByText('default')).toBeInTheDocument();
      expect(screen.getByText('sales')).toBeInTheDocument();
    });

    it('expands namespace when clicked', async () => {
      renderPage();

      // Wait for the namespace label to actually render, not just for the
      // metrics() spy to be called — the spy fires before React commits.
      const defaultNamespace = await screen.findByText('default');
      fireEvent.click(defaultNamespace);

      // Metrics should now be visible
      await waitFor(() => {
        expect(screen.getByText('num_repair_orders')).toBeInTheDocument();
      });
    });

    it('fetches common dimensions when metrics are selected', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Expand and select a metric
      const defaultNamespace = screen.getByText('default');
      fireEvent.click(defaultNamespace);

      await waitFor(() => {
        const checkbox = screen.getByRole('checkbox', {
          name: /num_repair_orders/i,
        });
        fireEvent.click(checkbox);
      });

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });
    });
  });

  describe('Search Functionality', () => {
    it('filters metrics by search term', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      const searchInput = screen.getByPlaceholderText('Search metrics...');
      fireEvent.change(searchInput, { target: { value: 'repair' } });

      // Should auto-expand matching namespaces
      await waitFor(() => {
        expect(screen.getByText('num_repair_orders')).toBeInTheDocument();
      });
    });
  });

  describe('Cube Preset Loading', () => {
    it('displays cube dropdown button when cubes are available', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });

      expect(screen.getByText('Load from Cube')).toBeInTheDocument();
    });

    it('opens cube dropdown when clicked', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });

      const cubeButton = screen.getByText('Load from Cube');
      fireEvent.click(cubeButton);

      expect(
        screen.getByPlaceholderText('Search cubes...'),
      ).toBeInTheDocument();
    });

    it('loads cube data when a cube is selected', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue(mockCubeData);

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });

      // Open dropdown
      const cubeButton = screen.getByText('Load from Cube');
      fireEvent.click(cubeButton);

      // Select a cube
      const cubeOption = screen.getByText('Test Cube');
      fireEvent.click(cubeOption);

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalledWith(
          'default.test_cube',
        );
      });
    });
  });

  describe('URL Parameter Handling', () => {
    it('initializes from URL with metrics parameter', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue(mockCubeData);

      renderPage([
        '/planner?metrics=default.num_repair_orders,default.avg_repair_price',
      ]);

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });
    });

    it('initializes from URL with cube parameter', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue(mockCubeData);

      renderPage(['/planner?cube=default.test_cube']);

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalledWith(
          'default.test_cube',
        );
      });
    });

    it('selects cube dimensions immediately from the preset without waiting on common dimensions', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue(mockCubeData);
      // Simulate the (slow) common-dimensions intersection never resolving;
      // the cube's dimensions must still be shown and selected from the preset.
      mockDjClient.commonDimensions.mockReturnValue(new Promise(() => {}));

      renderPage(['/planner?cube=default.test_cube']);

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalledWith(
          'default.test_cube',
        );
      });

      // Dimensions section is populated and the cube's dimension is selected,
      // even though commonDimensions never resolved (no spinner, no empty list).
      await waitFor(() => {
        expect(
          screen.getByText(/1 selected \/ 1 available/),
        ).toBeInTheDocument();
      });
      expect(
        screen.queryByText('Loading dimensions...'),
      ).not.toBeInTheDocument();
    });
  });

  describe('Graph Interactions', () => {
    it('displays graph when metrics and dimensions are selected', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Expand and select metric
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      // Wait for dimensions and select one
      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      const dimCheckbox = screen.getByRole('checkbox', { name: /dateint/i });
      fireEvent.click(dimCheckbox);

      // Wait for measures to be fetched
      await waitFor(() => {
        expect(mockDjClient.measuresV3).toHaveBeenCalled();
      });

      // Graph should now be displayed
      await waitFor(() => {
        expect(screen.getByTestId('metric-flow-graph')).toBeInTheDocument();
      });
    });

    it('shows loading state while building data flow', async () => {
      // Delay the measures response
      mockDjClient.measuresV3.mockImplementation(
        () =>
          new Promise(resolve =>
            setTimeout(() => resolve(mockMeasuresResult), 100),
          ),
      );

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select metric and dimension quickly
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      fireEvent.click(screen.getByRole('checkbox', { name: /dateint/i }));

      // Should show loading state
      await waitFor(() => {
        expect(screen.getByText('Building data flow...')).toBeInTheDocument();
      });
    });

    it('shows pre-agg details when preagg node is selected', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select metric and dimension
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      fireEvent.click(screen.getByRole('checkbox', { name: /dateint/i }));

      // Wait for graph to appear
      await waitFor(() => {
        expect(screen.getByTestId('metric-flow-graph')).toBeInTheDocument();
      });

      // Click on preagg node
      fireEvent.click(screen.getByTestId('select-preagg'));

      // Should show pre-agg details
      await waitFor(() => {
        expect(screen.getByText('Pre-aggregation')).toBeInTheDocument();
      });
    });

    it('shows metric details when metric node is selected', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select metric and dimension
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      fireEvent.click(screen.getByRole('checkbox', { name: /dateint/i }));

      // Wait for graph to appear
      await waitFor(() => {
        expect(screen.getByTestId('metric-flow-graph')).toBeInTheDocument();
      });

      // Click on metric node
      fireEvent.click(screen.getByTestId('select-metric'));

      // Should show metric details (badge shows "Metric" for non-derived)
      await waitFor(() => {
        expect(screen.getByText('Metric')).toBeInTheDocument();
      });
    });
  });

  describe('Error Handling', () => {
    it('displays error when API call fails', async () => {
      mockDjClient.measuresV3.mockRejectedValue(new Error('API Error'));

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select metric and dimension
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      fireEvent.click(screen.getByRole('checkbox', { name: /dateint/i }));

      // Should show error
      await waitFor(() => {
        expect(screen.getByText('API Error')).toBeInTheDocument();
      });
    });

    it('handles commonDimensions API error gracefully', async () => {
      mockDjClient.commonDimensions.mockRejectedValue(
        new Error('Dimensions fetch failed'),
      );

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select a metric
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      // Should handle gracefully (empty dimensions)
      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });
    });
  });

  describe('Dimension Selection', () => {
    it('clears invalid dimension selections when dimensions change', async () => {
      // First commonDimensions call returns 2 dimensions
      mockDjClient.commonDimensions.mockResolvedValueOnce(mockCommonDimensions);
      // Second call returns only 1 dimension
      mockDjClient.commonDimensions.mockResolvedValueOnce([
        mockCommonDimensions[0],
      ]);

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select first metric
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      // Wait for dimensions
      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      // Select a dimension
      const dimCheckbox = screen.getByRole('checkbox', { name: /dateint/i });
      fireEvent.click(dimCheckbox);

      // Select another metric (triggers new commonDimensions call)
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /avg_repair_price/i }),
        );
      });

      // Invalid dimensions should be cleared automatically
      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalledTimes(2);
      });
    });
  });

  describe('Clear Selection', () => {
    it('clears all selections when Clear is clicked', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue(mockCubeData);

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });

      // Load a cube
      fireEvent.click(screen.getByText('Load from Cube'));
      fireEvent.click(screen.getByText('Test Cube'));

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalled();
      });

      // Wait for common dimensions to load after cube selection
      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      // Click the global Clear button (clear-all-btn class)
      const clearButton = document.querySelector('.clear-all-btn');
      fireEvent.click(clearButton);

      // Should show "Load from Cube" again (cube unloaded)
      await waitFor(() => {
        expect(screen.getByText('Load from Cube')).toBeInTheDocument();
      });
    });
  });

  describe('Pre-aggregation Lookup', () => {
    it('fetches existing pre-aggregations when measures result changes', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select metric and dimension
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      fireEvent.click(screen.getByRole('checkbox', { name: /dateint/i }));

      // Should fetch existing pre-aggs
      await waitFor(() => {
        expect(mockDjClient.listPreaggs).toHaveBeenCalled();
      });
    });
  });

  describe('Filter Handling', () => {
    it('adds a filter when add filter button is clicked', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Find filter input and add a filter
      const filterInput = screen.getByPlaceholderText(
        /e\.g\. v3\.date\.date_id/i,
      );
      fireEvent.change(filterInput, {
        target: { value: "date_id >= '2024-01-01'" },
      });

      const addButton = screen.getByText('Add');
      fireEvent.click(addButton);

      // Filter should be added (check for chip)
      await waitFor(() => {
        expect(screen.getByText("date_id >= '2024-01-01'")).toBeInTheDocument();
      });
    });

    it('removes a filter when remove button is clicked', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Add a filter first
      const filterInput = screen.getByPlaceholderText(
        /e\.g\. v3\.date\.date_id/i,
      );
      fireEvent.change(filterInput, {
        target: { value: "status = 'active'" },
      });
      fireEvent.click(screen.getByText('Add'));

      await waitFor(() => {
        expect(screen.getByText("status = 'active'")).toBeInTheDocument();
      });

      // Remove the filter (button has generic title "Remove filter")
      const removeButton = screen.getByTitle('Remove filter');
      fireEvent.click(removeButton);

      await waitFor(() => {
        expect(screen.queryByText("status = 'active'")).not.toBeInTheDocument();
      });
    });

    it('clears existing filters when a new cube is loaded', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue(mockCubeData);

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });

      // Set a filter in the current (ad-hoc) session.
      const filterInput = screen.getByPlaceholderText(
        /e\.g\. v3\.date\.date_id/i,
      );
      fireEvent.change(filterInput, { target: { value: "status = 'active'" } });
      fireEvent.click(screen.getByText('Add'));

      await waitFor(() => {
        expect(screen.getByText("status = 'active'")).toBeInTheDocument();
      });

      // Load a cube preset from the dropdown.
      fireEvent.click(screen.getByText('Load from Cube'));
      fireEvent.click(screen.getByText('Test Cube'));

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalled();
      });

      // The stale filter from the previous selection is gone.
      await waitFor(() => {
        expect(screen.queryByText("status = 'active'")).not.toBeInTheDocument();
      });
    });

    it('moves a filter back into the input when its chip is clicked (edit)', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      const filterInput = screen.getByPlaceholderText(
        /e\.g\. v3\.date\.date_id/i,
      );
      fireEvent.change(filterInput, { target: { value: "status = 'active'" } });
      fireEvent.click(screen.getByText('Add'));

      await waitFor(() => {
        expect(screen.getByText("status = 'active'")).toBeInTheDocument();
      });

      // Click the chip text to edit it.
      fireEvent.click(screen.getByText("status = 'active'"));

      // The expression returns to the input and the chip is gone.
      await waitFor(() => {
        expect(filterInput.value).toBe("status = 'active'");
      });
      expect(screen.queryByText("status = 'active'")).not.toBeInTheDocument();
    });

    it('disables add button when filter input is empty', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      const addButton = screen.getByText('Add');
      expect(addButton).toBeDisabled();
    });

    it('prefills the filter input when a dimension is dragged onto the Filters section', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue(mockCubeData);

      renderPage(['/planner?cube=default.test_cube']);

      // Cube preset loads and selects its dimension (rendered as a draggable chip).
      await waitFor(() => {
        expect(
          screen.getByText(/1 selected \/ 1 available/),
        ).toBeInTheDocument();
      });

      // Minimal DataTransfer stand-in for jsdom.
      const dataTransfer = {
        store: {},
        types: ['application/x-dj-dimension'],
        setData(type, val) {
          this.store[type] = val;
        },
        getData(type) {
          return this.store[type] || '';
        },
      };
      dataTransfer.setData(
        'application/x-dj-dimension',
        'default.date_dim.dateint',
      );

      const filtersSection = document.querySelector('.filters-section');
      fireEvent.dragOver(filtersSection, { dataTransfer });
      fireEvent.drop(filtersSection, { dataTransfer });

      const filterInput = screen.getByPlaceholderText(
        /e\.g\. v3\.date\.date_id/i,
      );
      await waitFor(() => {
        expect(filterInput.value).toContain('default.date_dim.dateint');
      });
    });

    it('expands a cube-based URL to metrics/dimensions/filters when a filter is added', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue(mockCubeData);

      const LocationDisplay = () => {
        const location = useLocation();
        return <div data-testid="location-search">{location.search}</div>;
      };

      render(
        <MemoryRouter initialEntries={['/planner?cube=default.test_cube']}>
          <Routes>
            <Route
              path="/planner"
              element={
                <DJClientContext.Provider
                  value={{ DataJunctionAPI: mockDjClient }}
                >
                  <>
                    <QueryPlannerPage />
                    <LocationDisplay />
                  </>
                </DJClientContext.Provider>
              }
            />
          </Routes>
        </MemoryRouter>,
      );

      // Cube preset loads from the URL...
      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalledWith(
          'default.test_cube',
        );
      });
      // ...and the URL stays compact (just the cube name) until the user edits.
      await waitFor(() => {
        expect(screen.getByTestId('location-search').textContent).toContain(
          'cube=default.test_cube',
        );
      });
      // Cube dimensions are applied once common dimensions resolve.
      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      // Apply a filter on the cube-based URL.
      const filterInput = screen.getByPlaceholderText(
        /e\.g\. v3\.date\.date_id/i,
      );
      fireEvent.change(filterInput, {
        target: { value: 'default.date_dim.dateint = 20260101' },
      });
      fireEvent.click(screen.getByText('Add'));

      // The URL must expand to its fully-qualified form so the filter is
      // reflected and the link is shareable: the cube param is dropped and
      // metrics, dimensions, and filters are all encoded.
      await waitFor(() => {
        const search = screen.getByTestId('location-search').textContent;
        expect(search).not.toContain('cube=');
        expect(search).toContain('metrics=');
        expect(search).toContain('dimensions=');
        expect(search).toContain('filters=');
      });
    });
  });

  describe('Run Query', () => {
    beforeEach(() => {
      mockDjClient.metricsV3.mockResolvedValue({
        sql: 'SELECT * FROM metrics',
        dialect: 'SPARK',
        cube_name: null,
      });
    });

    it('shows Run Query button when metrics and dimensions selected', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select metric
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      // Select dimension
      fireEvent.click(screen.getByRole('checkbox', { name: /dateint/i }));

      // Run Query button should exist
      await waitFor(() => {
        expect(screen.getByText('Run Query')).toBeInTheDocument();
      });
    });

    it('disables Run Query button when no metrics selected', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      const runButton = screen.getByText('Run Query');
      expect(runButton).toBeDisabled();
    });
  });

  describe('Materialization Handlers', () => {
    const mockMeasuresResult = {
      grainGroups: [
        {
          node: 'default.repair_orders',
          grain_columns: ['default.date_dim.dateint'],
          measures: [
            { name: 'sum_revenue', expression: 'SUM(revenue)' },
            { name: 'count_orders', expression: 'COUNT(*)' },
          ],
        },
      ],
      metricFormulas: [
        {
          metric: 'default.num_repair_orders',
          formula: 'default.repair_orders.count_orders',
        },
      ],
    };

    beforeEach(() => {
      mockDjClient.measuresV3.mockResolvedValue(mockMeasuresResult);
      mockDjClient.metricsV3.mockResolvedValue({
        sql: 'SELECT * FROM metrics',
        dialect: 'SPARK',
        cube_name: null,
      });
      mockDjClient.listPreaggs.mockResolvedValue({ items: [] });
      mockDjClient.getNodeColumnsWithPartitions.mockResolvedValue({
        columns: [],
        temporalPartitions: [],
      });
    });

    it('calls planPreaggs when materialization is planned', async () => {
      mockDjClient.planPreaggs.mockResolvedValue({
        preaggs: [
          {
            id: 1,
            node_name: 'default.repair_orders',
            grain_columns: ['default.date_dim.dateint'],
          },
        ],
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select metric and dimension
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      fireEvent.click(screen.getByRole('checkbox', { name: /dateint/i }));

      await waitFor(() => {
        expect(mockDjClient.measuresV3).toHaveBeenCalled();
      });
    });

    it('handles planPreaggs error gracefully', async () => {
      mockDjClient.planPreaggs.mockResolvedValue({
        _error: true,
        message: 'Failed to plan',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('calls materializePreagg when workflow is created', async () => {
      mockDjClient.planPreaggs.mockResolvedValue({
        preaggs: [
          {
            id: 123,
            node_name: 'default.repair_orders',
            grain_columns: ['default.date_dim.dateint'],
          },
        ],
      });
      mockDjClient.materializePreagg.mockResolvedValue({
        workflow_urls: ['http://workflow.example.com'],
        workflow_status: 'active',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('handles materializePreagg error', async () => {
      mockDjClient.materializePreagg.mockResolvedValue({
        _error: true,
        message: 'Failed to create workflow',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('calls runPreaggBackfill when backfill is triggered', async () => {
      mockDjClient.runPreaggBackfill.mockResolvedValue({
        job_url: 'http://job.example.com',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('handles runPreaggBackfill error', async () => {
      mockDjClient.runPreaggBackfill.mockResolvedValue({
        _error: true,
        message: 'Backfill failed',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('calls updatePreaggConfig for config updates', async () => {
      mockDjClient.updatePreaggConfig.mockResolvedValue({
        id: 123,
        strategy: 'incremental_time',
        schedule: '0 6 * * *',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('handles updatePreaggConfig error', async () => {
      mockDjClient.updatePreaggConfig.mockResolvedValue({
        _error: true,
        message: 'Config update failed',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('calls deactivatePreaggWorkflow when workflow is deactivated', async () => {
      mockDjClient.deactivatePreaggWorkflow.mockResolvedValue({
        success: true,
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('handles deactivatePreaggWorkflow error', async () => {
      mockDjClient.deactivatePreaggWorkflow.mockResolvedValue({
        _error: true,
        message: 'Failed to deactivate',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });
  });

  describe('Cube Materialization Flow', () => {
    beforeEach(() => {
      mockDjClient.listCubesForPreset.mockResolvedValue([
        { name: 'default.test_cube', display_name: 'Test Cube' },
      ]);
      mockDjClient.cubeForPlanner.mockResolvedValue({
        name: 'default.test_cube',
        display_name: 'Test Cube',
        cube_node_metrics: ['default.num_repair_orders'],
        cube_node_dimensions: ['default.date_dim.dateint'],
        cubeMaterialization: {
          strategy: 'full',
          schedule: '0 6 * * *',
          lookbackWindow: null,
          druidDatasource: 'test_ds',
          preaggTables: [],
          workflowUrls: ['http://workflow.url'],
        },
        availability: null,
      });
    });

    it('calls createCube when new cube is created', async () => {
      mockDjClient.createCube.mockResolvedValue({
        status: 200,
        json: { name: 'new.cube' },
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('handles createCube error - cube already exists', async () => {
      mockDjClient.createCube.mockResolvedValue({
        status: 400,
        json: { message: 'Cube already exists' },
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('handles createCube error - other error', async () => {
      mockDjClient.createCube.mockResolvedValue({
        status: 500,
        json: { message: 'Server error' },
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('calls materializeCubeV2 for cube materialization', async () => {
      mockDjClient.materializeCubeV2.mockResolvedValue({
        status: 200,
        json: {
          workflow_urls: ['http://cube-workflow.example.com'],
        },
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('handles materializeCubeV2 error', async () => {
      mockDjClient.materializeCubeV2.mockResolvedValue({
        status: 500,
        json: { message: 'Cube materialization failed' },
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('calls refreshCubeWorkflow for config updates', async () => {
      mockDjClient.refreshCubeWorkflow.mockResolvedValue({
        status: 200,
        json: { workflow_urls: ['http://updated-workflow.url'] },
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('handles refreshCubeWorkflow error', async () => {
      mockDjClient.refreshCubeWorkflow.mockResolvedValue({
        status: 500,
        json: { message: 'Refresh failed' },
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('calls deactivateCubeWorkflow when deactivating', async () => {
      mockDjClient.deactivateCubeWorkflow.mockResolvedValue({
        status: 200,
        json: { success: true },
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });

      // Load cube first
      fireEvent.click(screen.getByText('Load from Cube'));
      fireEvent.click(screen.getByText('Test Cube'));

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalled();
      });
    });

    it('handles deactivateCubeWorkflow error', async () => {
      mockDjClient.deactivateCubeWorkflow.mockResolvedValue({
        status: 500,
        json: { message: 'Deactivation failed' },
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('calls runCubeBackfill for backfill', async () => {
      mockDjClient.runCubeBackfill.mockResolvedValue({
        job_url: 'http://backfill-job.example.com',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });

      // Load cube
      fireEvent.click(screen.getByText('Load from Cube'));
      fireEvent.click(screen.getByText('Test Cube'));

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalled();
      });
    });

    it('handles runCubeBackfill error', async () => {
      mockDjClient.runCubeBackfill.mockResolvedValue({
        _error: true,
        message: 'Backfill failed',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });
    });

    it('handles runCubeBackfill with missing cube name', async () => {
      // Don't load any cube - loadedCubeName will be null
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // No cube loaded, so runCubeBackfill shouldn't be callable
    });
  });

  describe('Workflow Status Updates', () => {
    const mockMeasuresWithPreagg = {
      grainGroups: [
        {
          node: 'default.repair_orders',
          grain_columns: ['default.date_dim.dateint'],
          measures: [{ name: 'count_orders', expression: 'COUNT(*)' }],
        },
      ],
      metricFormulas: [
        {
          metric: 'default.num_repair_orders',
          formula: 'count_orders',
        },
      ],
    };

    beforeEach(() => {
      mockDjClient.measuresV3.mockResolvedValue(mockMeasuresWithPreagg);
      mockDjClient.metricsV3.mockResolvedValue({
        sql: 'SELECT * FROM metrics',
        dialect: 'SPARK',
        cube_name: null,
      });
      mockDjClient.listPreaggs.mockResolvedValue({ items: [] });
    });

    it('updates state when workflow is created successfully', async () => {
      mockDjClient.materializePreagg.mockResolvedValue({
        workflow_urls: ['http://new-workflow.example.com'],
        workflow_status: 'active',
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('updates state when workflow is deactivated', async () => {
      mockDjClient.deactivatePreaggWorkflow.mockResolvedValue({
        success: true,
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('handles network errors in workflow operations', async () => {
      mockDjClient.materializePreagg.mockRejectedValue(
        new Error('Network error'),
      );

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });
  });

  describe('Raw SQL Fetching', () => {
    it('fetches raw SQL when needed', async () => {
      mockDjClient.metricsV3.mockResolvedValue({
        sql: 'SELECT * FROM raw_tables',
        dialect: 'SPARK',
        cube_name: null,
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      // Select metric and dimension to trigger SQL fetch
      fireEvent.click(screen.getByText('default'));
      await waitFor(() => {
        fireEvent.click(
          screen.getByRole('checkbox', { name: /num_repair_orders/i }),
        );
      });

      await waitFor(() => {
        expect(mockDjClient.commonDimensions).toHaveBeenCalled();
      });

      fireEvent.click(screen.getByRole('checkbox', { name: /dateint/i }));

      await waitFor(() => {
        expect(mockDjClient.metricsV3).toHaveBeenCalled();
      });
    });
  });

  describe('Cube Workflow Handlers', () => {
    beforeEach(() => {
      mockDjClient.listCubesForPreset.mockResolvedValue([
        { name: 'default.test_cube', display_name: 'Test Cube' },
      ]);
    });

    it('handles cube deactivation', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue({
        name: 'default.test_cube',
        display_name: 'Test Cube',
        cube_node_metrics: ['default.num_repair_orders'],
        cube_node_dimensions: ['default.date_dim.dateint'],
        cubeMaterialization: {
          strategy: 'full',
          schedule: '0 6 * * *',
          lookbackWindow: null,
          druidDatasource: 'test_ds',
          preaggTables: [],
          workflowUrls: ['http://workflow.url'],
        },
        availability: null,
      });
      mockDjClient.deactivateCubeWorkflow.mockResolvedValue({ success: true });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });

      // Load cube
      fireEvent.click(screen.getByText('Load from Cube'));
      fireEvent.click(screen.getByText('Test Cube'));

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalled();
      });
    });

    it('handles cube backfill', async () => {
      mockDjClient.cubeForPlanner.mockResolvedValue({
        name: 'default.test_cube',
        display_name: 'Test Cube',
        cube_node_metrics: ['default.num_repair_orders'],
        cube_node_dimensions: ['default.date_dim.dateint'],
        cubeMaterialization: {
          strategy: 'full',
          schedule: '0 6 * * *',
          lookbackWindow: null,
          druidDatasource: 'test_ds',
          preaggTables: [],
          workflowUrls: [],
        },
        availability: null,
      });
      mockDjClient.runCubeBackfill.mockResolvedValue({ job_url: 'http://job' });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.listCubesForPreset).toHaveBeenCalled();
      });

      // Load cube
      fireEvent.click(screen.getByText('Load from Cube'));
      fireEvent.click(screen.getByText('Test Cube'));

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalled();
      });
    });
  });

  describe('Partition Handling', () => {
    it('fetches node partitions when needed', async () => {
      mockDjClient.getNodeColumnsWithPartitions.mockResolvedValue({
        columns: [{ name: 'date_col', type: 'date' }],
        temporalPartitions: [{ column: 'date_col' }],
      });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });

    it('handles setPartition call', async () => {
      mockDjClient.setPartition.mockResolvedValue({ success: true });

      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });
  });

  describe('Results View Navigation', () => {
    it('shows results view hint when no selection', async () => {
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });

      expect(
        screen.getByText('Select at least one metric to run a query'),
      ).toBeInTheDocument();
    });
  });

  describe('Pane Resize Handlers', () => {
    it('resizes selection panel when dragging left vertical-resizer', async () => {
      renderPage();
      await waitFor(() => expect(mockDjClient.metrics).toHaveBeenCalled());

      const resizers = document.querySelectorAll('.vertical-resizer');
      // First resizer is between selection panel and main content
      const selectionResizer = resizers[0];
      expect(selectionResizer).toBeInTheDocument();

      // Mousedown begins drag
      fireEvent.mouseDown(selectionResizer, { clientX: 200 });

      // Move right → wider selection panel
      fireEvent.mouseMove(document, { clientX: 280 });

      const selectionPane = document.querySelector('.planner-selection');
      // Inline width should be applied (clamped to min 200)
      expect(selectionPane.style.width).toBeTruthy();

      // Mouseup ends drag
      fireEvent.mouseUp(document);

      // Further moves should not update
      const widthAfterUp = selectionPane.style.width;
      fireEvent.mouseMove(document, { clientX: 600 });
      expect(selectionPane.style.width).toBe(widthAfterUp);
    });

    it('resizes details panel when dragging right vertical-resizer', async () => {
      renderPage();
      await waitFor(() => expect(mockDjClient.metrics).toHaveBeenCalled());

      const resizers = document.querySelectorAll('.vertical-resizer');
      // Second resizer is between graph and details panel (visible in non-results state)
      expect(resizers.length).toBeGreaterThanOrEqual(2);
      const detailsResizer = resizers[1];

      // Mousedown begins drag
      fireEvent.mouseDown(detailsResizer, { clientX: 800 });

      // Move left → wider details panel
      fireEvent.mouseMove(document, { clientX: 700 });

      const detailsPane = document.querySelector('.planner-details');
      expect(detailsPane.style.width).toBeTruthy();

      // Mouseup ends drag
      fireEvent.mouseUp(document);

      const widthAfterUp = detailsPane.style.width;
      fireEvent.mouseMove(document, { clientX: 400 });
      expect(detailsPane.style.width).toBe(widthAfterUp);
    });
  });

  describe('URL Init Edge Cases', () => {
    it('handles invalid cube data returned from cubeForPlanner (missing cube_node_metrics)', async () => {
      // cubeForPlanner returns data without cube_node_metrics array
      mockDjClient.cubeForPlanner.mockResolvedValue({ name: 'bad.cube' });

      renderPage(['/planner?cube=bad.cube']);

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalledWith('bad.cube');
      });

      // Page should still render without crashing (invalid cube data logged, not applied)
      expect(screen.getByText('Load from Cube')).toBeInTheDocument();
    });

    it('handles cubeForPlanner throwing during URL init', async () => {
      mockDjClient.cubeForPlanner.mockRejectedValue(new Error('Network error'));

      renderPage(['/planner?cube=bad.cube']);

      await waitFor(() => {
        expect(mockDjClient.cubeForPlanner).toHaveBeenCalled();
      });

      // Page should still render gracefully
      expect(screen.getByText('Load from Cube')).toBeInTheDocument();
    });

    it('handles metrics API failure on mount gracefully', async () => {
      mockDjClient.metrics.mockRejectedValue(new Error('API down'));

      // Should not throw
      renderPage();

      await waitFor(() => {
        expect(mockDjClient.metrics).toHaveBeenCalled();
      });
    });
  });
});
