/**
 * @file Visual Regression Tests for KERN UX Drupal Components.
 * @description Automated visual testing that compares Drupal SDC components
 * against the official KERN-UX design system implementation.
 *
 * ## How It Works
 * 1. KERN-UX baselines are captured from the reference Storybook (official implementation)
 * 2. Drupal SDC screenshots are captured from the local Storybook
 * 3. Both are compared to ensure visual compliance
 *
 * ## Workflow
 * 1. Run `npm run vrt:generate-reference` to download official KERN-UX Storybook
 * 2. Run `npm run vrt:baseline` to capture KERN-UX baselines (first time or after KERN-UX updates)
 * 3. Run `npm run vrt` to compare Drupal SDC components against KERN-UX baselines
 */

import { test, expect, TestInfo, Page } from "@playwright/test";
import * as fs from "fs";
import * as path from "path";
import {
  COMPONENT_MAPPINGS,
  ComponentMapping,
  getEnabledMappings,
} from "./component-mapping";

/** Storybook server port (Drupal SDC) - using dev server */
const STORYBOOK_PORT = 6006;

/** Reference server port (KERN-UX) */
const REFERENCE_PORT = 6008;

/** Viewport configuration for consistent screenshots */
const VIEWPORT = { width: 1280, height: 720 };

/** Reference Storybook directory */
const REFERENCE_DIR = path.resolve(__dirname, "reference-storybook");

/**
 * Checks if the reference Storybook exists.
 *
 * @returns True if reference Storybook is available
 */
function isReferenceAvailable(): boolean {
  return fs.existsSync(path.join(REFERENCE_DIR, "index.json"));
}

/**
 * Extracts the theme from project metadata or name.
 *
 * @param testInfo - Playwright test info object
 * @returns The theme to use for this test ("light" or "dark")
 */
function getThemeFromProject(testInfo: TestInfo): "light" | "dark" {
  const projectName = testInfo.project.name;
  if (projectName.includes("dark")) {
    return "dark";
  }
  return "light";
}

/**
 * Generates a consistent snapshot name for a mapping.
 *
 * @param mapping - The component mapping
 * @param theme - The theme
 * @param browserName - The browser name
 * @returns The snapshot filename
 */
function getSnapshotName(
  mapping: ComponentMapping,
  theme: "light" | "dark",
  browserName: string
): string {
  // Extract component and story name from Drupal story ID
  // e.g., "kern-ux-sdc-button--primary" -> "button--primary"
  // Note: Playwright requires .png extension when using custom snapshot names
  const storyPart = mapping.drupalStoryId.replace("kern-ux-sdc-", "");
  return `${storyPart}--${theme}--${browserName}.png`;
}

/**
 * Navigates to a Storybook story and waits for it to be ready.
 *
 * @param page - Playwright page object
 * @param baseUrl - Base URL of the Storybook
 * @param storyId - The story ID to navigate to
 * @param theme - The theme to apply
 */
async function navigateToStory(
  page: Page,
  baseUrl: string,
  storyId: string,
  theme: "light" | "dark"
): Promise<void> {
  // Navigate to main Storybook page to initialize JavaScript modules
  await page.goto(baseUrl, { waitUntil: "domcontentloaded" });

  // Wait for Storybook UI to load (confirms JS is initialized)
  await page.waitForSelector("#storybook-preview-iframe", { timeout: 10000 });

  // Navigate to iframe directly for clean component screenshot
  // Development Storybook (port 6006) uses /iframe.html
  // Reference Storybook (port 6008) uses /iframe (no .html suffix)
  const isReferenceServer = baseUrl.includes(`:${REFERENCE_PORT}`);
  const iframePath = isReferenceServer ? "/iframe" : "/iframe.html";
  const iframeUrl = `${baseUrl}${iframePath}?id=${encodeURIComponent(storyId)}&viewMode=story&globals=theme:${theme}`;
  await page.goto(iframeUrl, { waitUntil: "domcontentloaded" });

  // Wait for story root element to be present
  await page.waitForSelector("#storybook-root", { timeout: 100000 });

  // Wait for web fonts to finish loading
  await page.evaluate(async () => {
    await document.fonts.ready;
  });
}

/**
 * Gets the story root element for screenshots.
 * When on iframe.html directly, #storybook-root contains the component.
 *
 * @param page - Playwright page object
 * @returns Locator for the story root element
 */
function getStoryRoot(page: Page) {
  return page.locator("#storybook-root");
}

/**
 * Waits for a story to be fully loaded and rendered.
 * Waits for #storybook-root to have visible content.
 *
 * @param page - Playwright page object
 */
async function waitForStoryContent(page: Page): Promise<void> {
  // Wait for #storybook-root to have child elements
  await page.locator("#storybook-root > *").first().waitFor({ state: "visible", timeout: 10000 });

  // Brief stabilization wait for animations/fonts
  await page.waitForTimeout(500);
}


/**
 * Test suite: Capture KERN-UX baselines.
 *
 * Run this to capture screenshots from the official KERN-UX Storybook.
 * These become the "golden" baselines that Drupal components are compared against.
 *
 * Usage: npm run vrt:baseline
 */
test.describe("KERN-UX Baseline Capture", () => {
  test.beforeAll(() => {
    if (!isReferenceAvailable()) {
      throw new Error(
        "Reference Storybook not found. Run `npm run vrt:generate-reference` first."
      );
    }
  });

  /**
   * Smoke test to verify we're connecting to the KERN-UX reference server.
   * This ensures baselines are captured from the correct source.
   */
  test("Reference server is accessible", async ({ page }, testInfo) => {
    test.skip(
      !testInfo.project.name.includes("baseline"),
      "Skipping: Not a baseline capture project"
    );

    const referenceUrl = `http://localhost:${REFERENCE_PORT}`;

    // Navigate to the reference Storybook
    const response = await page.goto(referenceUrl, { waitUntil: "domcontentloaded" });

    // Verify server is reachable
    expect(response?.ok(), `Reference server not reachable at ${referenceUrl}`).toBeTruthy();

    // Wait for Storybook to initialize
    await page.waitForTimeout(2000);

    // Verify it's the KERN-UX reference by checking for known content
    const pageContent = await page.content();
    const isKernUxReference = pageContent.includes("kern") || pageContent.includes("KERN") || pageContent.includes("Storybook");
    expect(isKernUxReference, "Server does not appear to be KERN-UX reference Storybook").toBeTruthy();
  });

  test.use({ viewport: VIEWPORT });

  for (const [componentName, mappings] of Object.entries(COMPONENT_MAPPINGS)) {
    test.describe(`Baseline: ${componentName}`, () => {
      for (const mapping of mappings) {
        if (!mapping.enabled) continue;

        test(`Capture: ${mapping.description}`, async ({ page, browserName }, testInfo) => {
          // Skip if not running baseline capture project
          test.skip(
            !testInfo.project.name.includes("baseline"),
            "Skipping: Not a baseline capture project"
          );

          const theme = getThemeFromProject(testInfo);
          const baseUrl = `http://localhost:${REFERENCE_PORT}`;

          // Navigate to the story in the KERN-UX reference Storybook
          await navigateToStory(page, baseUrl, mapping.kernUxStoryId, theme);

          // Wait for component to render
          await waitForStoryContent(page);

          // Get the story root element for screenshot
          const root = getStoryRoot(page);

          // Generate snapshot filename and capture screenshot
          const snapshotName = getSnapshotName(mapping, theme, browserName);

          await expect(root).toHaveScreenshot(snapshotName, {
            maxDiffPixelRatio: 0.01,
            threshold: 0.1,
            animations: "disabled",
          });
        });
      }
    });
  }
});

/**
 * Test suite: Compare Drupal SDC against KERN-UX baselines.
 *
 * This compares your Drupal SDC components against the KERN-UX baselines.
 * Failures indicate visual differences from the official design system.
 *
 * Usage: npm run vrt
 */
test.describe("KERN-UX Compliance Tests", () => {
  test.use({ viewport: VIEWPORT });

  for (const [componentName, mappings] of Object.entries(COMPONENT_MAPPINGS)) {
    test.describe(`Component: ${componentName}`, () => {
      for (const mapping of mappings) {
        if (!mapping.enabled) continue;

        test(`${mapping.description} matches KERN-UX`, async ({ page, browserName }, testInfo) => {
          // Skip if running baseline capture
          test.skip(
            testInfo.project.name.includes("baseline"),
            "Skipping: This is a baseline capture project"
          );

          const theme = getThemeFromProject(testInfo);
          const baseUrl = `http://localhost:${STORYBOOK_PORT}`;

          // Navigate to the story in the Drupal SDC Storybook
          await navigateToStory(page, baseUrl, mapping.drupalStoryId, theme);

          // Wait for component to render
          await waitForStoryContent(page);

          // Get the story root element for screenshot
          const root = getStoryRoot(page);

          // Compare screenshot against KERN-UX baseline
          const snapshotName = getSnapshotName(mapping, theme, browserName);
          await expect(root).toHaveScreenshot(snapshotName, {
            maxDiffPixelRatio: 0.02,
            threshold: 0.3,
            animations: "disabled",
          });
        });
      }
    });
  }
});

/**
 * Smoke tests to verify servers are running.
 */
test.describe("Server Smoke Tests", () => {
  test("Storybook (Drupal SDC) is accessible", async ({ page }) => {
    await page.goto(`http://localhost:${STORYBOOK_PORT}/`);
    await page.waitForLoadState("networkidle");
    const content = await page.content();
    expect(
      content.toLowerCase().includes("storybook") ||
      content.toLowerCase().includes("kern")
    ).toBeTruthy();
  });

  test("Component mappings are configured", () => {
    const mappings = getEnabledMappings();
    expect(mappings.length).toBeGreaterThan(0);
  });
});
