// ========================================
// Dashboard E2E Helper Functions
// ========================================
// Reusable utilities for dashboard E2E interactions

import { Page, Locator, expect } from '@playwright/test';

/**
 * Wait for all dashboard widgets to finish loading
 * @param page - Playwright Page object
 * @param timeout - Maximum wait time in milliseconds (default: 30000)
 */
export async function waitForDashboardLoad(page: Page, timeout = 30000): Promise<void> {
  // Wait for network idle first
  await page.waitForLoadState('networkidle', { timeout });

  // Wait for dashboard grid container to be visible
  const dashboardGrid = page.getByTestId('dashboard-grid-container').or(
    page.locator('.dashboard-grid-container')
  );

  await expect(dashboardGrid).toBeVisible({ timeout });

  // Wait for all widget skeletons to disappear
  const skeletons = page.locator('[data-testid*="skeleton"]');
  const skeletonCount = await skeletons.count();

  if (skeletonCount > 0) {
    // Wait for skeletons to be hidden
    await page.waitForFunction(
      () => {
        const skels = document.querySelectorAll('[data-testid*="skeleton"]');
        return Array.from(skels).every(
          (skel) => window.getComputedStyle(skel).display === 'none'
        );
      },
      { timeout }
    );
  }

  // Wait for stats cards to be visible
  const statsCards = page.getByTestId(/stat-card/).or(page.locator('.stat-card'));
  const statsCount = await statsCards.count();

  if (statsCount > 0) {
    await expect(statsCards.first()).toBeVisible({ timeout });
  }

  // Wait for animations to complete - use waitForLoadState instead of timeout
  await page.waitForLoadState('domcontentloaded');
}

/**
 * Verify a specific chart type has rendered correctly
 * @param page - Playwright Page object
 * @param chartType - Type of chart to verify
 * @returns Promise<boolean> indicating if chart rendered successfully
 */
export async function verifyChartRendered(
  page: Page,
  chartType: 'pie' | 'line' | 'bar'
): Promise<boolean> {
  let chartSelector: string;

  switch (chartType) {
    case 'pie':
      chartSelector = '[data-testid="workflow-status-pie-chart"]';
      break;
    case 'line':
      chartSelector = '[data-testid="activity-line-chart"]';
      break;
    case 'bar':
      chartSelector = '[data-testid="task-type-bar-chart"]';
      break;
  }

  // Find chart container
  const chartContainer = page.locator(chartSelector);
  const isVisible = await chartContainer.isVisible().catch(() => false);

  if (!isVisible) {
    return false;
  }

  // Verify chart has rendered content (SVG elements)
  const svgElement = chartContainer.locator('svg').first();
  const hasSvg = await svgElement.isVisible().catch(() => false);

  if (!hasSvg) {
    return false;
  }

  // Check for chart-specific elements
  switch (chartType) {
    case 'pie': {
      // Pie chart should have path elements (slices)
      const slices = chartContainer.locator('path.recharts-pie-sector');
      const sliceCount = await slices.count();
      return sliceCount > 0;
    }
    case 'line': {
      // Line chart should have line path elements
      const lines = chartContainer.locator('path.recharts-line-curve');
      const lineCount = await lines.count();
      return lineCount > 0;
    }
    case 'bar': {
      // Bar chart should have rect elements (bars)
      const bars = chartContainer.locator('rect.recharts-bar-rectangle');
      const barCount = await bars.count();
      return barCount > 0;
    }
  }
}

/**
 * Simulate drag-drop interaction for widget repositioning
 * @param page - Playwright Page object
 * @param widgetId - Widget identifier (data-grid i attribute)
 * @param targetX - Target X coordinate
 * @param targetY - Target Y coordinate
 */
export async function simulateDragDrop(
  page: Page,
  widgetId: string,
  targetX: number,
  targetY: number
): Promise<void> {
  // Find widget by data-grid attribute
  const widget = page.locator(`[data-grid*='"i":"${widgetId}"']`).or(
    page.getByTestId(`widget-${widgetId}`)
  );

  await expect(widget).toBeVisible();

  // Get widget's current position
  const widgetBox = await widget.boundingBox();
  if (!widgetBox) {
    throw new Error(`Widget ${widgetId} not found or not visible`);
  }

  // Calculate drag coordinates
  const startX = widgetBox.x + widgetBox.width / 2;
  const startY = widgetBox.y + 20; // Drag from header area

  // Perform drag-drop
  await page.mouse.move(startX, startY);
  await page.mouse.down();

  // Move to target position
  await page.mouse.move(targetX, targetY, { steps: 10 });

  await page.mouse.up();

  // Wait for layout to settle - use waitForLoadState instead of timeout
  await page.waitForLoadState('domcontentloaded');
}

/**
 * Get current layout configuration from dashboard
 * @param page - Playwright Page object
 * @returns Layout configuration object
 */
export async function getDashboardLayout(page: Page): Promise<Record<string, any>> {
  const layout = await page.evaluate(() => {
    const storage = localStorage.getItem('ccw-app-store');
    if (!storage) return null;

    const parsed = JSON.parse(storage);
    return parsed.state?.dashboardLayout || null;
  });

  return layout;
}

/**
 * Verify navigation group is expanded/collapsed
 * @param page - Playwright Page object
 * @param groupName - Navigation group name (e.g., 'Overview', 'Workflow')
 * @param expectedExpanded - Whether group should be expanded
 */
export async function verifyNavGroupState(
  page: Page,
  groupName: string,
  expectedExpanded: boolean
): Promise<void> {
  const groupTrigger = page.getByRole('button', { name: new RegExp(groupName, 'i') });
  await expect(groupTrigger).toBeVisible();

  const ariaExpanded = await groupTrigger.getAttribute('aria-expanded');
  const isExpanded = ariaExpanded === 'true';

  if (isExpanded !== expectedExpanded) {
    throw new Error(
      `Navigation group "${groupName}" expected to be ${expectedExpanded ? 'expanded' : 'collapsed'} but was ${isExpanded ? 'expanded' : 'collapsed'}`
    );
  }
}

/**
 * Toggle navigation group expand/collapse
 * @param page - Playwright Page object
 * @param groupName - Navigation group name
 */
export async function toggleNavGroup(page: Page, groupName: string): Promise<void> {
  const groupTrigger = page.getByRole('button', { name: new RegExp(groupName, 'i') });
  await expect(groupTrigger).toBeVisible();

  await groupTrigger.click();
  // Wait for accordion animation - use explicit wait
  await page.waitForFunction(() => {
    const group = document.querySelector('[aria-expanded]');
    return group !== null;
  }, { timeout: 3000 });
}

/**
 * Verify ticker marquee is displaying messages
 * @param page - Playwright Page object
 * @returns Number of messages displayed
 */
export async function verifyTickerMessages(page: Page): Promise<number> {
  const tickerContainer = page.getByTestId('ticker-marquee').or(
    page.locator('.ticker-marquee')
  );

  const isVisible = await tickerContainer.isVisible().catch(() => false);
  if (!isVisible) {
    return 0;
  }

  const messages = tickerContainer.locator('.ticker-message').or(
    tickerContainer.locator('[data-message]')
  );

  return await messages.count();
}

/**
 * Simulate WebSocket message for ticker testing
 * @param page - Playwright Page object
 * @param message - Mock ticker message
 */
export async function simulateTickerMessage(
  page: Page,
  message: {
    id: string;
    text: string;
    type: 'session' | 'task' | 'workflow' | 'status';
    link?: string;
    timestamp: number;
  }
): Promise<void> {
  await page.evaluate((msg) => {
    const event = new MessageEvent('message', {
      data: JSON.stringify(msg),
    });

    // Dispatch to WebSocket mock if available
    const ws = (window as any).__mockWebSocket;
    if (ws && ws.onmessage) {
      ws.onmessage(event);
    }
  }, message);

  // Wait for message to be processed - use explicit wait
  await page.waitForLoadState('domcontentloaded');
}

/**
 * Verify chart tooltip appears on hover
 * @param page - Playwright Page object
 * @param chartType - Type of chart
 * @returns True if tooltip appeared
 */
export async function verifyChartTooltip(
  page: Page,
  chartType: 'pie' | 'line' | 'bar'
): Promise<boolean> {
  let chartSelector: string;

  switch (chartType) {
    case 'pie':
      chartSelector = '[data-testid="workflow-status-pie-chart"]';
      break;
    case 'line':
      chartSelector = '[data-testid="activity-line-chart"]';
      break;
    case 'bar':
      chartSelector = '[data-testid="task-type-bar-chart"]';
      break;
  }

  const chartContainer = page.locator(chartSelector);

  // Find interactive chart element
  const chartElement = chartContainer.locator('svg').first();
  await expect(chartElement).toBeVisible();

  // Hover over chart
  await chartElement.hover({ position: { x: 50, y: 50 } });
  // Wait for tooltip animation - use explicit wait
  await page.waitForFunction(() => {
    const tooltip = document.querySelector('.recharts-tooltip-wrapper, [role="tooltip"]');
    return tooltip !== null && window.getComputedStyle(tooltip).opacity !== '0';
  }, { timeout: 3000 }).catch(() => true); // Don't fail if tooltip doesn't appear

  // Check if tooltip is visible
  const tooltip = page.locator('.recharts-tooltip-wrapper').or(
    page.locator('[role="tooltip"]')
  );

  return await tooltip.isVisible().catch(() => false);
}

/**
 * Verify all widgets are present on dashboard
 * @param page - Playwright Page object
 * @param expectedWidgetCount - Expected number of widgets
 */
export async function verifyAllWidgetsPresent(
  page: Page,
  expectedWidgetCount = 5
): Promise<void> {
  // Look for widget containers
  const widgets = page.locator('[data-grid]').or(page.locator('.widget-container'));

  const widgetCount = await widgets.count();

  if (widgetCount < expectedWidgetCount) {
    throw new Error(
      `Expected ${expectedWidgetCount} widgets but found ${widgetCount}`
    );
  }

  // Verify each widget is visible
  for (let i = 0; i < expectedWidgetCount; i++) {
    await expect(widgets.nth(i)).toBeVisible();
  }
}

/**
 * Wait for specific widget to load
 * @param page - Playwright Page object
 * @param widgetId - Widget identifier
 */
export async function waitForWidgetLoad(page: Page, widgetId: string): Promise<void> {
  const widget = page.getByTestId(`widget-${widgetId}`).or(
    page.locator(`[data-widget="${widgetId}"]`)
  );

  await expect(widget).toBeVisible({ timeout: 10000 });

  // Wait for skeleton to disappear
  const skeleton = widget.locator('[data-testid*="skeleton"]');
  const hasSkeleton = await skeleton.isVisible().catch(() => false);

  if (hasSkeleton) {
    await expect(skeleton).toBeHidden({ timeout: 5000 });
  }
}

/**
 * Verify responsive layout changes at breakpoint
 * @param page - Playwright Page object
 * @param breakpoint - Breakpoint name ('mobile', 'tablet', 'desktop')
 */
export async function verifyResponsiveLayout(
  page: Page,
  breakpoint: 'mobile' | 'tablet' | 'desktop'
): Promise<void> {
  const viewportSizes = {
    mobile: { width: 375, height: 667 },
    tablet: { width: 768, height: 1024 },
    desktop: { width: 1440, height: 900 },
  };

  await page.setViewportSize(viewportSizes[breakpoint]);
  // Wait for layout reflow - use explicit wait
  await page.waitForLoadState('domcontentloaded');

  // Verify grid layout adjusts
  const grid = page.getByTestId('dashboard-grid-container');
  await expect(grid).toBeVisible();

  // Check computed styles for grid columns
  const gridColumns = await grid.evaluate((el) => {
    return window.getComputedStyle(el).gridTemplateColumns;
  });

  // Verify column count matches breakpoint expectations
  const columnCount = gridColumns.split(' ').length;
  const expectedColumns = {
    mobile: [1, 2], // 1-2 columns on mobile
    tablet: [2, 6], // 2-6 columns on tablet
    desktop: [12], // 12 columns on desktop
  };

  const isValidLayout = expectedColumns[breakpoint].some((count) =>
    Math.abs(columnCount - count) <= 1
  );

  if (!isValidLayout) {
    throw new Error(
      `Layout at ${breakpoint} has ${columnCount} columns, expected ${expectedColumns[breakpoint]}`
    );
  }
}
