import englishLocale from '@node-core/website-i18n/locales/en.json' with { type: 'json' };
import { test, expect, type Page } from '@playwright/test';

import type { Locale } from '@node-core/website-i18n/types';

// TODO(@avivkeller): It would be ideal for all the Test IDs to not exist in the
//                    ui-components package, and instead be passed as props.
const locators = {
  // Navigation elements
  mobileMenuToggleName:
    englishLocale.components.containers.navBar.controls.toggle,
  navLinksLocator: `[aria-label="${englishLocale.components.containers.navBar.controls.toggle}"] + div`,
  // The Beta Docs link renders untranslated (English) across locales, so we
  // match on its English label to skip it during translation checks.
  betaDocsName: englishLocale.components.containers.navBar.links.betaDocs,
  // Global UI controls
  languageDropdownName: englishLocale.components.common.languageDropdown.label,
  themeToggleName: englishLocale.components.header.buttons.theme,
  themeDarkLabel: englishLocale.components.header.buttons.themeDarkMode,
  themeLightLabel: englishLocale.components.header.buttons.themeLightMode,
};

const getTheme = (page: Page) =>
  page.evaluate(
    () => document.documentElement.dataset.theme as 'light' | 'dark'
  );

const openLanguageMenu = async (page: Page) => {
  const button = page.getByRole('button', {
    name: locators.languageDropdownName,
  });
  const selector = `[aria-labelledby=${await button.getAttribute('id')}]`;
  await button.click();

  await page.waitForSelector(selector);
  return page.locator(selector);
};

const verifyTranslation = async (page: Page, locale: Locale | string) => {
  // Load locale data if string code provided (e.g., 'es', 'fr')
  const localeData: Locale =
    typeof locale === 'string'
      ? (
          await import(`@node-core/website-i18n/locales/${locale}.json`, {
            with: { type: 'json' },
          })
        ).default
      : locale;

  // Get navigation links and expected translations
  const links = await page
    .locator(locators.navLinksLocator)
    .locator('a > span')
    .all();
  const expectedTexts = Object.values(
    localeData.components.containers.navBar.links
  );

  // Verify each navigation link text matches an expected translation
  for (const link of links) {
    const linkText = (await link.textContent())!.trim();
    // Skip the Beta Docs link: it renders untranslated (English) across
    // locales and has no translation entry in most locale files, so it
    // won't appear in `expectedTexts` for non-English locales (e.g. es).
    if (linkText === locators.betaDocsName) {
      continue;
    }
    expect(expectedTexts).toContain(linkText);
  }
};

test.describe('Node.js Website', () => {
  // Start each test from the English homepage
  test.beforeEach(async ({ page }) => {
    await page.goto('/en');
  });

  test.describe('Theme', () => {
    test('should change to dark theme via dropdown', async ({ page }) => {
      const themeToggle = page.getByRole('button', {
        name: locators.themeToggleName,
      });

      await themeToggle.click();
      await page
        .getByRole('menuitem', { name: locators.themeDarkLabel })
        .click();

      expect(await getTheme(page)).toBe('dark');
    });

    test('should change to light theme via dropdown', async ({ page }) => {
      const themeToggle = page.getByRole('button', {
        name: locators.themeToggleName,
      });

      // Set dark first, then switch to light
      await themeToggle.click();
      await page
        .getByRole('menuitem', { name: locators.themeDarkLabel })
        .click();

      await themeToggle.click();
      await page
        .getByRole('menuitem', { name: locators.themeLightLabel })
        .click();

      expect(await getTheme(page)).toBe('light');
    });

    test('should persist theme across page navigation', async ({ page }) => {
      const themeToggle = page.getByRole('button', {
        name: locators.themeToggleName,
      });

      await themeToggle.click();
      await page
        .getByRole('menuitem', { name: locators.themeDarkLabel })
        .click();

      await page.reload();
      expect(await getTheme(page)).toBe('dark');
    });

    test('should respect system preference initially', async ({ browser }) => {
      const context = await browser.newContext({ colorScheme: 'dark' });
      const page = await context.newPage();

      await page.goto('/en');
      expect(await getTheme(page)).toBe('dark');

      await context.close();
    });
  });

  test.describe('Language', () => {
    test('should correctly translate UI elements according to language files', async ({
      page,
    }) => {
      await verifyTranslation(page, englishLocale);

      // Change to Spanish and verify translations
      const menu = await openLanguageMenu(page);
      await menu.getByText(/español/i).click();
      await page.waitForURL(/\/es$/);

      await verifyTranslation(page, 'es');
    });
  });

  test.describe('Navigation', () => {
    test('should have functioning mobile menu on small screens', async ({
      page,
    }) => {
      // Set mobile viewport size
      await page.setViewportSize({ width: 375, height: 667 });

      // Locate mobile menu toggle button and verify it's visible
      const mobileToggle = page.getByRole('button', {
        name: locators.mobileMenuToggleName,
      });
      await expect(mobileToggle).toBeVisible();

      const navLinks = page.locator(locators.navLinksLocator);

      // Toggle menu open and verify it's visible
      await mobileToggle.click();
      await expect(navLinks.first()).toBeVisible();

      // Toggle menu closed and verify it's hidden
      await mobileToggle.click();
      await expect(navLinks.first()).not.toBeVisible();
    });
  });
});
