import path from 'path';
import { fileURLToPath } from 'url';
import { createRequire } from 'module';
import fs from 'fs-extra';
import { globSync } from 'glob';
import which from 'which';
import os from 'os';
import { spawnSync, execSync } from 'child_process';
import { Browser, BrowserContext, chromium } from 'playwright';
import * as Sentry from '@sentry/node';
import { PlaywrightCrawler } from 'crawlee';
import { consoleLogger, silentLogger } from '../logs.js';
import { PageInfo } from '../mergeAxeResults.js';

const filename = fileURLToPath(import.meta.url);
const dirname = path.dirname(filename);
const require = createRequire(import.meta.url);

const maxRequestsPerCrawl = 100;

export const blackListedFileExtensions = [
  'css',
  'js',
  'txt',
  'mp3',
  'mp4',
  'jpg',
  'jpeg',
  'png',
  'svg',
  'gif',
  'woff',
  'woff2',
  'zip',
  'webp',
  'json',
  'xml',
  'ico',
  'bmp',
  'tiff',
  'tif',
  'avi',
  'mov',
  'wmv',
  'flv',
  'ogg',
  'wav',
  'doc',
  'docx',
  'xls',
  'xlsx',
  'ppt',
  'pptx',
  'apk',
];

export const getIntermediateScreenshotsPath = (datasetsPath: string): string =>
  `${datasetsPath}/screenshots`;
export const destinationPath = (storagePath: string): string => `${storagePath}/screenshots`;

/**  Get the path to Default Profile in the Chrome Data Directory
 * as per https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md
 * @returns path to Default Profile in the Chrome Data Directory
 */
export const getDefaultChromeDataDir = (): string => {
  try {
    // If GOOGLE_SAFE_BROWSING is set, use the pre-warmed profile prepared by Dockerfile
    if (process.env.GOOGLE_SAFE_BROWSING && fs.existsSync('/data/chrome-profile')) {
      try {
        fs.accessSync('/data/chrome-profile', fs.constants.W_OK);
        return '/data/chrome-profile';
      } catch {
        // Not writable — fall through to other options
      }
    }

    // Check for environment override (used when GSB profile is pre-warmed in Docker)
    if (process.env.OOBEE_CHROME_DATA_DIR && fs.existsSync(process.env.OOBEE_CHROME_DATA_DIR)) {
      return process.env.OOBEE_CHROME_DATA_DIR;
    }

    let defaultChromeDataDir = null;
    if (os.platform() === 'win32') {
      defaultChromeDataDir = path.join(
        os.homedir(),
        'AppData',
        'Local',
        'Google',
        'Chrome',
        'User Data',
      );
    } else if (os.platform() === 'darwin') {
      defaultChromeDataDir = path.join(
        os.homedir(),
        'Library',
        'Application Support',
        'Google',
        'Chrome',
      );
    }

    if (defaultChromeDataDir && fs.existsSync(defaultChromeDataDir)) {
      return defaultChromeDataDir;
    }

    // Linux: check if Chrome is installed; use same scratch dir pattern as Chromium
    if (os.platform() === 'linux') {
      const chromeExists = fs.existsSync('/usr/bin/google-chrome') || fs.existsSync('/usr/bin/google-chrome-stable');
      if (chromeExists) {
        let linuxChromeDataDir = path.join(process.cwd(), 'Chromium Support');
        try {
          fs.mkdirSync(linuxChromeDataDir, { recursive: true });
        } catch {
          linuxChromeDataDir = '/tmp';
        }
        // Create minimal Local State file so cloneChromeProfiles succeeds
        const localStatePath = path.join(linuxChromeDataDir, 'Local State');
        if (!fs.existsSync(localStatePath)) {
          fs.writeFileSync(localStatePath, JSON.stringify({ profile: { info_cache: {} } }));
        }
        return linuxChromeDataDir;
      }
    }

    return null;
  } catch (error) {
    console.error(`Error in getDefaultChromeDataDir(): ${error}`);
  }
};

/**
 * Get the path to Default Profile in the Edge Data Directory
 * @returns path to Default Profile in the Edge Data Directory
 */
export const getDefaultEdgeDataDir = (): string => {
  try {
    let defaultEdgeDataDir = null;
    if (os.platform() === 'win32') {
      defaultEdgeDataDir = path.join(
        os.homedir(),
        'AppData',
        'Local',
        'Microsoft',
        'Edge',
        'User Data',
      );
    } else if (os.platform() === 'darwin') {
      defaultEdgeDataDir = path.join(
        os.homedir(),
        'Library',
        'Application Support',
        'Microsoft Edge',
      );
    }

    if (defaultEdgeDataDir && fs.existsSync(defaultEdgeDataDir)) {
      return defaultEdgeDataDir;
    }
    return null;
  } catch (error) {
    console.error(`Error in getDefaultEdgeDataDir(): ${error}`);
  }
};

export const getDefaultChromiumDataDir = () => {
  try {
    let defaultChromiumDataDir = null;

    if (os.platform() === 'win32') {
      defaultChromiumDataDir = path.join(os.homedir(), 'AppData', 'Local', 'Chromium', 'User Data');
    } else if (os.platform() === 'darwin') {
      defaultChromiumDataDir = path.join(
        os.homedir(),
        'Library',
        'Application Support',
        'Chromium',
      );
    } else {
      defaultChromiumDataDir = path.join(process.cwd(), 'Chromium Support');

      try {
        fs.mkdirSync(defaultChromiumDataDir, { recursive: true }); // Use { recursive: true } to create parent directories if they don't exist
      } catch {
        defaultChromiumDataDir = '/tmp';
      }

      consoleLogger.info(`Using Chromium support directory at ${defaultChromiumDataDir}`);
    }

    if (defaultChromiumDataDir && fs.existsSync(defaultChromiumDataDir)) {
      return defaultChromiumDataDir;
    }
    return null;
  } catch (error) {
    consoleLogger.error(`Error in getDefaultChromiumDataDir(): ${error}`);
  }
};

export function removeQuarantineFlag(searchPattern: string, allowedRoot = process.cwd()) {
  if (os.platform() !== 'darwin') return;

  const matches = globSync(searchPattern, {
    absolute: true,
    nodir: true,
    dot: true,
    follow: false, // don't follow symlinks
  });

  const root = path.resolve(allowedRoot);

  for (const p of matches) {
    const resolved = path.resolve(p);

    // Ensure the file is under the allowed root (containment check)
    if (!resolved.startsWith(root + path.sep)) continue;

    // lstat: skip if not a regular file or if it's a symlink
    let st: fs.Stats;
    try {
      st = fs.lstatSync(resolved);
    } catch {
      continue;
    }
    if (!st.isFile() || st.isSymbolicLink()) continue;

    // basic filename sanity: no control chars
    const base = path.basename(resolved);
    if (/[\x00-\x1F]/.test(base)) continue;

    // Use absolute binary path and terminate options with "--"
    const proc = spawnSync('/usr/bin/xattr', ['-d', 'com.apple.quarantine', '--', resolved], {
      stdio: ['ignore', 'ignore', 'pipe'],
    });

    // Optional: inspect errors (common benign case is "No such xattr")
    if (proc.status !== 0) {
      const err = proc.stderr?.toString() || '';
      // swallow benign errors; otherwise log if you have a logger
      if (!/No such xattr/i.test(err)) {
        // console.warn(`xattr failed for ${resolved}: ${err.trim()}`);
      }
    }
  }
}

export const getExecutablePath = function (dir: string, file: string): string {
  let execPaths = globSync(`${dir}/${file}`, { absolute: true, nodir: true });

  if (execPaths.length === 0) {
    const execInPATH = which.sync(file, { nothrow: true });

    if (execInPATH) {
      return fs.realpathSync(execInPATH);
    }
    const splitPath =
      os.platform() === 'win32' ? process.env.PATH.split(';') : process.env.PATH.split(':');

    for (const path in splitPath) {
      execPaths = globSync(`${path}/${file}`, { absolute: true, nodir: true });
      if (execPaths.length !== 0) return fs.realpathSync(execPaths[0]);
    }
    return null;
  }
  removeQuarantineFlag(execPaths[0]);
  return execPaths[0];
};

/**
 * Matches the pattern user:password@domain.com
 */
export const basicAuthRegex = /^.*\/\/.*:.*@.*$/i;

// for crawlers
export const axeScript = require.resolve('axe-core/axe.min.js');
export class UrlsCrawled {
  siteName: string;
  toScan: string[] = [];
  scanned: PageInfo[] = [];
  invalid: PageInfo[] = [];
  scannedRedirects: { fromUrl: string; toUrl: string }[] = [];
  notScannedRedirects: { fromUrl: string; toUrl: string }[] = [];
  outOfDomain: PageInfo[] = [];
  blacklisted: PageInfo[] = [];
  error: PageInfo[] = [];
  exceededRequests: PageInfo[] = [];
  forbidden: PageInfo[] = [];
  userExcluded: PageInfo[] = [];
  everything: string[] = [];

  constructor(urlsCrawled?: Partial<UrlsCrawled>) {
    if (urlsCrawled) {
      Object.assign(this, urlsCrawled);
    }
  }
}

const urlsCrawledObj = new UrlsCrawled();

/* eslint-disable no-unused-vars */
export enum ScannerTypes {
  SITEMAP = 'Sitemap',
  WEBSITE = 'Website',
  CUSTOM = 'Custom',
  INTELLIGENT = 'Intelligent',
  LOCALFILE = 'LocalFile',
}
/* eslint-enable no-unused-vars */

export enum FileTypes {
  All = 'all',
  PdfOnly = 'pdf-only',
  HtmlOnly = 'html-only',
}

export function getEnumKey<E extends Record<string, string>>(
  enumObj: E,
  value: string,
): keyof E | undefined {
  return (Object.keys(enumObj) as Array<keyof E>).find(k => enumObj[k] === value);
}

export const guiInfoStatusTypes = {
  SCANNED: 'scanned',
  SKIPPED: 'skipped',
  COMPLETED: 'completed',
  ERROR: 'error',
  DUPLICATE: 'duplicate',
};

let launchOptionsArgs: string[] = [];

// Check if running in docker container
if (fs.existsSync('/.dockerenv')) {
  launchOptionsArgs = ['--disable-gpu', '--disable-dev-shm-usage', '--no-zygote'];
}

export const impactOrder = {
  minor: 0,
  moderate: 1,
  serious: 2,
  critical: 3,
};

/**
 * Suppresses the "Setting the NODE_TLS_REJECT_UNAUTHORIZED
 * environment variable to '0' is insecure" warning,
 * then disables TLS validation globally.
 */
export function suppressTlsRejectWarning(): void {
  // Monkey-patch process.emitWarning
  const originalEmitWarning = process.emitWarning;
  process.emitWarning = (warning: string | Error, ...args: any[]) => {
    const msg = typeof warning === 'string' ? warning : warning.message;
    if (msg.includes('NODE_TLS_REJECT_UNAUTHORIZED')) {
      // swallow only that one warning
      return;
    }
    // forward everything else
    originalEmitWarning.call(process, warning, ...args);
  };

  // Now turn off cert validation
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}

suppressTlsRejectWarning();

export const sentryConfig = {
  dsn:
    process.env.OOBEE_SENTRY_DSN ||
    'https://3b8c7ee46b06f33815a1301b6713ebc3@o4509047624761344.ingest.us.sentry.io/4509327783559168',
  tracesSampleRate: 1.0, // Capture 100% of transactions for performance monitoring
  profilesSampleRate: 1.0, // Capture 100% of profiles
};

// Function to set Sentry user ID from userData.txt
export const setSentryUser = (userId: string) => {
  if (userId) {
    Sentry.setUser({ id: userId });
  }
};

// Legacy code start - Google Sheets submission
export const formDataFields = {
  formUrl: `https://docs.google.com/forms/d/e/1FAIpQLSem5C8fyNs5TiU5Vv2Y63-SH7CHN86f-LEPxeN_1u_ldUbgUA/formResponse`, // prod
  entryUrlField: 'entry.1562345227',
  redirectUrlField: 'entry.473072563',
  scanTypeField: 'entry.1148680657',
  emailField: 'entry.52161304',
  nameField: 'entry.1787318910',
  resultsField: 'entry.904051439',
  numberOfPagesScannedField: 'entry.238043773',
  additionalPageDataField: 'entry.2090887881',
  metadataField: 'entry.1027769131',
};
// Legacy code end - Google Sheets submission

export const sitemapPaths = [
  '/sitemap.xml',
  '/sitemap/sitemap.xml',
  '/sitemap-index.xml',
  '/sitemap_index.xml',
  '/sitemapindex.xml',
  '/sitemap/index.xml',
  '/sitemap1.xml',
  '/sitemap/',
  '/post-sitemap',
  '/page-sitemap',
  '/sitemap.txt',
  '/sitemap.php',
  '/sitemap.xml.bz2',
  '/sitemap.xml.xz',
  '/sitemap_index.xml.bz2',
  '/sitemap_index.xml.xz',
];

// Remember to update getWcagPassPercentage() in src/utils/utils.ts if you change this
const wcagLinks = {
  'WCAG 1.1.1': 'https://www.w3.org/TR/WCAG22/#non-text-content',
  'WCAG 1.2.2': 'https://www.w3.org/TR/WCAG22/#captions-prerecorded',
  'WCAG 1.3.1': 'https://www.w3.org/TR/WCAG22/#info-and-relationships',
  // 'WCAG 1.3.4': 'https://www.w3.org/TR/WCAG22/#orientation', - TODO: review for veraPDF
  'WCAG 1.3.5': 'https://www.w3.org/TR/WCAG22/#identify-input-purpose',
  'WCAG 1.4.1': 'https://www.w3.org/TR/WCAG22/#use-of-color',
  'WCAG 1.4.2': 'https://www.w3.org/TR/WCAG22/#audio-control',
  'WCAG 1.4.3': 'https://www.w3.org/TR/WCAG22/#contrast-minimum',
  'WCAG 1.4.4': 'https://www.w3.org/TR/WCAG22/#resize-text',
  'WCAG 1.4.6': 'https://www.w3.org/TR/WCAG22/#contrast-enhanced', // AAA
  // 'WCAG 1.4.10': 'https://www.w3.org/TR/WCAG22/#reflow', - TODO: review for veraPDF
  'WCAG 1.4.12': 'https://www.w3.org/TR/WCAG22/#text-spacing',
  'WCAG 2.1.1': 'https://www.w3.org/TR/WCAG22/#keyboard',
  'WCAG 2.1.3': 'https://www.w3.org/WAI/WCAG22/Understanding/keyboard-no-exception.html', // AAA
  'WCAG 2.2.1': 'https://www.w3.org/TR/WCAG22/#timing-adjustable',
  'WCAG 2.2.2': 'https://www.w3.org/TR/WCAG22/#pause-stop-hide',
  'WCAG 2.2.4': 'https://www.w3.org/TR/WCAG22/#interruptions', // AAA
  'WCAG 2.4.1': 'https://www.w3.org/TR/WCAG22/#bypass-blocks',
  'WCAG 2.4.2': 'https://www.w3.org/TR/WCAG22/#page-titled',
  'WCAG 2.4.4': 'https://www.w3.org/TR/WCAG22/#link-purpose-in-context',
  'WCAG 2.4.9': 'https://www.w3.org/TR/WCAG22/#link-purpose-link-only', // AAA
  'WCAG 2.5.8': 'https://www.w3.org/TR/WCAG22/#target-size-minimum',
  'WCAG 3.1.1': 'https://www.w3.org/TR/WCAG22/#language-of-page',
  'WCAG 3.1.2': 'https://www.w3.org/TR/WCAG22/#language-of-parts',
  'WCAG 3.1.5': 'https://www.w3.org/TR/WCAG22/#reading-level', // AAA
  'WCAG 3.2.5': 'https://www.w3.org/TR/WCAG22/#change-on-request', // AAA
  'WCAG 3.3.2': 'https://www.w3.org/TR/WCAG22/#labels-or-instructions',
  'WCAG 4.1.2': 'https://www.w3.org/TR/WCAG22/#name-role-value',
};

export const wcagCriteriaLabels: Record<string, string> = {
  'WCAG 1.1.1': 'A',
  'WCAG 1.2.2': 'A',
  'WCAG 1.3.1': 'A',
  'WCAG 1.3.5': 'AA',
  'WCAG 1.4.1': 'A',
  'WCAG 1.4.2': 'A',
  'WCAG 1.4.3': 'AA',
  'WCAG 1.4.4': 'AA',
  'WCAG 1.4.6': 'AAA',
  'WCAG 1.4.12': 'AA',
  'WCAG 2.1.1': 'A',
  'WCAG 2.1.3': 'AAA',
  'WCAG 2.2.1': 'A',
  'WCAG 2.2.2': 'A',
  'WCAG 2.2.4': 'AAA',
  'WCAG 2.4.1': 'A',
  'WCAG 2.4.2': 'A',
  'WCAG 2.4.4': 'A',
  'WCAG 2.4.9': 'AAA',
  'WCAG 2.5.8': 'AA',
  'WCAG 3.1.1': 'A',
  'WCAG 3.1.2': 'AA',
  'WCAG 3.1.5': 'AAA',
  'WCAG 3.2.5': 'AAA',
  'WCAG 3.3.2': 'A',
  'WCAG 4.1.2': 'A',
};

/**
 * Format a numeric WCAG criterion tag to a human-readable string.
 * Mirrors the identically-named function in utils.ejs (single source of truth
 * for server-side TypeScript; the EJS version remains for template use).
 *
 * wcag111  → "WCAG 1.1.1"
 * wcag143  → "WCAG 1.4.3"
 * wcag1412 → "WCAG 1.4.12"
 *
 * Non-numeric tags (e.g. wcag2a, best-practice) are returned unchanged.
 * NOTE: Uses string concatenation so Function.toString() embeds safely in
 * backtick template strings inside generateOobeeClientScanner.ts.
 */
export function formatWcagId(wcag: string): string {
  if (!wcag) return '';
  const numbers = wcag.replace('wcag', '').split('');
  if (numbers.length === 3) return 'WCAG ' + numbers[0] + '.' + numbers[1] + '.' + numbers[2];
  if (numbers.length === 4) return 'WCAG ' + numbers[0] + '.' + numbers[1] + '.' + numbers[2] + numbers[3];
  if (numbers.length === 5) return 'WCAG ' + numbers[0] + '.' + numbers[1] + '.' + numbers.slice(2).join('');
  return wcag;
}

const urlCheckStatuses = {
  success: { code: 0 },
  invalidUrl: { code: 11, message: 'Invalid URL. Please check and try again.' },
  cannotBeResolved: {
    code: 12,
    message: 'URL cannot be accessed. Please verify whether the website exists.',
  },
  errorStatusReceived: {
    // unused for now
    code: 13,
    message: 'Provided URL cannot be accessed. Server responded with code ', // append it with the response code received,
  },
  systemError: { code: 14, message: 'Something went wrong when verifying the URL. Please try again in a few minutes. If this issue persists, please contact the Oobee team.'},
  notASitemap: { code: 15, message: 'Invalid sitemap URL format. Please enter a valid sitemap URL ending with .XML or .TXT e.g. https://www.example.com/sitemap.xml.' },
  unauthorised: { code: 16, message: 'Login required. Please enter your credentials and try again.' },
  // browserError means engine could not find a browser to run the scan
  browserError: {
    code: 17,
    message: 'Incompatible browser. Please ensure you are using Chrome or Edge browser.',
  },
  sslProtocolError: {
    code: 18,
    message:
      'SSL certificate  error. Please check the SSL configuration of your website and try again.',
  },
  notALocalFile: {
    code: 19,
    message: 'Uploaded file format is incorrect. Please upload a HTML, PDF, XML or TXT file.',
  },
  notAPdf: { code: 20, message: 'URL/file format is incorrect. Please upload a PDF file.' },
  notASupportedDocument: {
    code: 21,
    message: 'Uploaded file format is incorrect. Please upload a HTML, PDF, XML or TXT file.',
  },
  connectionRefused: {
    code: 22,
    message:
      'Connection refused. Please try again in a few minutes. If this issue persists, please contact the Oobee team.',
  },
  timedOut: {
    code: 23,
    message:
      'Request timed out. Please try again in a few minutes. If this issue persists, please contact the Oobee team.',
  },
  blockedByClient: {
    code: 24,
    message:
      'Something went wrong when verifying the URL. If this issue persists, please contact the Oobee team.',
  },
};

/* eslint-disable no-unused-vars */
export enum BrowserTypes {
  CHROMIUM = 'chromium',
  CHROME = 'chrome',
  EDGE = 'msedge',
}
/* eslint-enable no-unused-vars */

const xmlSitemapTypes = {
  xml: 0,
  xmlIndex: 1,
  rss: 2,
  atom: 3,
  unknown: 4,
};

const forbiddenCharactersInDirPath = ['<', '>', ':', '"', '\\', '/', '|', '?', '*'];

const reserveFileNameKeywords = [
  'CON',
  'PRN',
  'AUX',
  'NUL',
  'COM1',
  'COM2',
  'COM3',
  'COM4',
  'COM5',
  'COM6',
  'COM7',
  'COM8',
  'COM9',
  'LPT1',
  'LPT2',
  'LPT3',
  'LPT4',
  'LPT5',
  'LPT6',
  'LPT7',
  'LPT8',
  'LPT9',
];

export const a11yRuleShortDescriptionMap = {
  'aria-meter-name': 'Meter elements need accessible labels',
  'aria-progressbar-name': 'Progress bars need accessible labels',
  'image-alt': 'Meaningful images need text descriptions',
  'input-image-alt': 'Image buttons need action labels',
  'object-alt': 'Embedded objects need identifying labels',
  'oobee-confusing-alt-text': 'Replace vague image descriptions with meaningful text',
  'role-img-alt': 'Elements marked as images need text descriptions',
  'svg-img-alt': 'Vector graphics marked as images need text descriptions',
  'video-caption': 'Videos need captions with transcript tracks',
  'aria-required-children': 'ARIA roles must contain their required child elements',
  'aria-required-parent': 'ARIA roles must be contained within their required parent elements',
  'definition-list': 'Glossaries must use proper term and definition structure',
  dlitem: 'Term and definition elements must be contained in definition lists',
  list: 'Bullet and numbered lists must only contain list items as direct children',
  listitem: 'List items must be placed inside a list container',
  'td-headers-attr': 'Table headers must clearly identify their relationship to cells',
  'th-has-data-cells': 'Table headers must be connected to their data cells',
  'autocomplete-valid': 'Form fields must use valid autocomplete attributes',
  'link-in-text-block': 'Links must be visually distinct beyond color alone',
  'avoid-inline-spacing': 'Page layouts must allow users to adjust text spacing',
  'no-autoplay-audio': 'Pages must not auto-play audio or must allow control',
  'color-contrast': 'Text and background colors must meet minimum contrast requirements',
  'color-contrast-enhanced': 'Text and background colors must meet enhanced contrast requirements',
  'frame-focusable-content':
    'Frames and iframes with interactive content must be keyboard accessible',
  'server-side-image-map': 'Replace server-side image maps with client-side image maps',
  'scrollable-region-focusable': 'Elements within scrollable regions must be keyboard accessible',
  'oobee-accessible-label': 'Clickable elements must have accessible labels',
  'meta-refresh': 'Pages must not use timed automatic refresh',
  blink: 'Blinking elements must not be used',
  marquee: 'Marquee animated elements must not be used',
  'meta-refresh-no-exceptions': 'Pages must not use automatic timed refresh',
  bypass: 'Pages must provide a way to bypass repeated blocks',
  'document-title': 'Every page must have a descriptive title',
  'link-name': 'Links must have descriptive accessible labels',
  'area-alt': 'Clickable areas in image maps must have labels',
  'identical-links-same-purpose':
    'Links with identical text must have accessible labels describing their purpose',
  'target-size': 'Clickable elements must be large enough or have sufficient spacing',
  'html-has-lang': 'Every page must declare its language',
  'html-lang-valid': 'Page language declaration must use valid language codes',
  'html-xml-lang-mismatch': 'Make different page language settings match',
  'valid-lang': 'Elements in different languages must use valid language codes',
  'oobee-grading-text-contents': 'Page content must use clear, plain language',
  'form-field-multiple-labels': 'Form fields must have only one label element',
  'aria-allowed-attr': 'ARIA attributes must be used with appropriate roles',
  'aria-braille-equivalent': 'Braille abbreviated labels must have full text equivalents',
  'aria-command-name': 'Elements that use ARIA labels must have an accessible name.',
  'aria-conditional-attr': 'ARIA attributes must not create conflicting or indeterminate states',
  'aria-deprecated-role': 'Remove outdated accessibility (ARIA) roles',
  'aria-hidden-body': 'The page body must not be hidden from screen readers',
  'aria-hidden-focus': 'Hidden elements must not contain keyboard-focusable content',
  'aria-input-field-name': 'Custom input fields must have accessible labels',
  'aria-prohibited-attr': 'Remove ARIA attributes not allowed on these elements',
  'aria-required-attr': 'Add required ARIA attributes for accessibility roles',
  'aria-roles': 'Elements must use valid, supported accessibility roles',
  'aria-toggle-field-name':
    'Toggle switches, checkboxes and radio buttons must have descriptive labels',
  'aria-tooltip-name': 'Tooltips must have accessible names',
  'aria-valid-attr': 'ARIA attributes must use correct syntax and valid names',
  'aria-valid-attr-value': 'ARIA attributes must use valid values',
  'button-name': 'Buttons must have descriptive text or labels',
  'duplicate-id-aria': 'Element IDs must be unique on the page',
  'frame-title': 'Frames and iframes must have descriptive titles',
  'frame-title-unique': 'Each frame must have a unique, descriptive title',
  'input-button-name': 'Input buttons must have descriptive text or values',
  label: 'Form fields must have associated labels',
  'nested-interactive': 'Interactive elements must not be nested inside each other',
  'select-name': 'Selected dropdowns must have associated labels',
  accesskeys: 'Custom keyboard shortcuts must be unique',
  'aria-dialog-name': 'Dialog popups must have descriptive titles',
  'aria-text': 'Text elements must not contain focusable content',
  'aria-treeitem-name': 'Tree view items must have accessible names',
  'empty-heading': 'Headings must contain descriptive text and not be hidden',
  'empty-table-header': 'Table headers must contain descriptive text',
  'frame-tested': 'Frames and iframes must be tested for accessibility',
  'heading-order': 'Heading levels must follow logical order',
  'image-redundant-alt': 'Image descriptions must not repeat surrounding text',
  'label-title-only': 'Form fields should have visible labels',
  'landmark-banner-is-top-level':
    "Header region or banner elements must be at the page's top level",
  'landmark-complementary-is-top-level':
    "Sidebar/complementary region must be at the page's top level",
  'landmark-contentinfo-is-top-level': "Footer region must be at the page's top level",
  'landmark-main-is-top-level': "Main content region must be at the page's top level",
  'landmark-no-duplicate-banner': 'Pages must have only one header region',
  'landmark-no-duplicate-contentinfo': 'Pages must have only one footer region',
  'landmark-no-duplicate-main': 'Pages must have only one main content region',
  'landmark-one-main': 'Every page must have a main content region',
  'landmark-unique': 'Page landmarks must be unique or clearly distinguished',
  'meta-viewport-large': 'Pages must allow zoom and scaling',
  'page-has-heading-one': 'Every page must have one main H1 heading',
  'presentation-role-conflict': 'Decorative elements must not be interactive or focusable',
  region: 'All page content must be within marked landmarks or regions',
  'scope-attr-valid': 'Table header scope attributes must be correct',
  'skip-link': 'Skip links must have valid, reachable targets',
  tabindex: 'Elements must not have positive tabindex values',
  'table-duplicate-name': 'Table caption and summary must not be identical',
  'meta-viewport': 'Pages must allow zoom and text scaling',
  'aria-allowed-role': 'Elements must use appropriate roles matching their actual behavior',
  'summary-name': 'Summary must have discernible text'
};

export const a11yRuleLongDescriptionMap = {
  'aria-meter-name':
    'Meters are visual indicators that show measurements (like how much storage is used) and need text labels. This helps people using screen readers understand what the meter is tracking.',
  'aria-progressbar-name':
    "Progress bars are visual indicators showing completion status and need clear labels describing what's being loaded or processed. This helps people using screen readers know what progress they're watching.",
  'image-alt':
    'Meaningful images (photos, charts, diagrams and other visuals) that communicate important information need text descriptions (called "alt text"). This helps people using screen readers understand what the image shows instead of just hearing/reading out as "image".',
  'input-image-alt':
    'When a button uses only an image instead of text, that image needs a label that describes the button\'s action (called an "accessible name"). e.g., a delete button with a trash can icon should be labeled "Delete" not just "trash can". This helps people using screen readers know what action the button performs.',
  'object-alt':
    'Embedded content, such as PDFs, videos, interactive maps, or other objects need a label that identifies what it is (called an "accessible name"). This helps people using screen readers understand what the object is and what it does. e.g., "View the 2024 annual report (PDF)" or "Video: Company overview (3 minutes)."',
  'oobee-confusing-alt-text':
    'Images that already have alt text (text descriptions for images) but use vague words like "image" "photo", need to be rewritten with actual descriptions of what the image shows. e.g., instead of alt text that says "photo," it should describe what the photo shows: "Team members at the 2024 conference".',
  'role-img-alt':
    'When design elements are marked with image role (a technical way to treat elements as images), they need text descriptions (called "accessible names"). This helps people using screen readers understand what each element represents. e.g., an icon marked as an image needs a description like "Settings icon" not just "image".',
  'svg-img-alt':
    'Vector graphics (scalable graphics created with code called SVGs), that are marked with image role (treated as images) need text descriptions (called "accessible names"). This helps people using screen readers understand what the graphic represents. e.g., an SVG logo should be labeled "Company logo".',
  'video-caption':
    'Videos need captions that show what people are saying and important sounds (captions provided through <track> elements in HTML). This helps people who are deaf or hard of hearing understand video content. Captions should be synchronized with the video and readable.',
  'aria-required-children':
    'Certain accessibility roles (ARIA roles, attributes that tell screen readers what type of element something is) require specific child elements nested inside them to work correctly. e.g., a menu role should contain "menu item" elements inside it. Without the proper child elements, screen readers cannot interpret the structure and the control won\'t work as intended.',
  'aria-required-parent':
    'Certain accessibility roles (ARIA roles, attributes that tell screen readers what type of element something is) require specific parent elements to contain them. e.g., a tab element should be inside a "tab list" parent. When a role is outside its required parent, screen readers cannot understand the relationship and structure, breaking the functionality.',
  'definition-list':
    'Glossaries and FAQs that pair terms with definitions must use proper structure (called a definition list). This means only term and definition elements should be direct children—no other content mixed in directly. This helps screen readers announce which definitions belong to which terms.',
  dlitem:
    'Terms and their definitions must always be grouped inside a definition list (a special structure for glossaries and FAQs). When they appear outside this structure, screen readers cannot understand they are related.',
  list: "When you create a bullet list or numbered list, only list item elements should be immediate children of the list container. This structure helps screen readers announce the list properly and count items correctly. (Note: list items themselves can contain other content like paragraphs, links, or formatting—that's allowed.)",
  listitem:
    'List item elements should only exist inside a list container (bullet list or numbered list). When list items appear outside a list container, screen readers cannot understand they are part of a list, breaking the list structure.',
  'td-headers-attr':
    'Table headers must clearly identify their purpose in relation to the cells they describe, whether they are column headers or row headers. e.g, a column header might be "Revenue" and a row header might be "Q1". Without clear header relationships, screen readers cannot help users understand what data they\'re reading.',
  'th-has-data-cells':
    'Table headers must be correctly labeled and connected to the data cells they describe. This relationship helps screen reader users understand which header applies to which data cell.',
  'autocomplete-valid':
    'Form fields need correct autocomplete attributes (coded hints that tell browsers what type of information goes in each field). When autocomplete attributes follow the specification, browsers can prefill information correctly. This helps people with cognitive disabilities and slow typists.',
  'link-in-text-block':
    'Links must look different from regular text in ways other than just color (like underlining or special styling). This helps people with color blindness and low vision identify which text is clickable.',
  'avoid-inline-spacing':
    "Users should be able to adjust text spacing in their browser settings (spacing is measured in units like ems, not percentages). When CSS styles don't have fixed line-spacing values, users with low vision can increase spacing to read comfortably. This helps people who need wider spacing to read without losing content.",
  'no-autoplay-audio':
    'Pages with audio or video must not auto-play sound when the page loads, unless the sound is very brief (3 seconds or less). Audio that auto-plays longer than 3 seconds must have clear pause/stop controls. This helps people with hearing aids, those who use multiple tabs, and those who need to focus on reading.',
  'color-contrast':
    'Text and background colors need enough contrast ratio (AA level—the baseline accessibility requirement) to be readable. This helps people with low vision see text clearly and read without strain.',
  'color-contrast-enhanced':
    'For enhanced accessibility, text and background colors should meet AAA level contrast (higher than the baseline AA requirement). This provides very high contrast and helps people with low vision see text with minimal strain.',
  'frame-focusable-content':
    'Frames and iframes that contain interactive content need to be accessible via keyboard. When users navigate using Tab, they should be able to reach and interact with content inside the frame. This helps people who navigate only with keyboards.',
  'server-side-image-map':
    "Image maps that use server-side clicking (where the server determines what was clicked based on coordinates) don't work with keyboard navigation. Replace them with client-side image maps (HTML-based maps) so everyone can use them via keyboard or any input method.",
  'scrollable-region-focusable':
    "Scrollable sections that contain interactive elements need to be accessible by keyboard and screen reader. Users should be able to scroll using the keyboard or a screen reader to reach/read all contents inside the scrollable regions. This helps people who can't use a mouse or those using screen readers.",
  'oobee-accessible-label':
    'Clickable elements (buttons, links, etc) need clear, accessible labels that describe what will happen when clicked. This helps screen reader users understand the purpose of each clickable element.',
  'meta-refresh':
    'Pages should not automatically refresh using timed refresh (meta refresh with delays under 20 hours). Automatic page refreshes interrupt users while reading and frustrate those trying to focus on content. If refresh is necessary, users should control it with a button or link.',
  blink:
    'Blinking or flashing text should not be used. This helps people with motion sensitivity, seizure disorders, and those who find flashing content distracting or disorienting.',
  marquee:
    'Scrolling or animated text (marquee elements) should not be used. Moving text is difficult to read and causes problems for people with attention disorders, motion sensitivities, or those with low vision. Content should be static or controlled by the user.',
  'meta-refresh-no-exceptions':
    'Pages must not automatically refresh using meta refresh or similar timed mechanisms. Automatic page refreshes interrupt users reading or using the page, and especially frustrate people with attention disabilities or those trying to focus. If page updates are needed, users should have control.',
  bypass:
    'Pages must provide a way for users to bypass repeated content blocks (e.g. navbars, sidebars, main, headings, footers). One common way to do this is through skip links. However, pages must also have a main landmark (a marked main content area) so screen readers and keyboard users can jump directly to the primary content. This helps users navigate pages more efficiently.',
  'document-title':
    "Every page needs a unique, descriptive title that appears in the browser tab and is read first by screen readers. The title should help users understand what page they're on. This is especially important for people using screen readers who rely on the page title to understand context.",
  'link-name':
    "Links need clear, descriptive text or labels that explain where the link goes or what it does. This helps screen reader users understand the link's purpose without reading surrounding context. Links should have an accessible name (either visible text or a programmatic label).",
  'area-alt':
    'Image maps (images where different clickable regions have different links or actions) must have text labels for each clickable area (called alt text on area elements). Each clickable region should have a descriptive label explaining where it links or what happens when clicked. This helps screen reader users understand what each area does without relying on the image.',
  'identical-links-same-purpose':
    'When links use the same text but go to different destinations, they need additional accessible labels (like aria-label attributes) to distinguish them. This helps screen reader users understand the purpose of each link when they see the same text repeated.',
  'target-size':
    'Clickable elements (buttons, links, form fields, etc) need to be at least 24 pixels in size or have adequate spacing between them. This helps people with mobility issues and those using mobile devices to accurately tap or click without missing or accidentally clicking the wrong element.',
  'html-has-lang':
    'Every page (and any frames or iframes within it) must declare its primary language using a language attribute (lang). This helps screen readers pronounce text with the correct accent and pronunciation, and helps translation tools work correctly.',
  'html-lang-valid':
    'The language declared on the page must use a valid ISO language code (like "en" for English, "fr" for French). Invalid or nonstandard language codes prevent screen readers and translation tools from working correctly.',
  'html-xml-lang-mismatch':
    'Language declarations using different formats (HTML and XML) need to match. If they disagree (e.g., lang="en" and xml:lang="fr" on the same element), screen readers and translation tools become confused about the content language.',
  'valid-lang':
    'When parts of a page use different languages (like a Spanish quote in an English article), those elements must be tagged with valid language codes. Invalid language codes prevent screen readers from switching to the correct pronunciation for that language.',
  'oobee-grading-text-contents':
    'Text on the page should be clear and use simple language. This helps people with cognitive disabilities and non-native speakers understand content. Avoid jargon, long complex sentences, and unclear references.',
  'form-field-multiple-labels':
    "Form fields should only have one label element associated with them. Multiple label elements cause screen readers to announce conflicting information and confuse users about the field's purpose.",
  'aria-allowed-attr':
    "ARIA attributes (accessibility attributes) must be used correctly with elements that support them. Using unsupported ARIA attributes on elements creates conflicting or incorrect screen reader announcements. This prevents users from understanding the element's purpose.",
  'aria-braille-equivalent':
    'When braille-specific abbreviated text is used as a label (like using aria-label="vol" for "volume"), a full text equivalent must also be provided. This ensures non-braille screen reader users and braille display users both understand the label correctly.',
  'aria-command-name':
    'Interactive command elements like role="button", role="link", must have clear, accessible labels. Labels can be visible text, aria-label attributes, or title attributes. Without labels, screen reader users don\'t know what each command does or where links go.',
  'aria-conditional-attr':
    'When ARIA attributes (accessibility attributes) are used on an element, they should not conflict with what the element actually does. Conflicting attributes create confusion about what the element is or what will happen when clicked. e.g., a checkbox is not checked but is aria-checked=true, conflicts for screen reader vs visual readers.',
  'aria-deprecated-role':
    'Some accessibility roles (ARIA roles—code attributes that tell screen readers what type of element something is) are outdated and no longer recommended. Using current, supported roles ensures screen readers announce elements correctly. Outdated roles may cause screen readers to announce elements incorrectly or not at all.',
  'aria-hidden-body':
    'The main page content (the body element) cannot be marked as hidden from screen readers (using aria-hidden="true"). Hiding the page body makes the entire page inaccessible to screen reader users. This is a critical error that breaks accessibility completely.',
  'aria-hidden-focus':
    'Elements marked as hidden from screen readers (aria-hidden="true") should not contain interactive elements like buttons, links, or form fields that can receive keyboard focus. If hidden content is focusable, keyboard users can tab into it but won\'t hear what it is, becoming confused or stuck.',
  'aria-input-field-name':
    "Custom input fields (created with code to look like text boxes, dropdowns etc) must have accessible labels that describe what information should be entered. Without labels, screen reader users don't know what to type.",
  'aria-prohibited-attr':
    "Certain ARIA attributes (accessibility attributes) are only allowed on specific element types. Using prohibited attributes on the wrong elements causes screen readers to become confused about the element's behavior. This creates conflicting or ignored announcements.",
  'aria-required-attr':
    "Certain accessibility roles require specific attributes to work correctly. e.g., a slider role needs aria-valuemin, aria-valuemax, and aria-valuenow to function properly. Without required attributes, screen readers cannot announce the element's current state or allow users to interact with it correctly.",
  'aria-roles':
    'Elements must use valid ARIA roles from the official list. Invalid, misspelled, or unsupported role names confuse screen readers and prevent them from announcing elements correctly. This causes screen reader users to misunderstand what elements do.',
  'aria-toggle-field-name':
    'Toggle switches and custom checkbox / radio button controls need clear labels that describe what is being toggled. e.g., a toggle should be labeled "Dark mode", not just "Toggle". This helps screen reader users understand what will change when they activate it.',
  'aria-tooltip-name':
    "Tooltips must have clear, accessible names. The name should describe what happens when the associated control is activated. This helps screen reader users understand a button's purpose before clicking.",
  'aria-valid-attr':
    'ARIA attributes must be spelled correctly and use valid, documented names. Misspelled or unsupported attribute names are ignored by screen readers, causing missing or incorrect announcements. e.g., "aria-labell" (misspelled) won\'t work; it must be "aria-label".',
  'aria-valid-attr-value':
    'ARIA attributes need valid values from the official specification. Using invalid values (like misspelled or unsupported values) prevents screen readers from interpreting the attribute correctly. e.g., aria-pressed must use "true" or "false", not "yes" or "no".',
  'button-name':
    "Every button must have descriptive text that explains what happens when clicked. This can be visible text inside the button, or a programmatic label (like aria-label or title attribute). Without clear text, screen reader users don't know what the button does.",
  'duplicate-id-aria':
    'Every HTML ID on a page must be unique. When the same ID is used multiple times, it breaks connections between labels and form fields, and confuses accessibility tools. For example, if two form fields both have id="email", a label pointing to one won\'t work correctly.',
  'frame-title':
    'Every frame or iframe (embedded content like maps, videos, widgets etc) must have a descriptive title attribute. The title helps screen reader users understand what content is in the frame before entering it.',
  'frame-title-unique':
    'When a page has multiple frames or iframes, each must have a unique title. If multiple frames share the same title, screen reader users cannot distinguish between them. e.g., a page with two maps needs titles like "Store locations map" and "Service area map"—not both "Map".',
  'input-button-name':
    'Buttons created using HTML input elements (like <input type="button">) must have descriptive text. This can be the value attribute for submit/button types, or alt text for image buttons. Screen reader users need to know what the button does.',
  label:
    "Every form field (text input, checkbox etc) needs a label that describes what information should be entered. Labels can be visible text associated with the field, or programmatic labels (aria-label). Without labels, screen reader users don't know what the field is for.",
  'nested-interactive':
    'Buttons, links, and other interactive elements should not be nested inside one another. e.g., a link should not contain a button, and a button should not contain a link. Nested interactive elements confuse screen readers about which element is clickable and create unexpected keyboard behavior.',
  'select-name':
    "Selected dropdowns (HTML <select> elements) must have labels that describe what choice the dropdown controls. Without labels, screen reader users don't know what selections they're making. Labels can be visible text or programmatic labels.",
  accesskeys:
    'Custom keyboard shortcuts (accesskey attributes) must be unique across the page. Duplicate or conflicting access keys cause unexpected behavior when users try to use them. Additionally, access keys should not conflict with browser (like Ctrl+S), screen reader, or system shortcuts.',
  'aria-dialog-name':
    'Dialog boxes and modal popups must have accessible names (titles) that describe their purpose. When a dialog opens, screen reader users should hear what the dialog is for. This can be visible text at the top of the dialog or an aria-label attribute.',
  'aria-text':
    'Elements marked with role="text" (indicating non-interactive text) should not contain interactive elements like buttons, links, or form fields. If elements marked as role="text" contains focusable elements, keyboard and screen reader users become confused about what they can interact with when they tab through the page.',
  'aria-treeitem-name':
    'Items in tree structures (e.g., navigation tree) or expandable lists (e.g., file explorer) must have clear, accessible names that describe each item. Without names, screen reader users cannot distinguish between different tree items or understand what each represents.',
  'empty-heading':
    'Headings must not be empty or marked hidden. Every heading should have text that describes the section it introduces. Empty headings confuse screen reader users and break the document structure.',
  'empty-table-header':
    'Table header cells (<th> elements) must contain text that describes the column or row. Empty headers make tables unreadable for screen reader users who cannot see the visual layout to infer what each column represents.',
  'frame-tested':
    'All frames and iframes on a page should be tested with accessibility scanning tools (Oobee) to ensure embedded content is accessible. Testing tools need access to frame content to identify issues. Without testing frames, accessibility problems inside them may be missed.',
  'heading-order':
    "Headings must follow a logical, hierarchical order: H1 (page title), then H2 (main sections), then H3 (subsections), etc. Headings should increase by only one level at a time. e.g., you shouldn't jump from H1 directly to H3. This helps screen reader users understand the page structure and navigate it correctly.",
  'image-redundant-alt':
    "When an image's alt text repeats text already visible on the page, screen reader users hear the same information twice—once as text, once as alt text. Alt text should provide new or clarifying information, not duplicate existing text. If an image is purely decorative or just illustrates text already present, its alt can be empty.",
  'label-title-only':
    'Form fields need visible text labels next to them, not just hidden labels or tooltips that only appear on hover. Visible labels help all users (screen reader users and sighted users) understand what to enter. Placeholders and hidden labels are not sufficient.',
  'landmark-banner-is-top-level':
    'The header/banner landmark (the main page header with site title and navigation) should be at the top level of the page, not nested inside the main content area or other landmarks. When headers are nested, keyboard users cannot easily skip to the main content and cannot navigate page structure correctly.',
  'landmark-complementary-is-top-level':
    'The sidebar or complementary content landmark (supporting content like related links or sidebars) should be at the top level of the page, not nested inside the main content. When sidebars are nested, keyboard and screen reader users cannot easily navigate to them and may not realize they exist.',
  'landmark-contentinfo-is-top-level':
    'The footer or contentinfo landmark (page footer with copyright, links, contact info) should be at the top level of the page, not nested inside the main content. When footers are nested, keyboard users cannot easily navigate to them and must scroll through all content to find footer information.',
  'landmark-main-is-top-level':
    'The main content landmark should be at the top level of the page, directly accessible. When main content is nested inside other landmarks or regions, keyboard users must navigate through unnecessary layers to reach the primary page content.',
  'landmark-no-duplicate-banner':
    "A page should have only one main header/banner landmark. When multiple headers exist, screen reader and keyboard users become confused about page structure. They don't know which header is the main one or why there are duplicates.",
  'landmark-no-duplicate-contentinfo':
    "A page should have only one main footer/contentinfo landmark. Multiple footers confuse screen reader and keyboard users about page structure. They don't know which footer is the main one or why duplicates exist.",
  'landmark-no-duplicate-main':
    'A page should have only one main content landmark. When multiple main regions are marked, screen reader and keyboard users become confused about where the primary content actually is. They don\'t know which region is the "real" main content.',
  'landmark-one-main':
    "Every page needs one designated main content landmark (a marked region containing the page's primary content). This helps screen reader and keyboard users navigate directly to the most important content without having to skip through navigation, sidebars, or headers.",
  'landmark-unique':
    'When a page has multiple landmarks of the same type (like two sidebars), each should have a unique label or title. This helps screen reader and keyboard users distinguish between them. e.g., instead of two unlabeled "navigation" regions, they should be labeled "Left sidebar" and "Right sidebar".',
  'meta-viewport-large':
    'Pages must allow users to zoom in and scale content. When zoom is blocked, people with low vision cannot enlarge text and controls to read them comfortably. The viewport meta tag should allow scaling and not restrict maximum zoom.',
  'page-has-heading-one':
    'Every page should have one or more H1 heading that serves as the main topic. The H1 helps screen reader users quickly understand what the page is about and provides a structural anchor for the document.',
  'presentation-role-conflict':
    'Elements marked with role="presentation" or role="none" (which tells assistive technology to ignore them as they\'re decorative) should not be focusable or have interactive behavior. If an element is marked as decorative but is also focusable or interactive, there\'s a conflict—keyboard users can tab to it but won\'t understand what it is.',
  region:
    "Every piece of content on a page should be within a marked landmark or region (like header, main, footer, sidebar, or navigation). Orphaned content that's not inside any landmark can be missed by screen reader users. Marking content into regions helps keyboard users skip between sections and understand page organization.",
  'scope-attr-valid':
    'Table headers should have scope attributes that correctly identify whether they\'re column headers (scope="col") or row headers (scope="row"). The scope attribute tells screen readers which header applies to which cells. Incorrect scope values confuse screen readers about cell relationships.',
  'skip-link':
    'Skip links should be the first focusable element on a page (appear when you press Tab). When clicked, they should jump directly to the main content—which means the target (the element it points to) must exist and be reachable. Without a valid target, the skip link is broken and useless.',
  tabindex:
    'The tabindex attribute should never have positive values (like tabindex="1"). Positive tabindex values override the natural page order and cause keyboard navigation to become confusing and chaotic—jumping around the page unpredictably.',
  'table-duplicate-name':
    'Tables should not have both a caption and a summary that say exactly the same thing. This causes screen reader users to hear the same information announced twice. The caption should briefly describe the table, and any summary should add additional context or explanation—not repeat the caption word-for-word.',
  'meta-viewport':
    'Pages must allow users to zoom in and scale text using their browser or pinch-to-zoom on mobile devices. Disabling zoom locks people with low vision out of being able to enlarge content to read them comfortably. The viewport meta tag should allow scaling and not restrict maximum zoom.',
  'aria-allowed-role': `Buttons, links, and interactive elements should behave the way they're marked. e.g., if something looks and acts like a button (performs an action), it should be labeled as a button. If it goes to a different page, it should be labeled as a link. When the label doesn't match the actual behavior, screen reader users get confused about what will happen when they click. When possible, use real buttons (<button>) and real links (<a>) instead of creating fake buttons or links from plain text and code.`,
  'summary-name': 'Ensure summary elements have discernible text that clearly indicates the topic or purpose of the information that will be revealed when using the summary control.'
};

export const disabilityBadgesMap = {
  'aria-meter-name': ['Visual'],
  'aria-progressbar-name': ['Visual'],
  'image-alt': ['Visual'],
  'input-image-alt': ['Visual'],
  'object-alt': ['Visual'],
  'oobee-confusing-alt-text': ['Visual', 'Learning'],
  'role-img-alt': ['Visual'],
  'svg-img-alt': ['Visual'],
  'video-caption': ['Hearing'],
  'aria-required-children': ['Visual'],
  'aria-required-parent': ['Visual'],
  'definition-list': ['Visual'],
  dlitem: ['Visual'],
  list: ['Visual'],
  listitem: ['Visual'],
  'td-headers-attr': ['Visual'],
  'th-has-data-cells': ['Visual'],
  'autocomplete-valid': ['Learning'],
  'link-in-text-block': ['Visual', 'Learning'],
  'avoid-inline-spacing': ['Visual', 'Learning'],
  'no-autoplay-audio': ['Hearing', 'Learning'],
  'color-contrast': ['Visual'],
  'color-contrast-enhanced': ['Visual'],
  'frame-focusable-content': ['Motor', 'Visual'],
  'server-side-image-map': ['Motor', 'Visual'],
  'scrollable-region-focusable': ['Motor', 'Visual'],
  'oobee-accessible-label': ['Motor', 'Visual'],
  'meta-refresh': ['Learning'],
  blink: ['Learning', 'Visual'],
  marquee: ['Learning', 'Visual'],
  'meta-refresh-no-exceptions': ['Learning'],
  bypass: ['Visual', 'Learning'],
  'document-title': ['Visual', 'Learning'],
  'link-name': ['Visual', 'Learning'],
  'area-alt': ['Visual', 'Learning'],
  'identical-links-same-purpose': ['Motor'],
  'target-size': ['Learning'],
  'html-has-lang': ['Learning'],
  'html-lang-valid': ['Learning'],
  'html-xml-lang-mismatch': ['Learning'],
  'valid-lang': ['Learning'],
  'oobee-grading-text-contents': ['Learning', 'Visual'],
  'form-field-multiple-labels': ['Visual'],
  'aria-allowed-attr': ['Visual'],
  'aria-braille-equivalent': ['Visual'],
  'aria-command-name': ['Visual'],
  'aria-conditional-attr': ['Visual'],
  'aria-deprecated-role': ['Visual'],
  'aria-hidden-body': ['Visual', 'Motor'],
  'aria-hidden-focus': ['Visual'],
  'aria-input-field-name': ['Visual'],
  'aria-prohibited-attr': ['Visual'],
  'aria-required-attr': ['Visual'],
  'aria-roles': ['Visual'],
  'aria-toggle-field-name': ['Visual'],
  'aria-tooltip-name': ['Visual'],
  'aria-valid-attr': ['Visual'],
  'aria-valid-attr-value': ['Visual'],
  'button-name': ['Visual'],
  'duplicate-id-aria': ['Visual'],
  'frame-title': ['Visual'],
  'frame-title-unique': ['Visual'],
  'input-button-name': ['Visual'],
  label: ['Motor', 'Learning', 'Visual'],
  'nested-interactive': ['Visual'],
  'select-name': ['Visual'],
  accesskeys: ['Motor', 'Learning'],
  'aria-allowed-role': ['Visual'],
  'aria-dialog-name': ['Visual', 'Learning'],
  'aria-text': ['Visual'],
  'aria-treeitem-name': ['Visual'],
  'empty-heading': ['Visual', 'Learning'],
  'empty-table-header': ['Visual'],
  'frame-tested': ['Visual'],
  'heading-order': ['Visual', 'Learning'],
  'image-redundant-alt': ['Visual'],
  'label-title-only': ['Visual'],
  'landmark-banner-is-top-level': ['Visual'],
  'landmark-complementary-is-top-level': ['Visual'],
  'landmark-contentinfo-is-top-level': ['Visual'],
  'landmark-main-is-top-level': ['Visual'],
  'landmark-no-duplicate-banner': ['Visual'],
  'landmark-no-duplicate-contentinfo': ['Visual'],
  'landmark-no-duplicate-main': ['Visual'],
  'landmark-one-main': ['Visual'],
  'landmark-unique': ['Visual'],
  'meta-viewport-large': ['Learning', 'Visual'],
  'page-has-heading-one': ['Visual', 'Learning'],
  'presentation-role-conflict': ['Visual'],
  region: ['Visual'],
  'scope-attr-valid': ['Visual'],
  'skip-link': ['Motor', 'Learning', 'Visual'],
  tabindex: ['Motor'],
  'meta-viewport': ['Visual'],
  'summary-name': ['Visual'],
};

export default {
  cliZipFileName: 'oobee-scan-results.zip',
  exportDirectory: undefined,
  maxRequestsPerCrawl,
  maxConcurrency: 25,
  urlsCrawledObj,
  impactOrder,
  launchOptionsArgs,
  xmlSitemapTypes,
  urlCheckStatuses,
  launcher: chromium,
  pdfScanResultFileName: 'pdf-scan-results.json',
  forbiddenCharactersInDirPath,
  reserveFileNameKeywords,
  wcagLinks,
  wcagCriteriaLabels,
  a11yRuleShortDescriptionMap,
  disabilityBadgesMap,
  robotsTxtUrls: null,
  sitemapFetchedLinks: null as { totalLinksFetchedFromSitemaps: number; fetchedSitemaps: { url: string; fetchedLinks: number }[] } | null,
  userDataDirectory: null, // This will be set later in the code
  randomToken: null, // This will be set later in the code
  // Track all active Crawlee / Playwright resources for cleanup
  resources: {
    crawlers: new Set<PlaywrightCrawler>(),
    browserContexts: new Set<BrowserContext>(),
    browsers: new Set<Browser>(),
  },
};

export const rootPath = dirname;
export const wcagWebPage = 'https://www.w3.org/TR/WCAG22/';
const latestAxeVersion = '4.11';
export const axeVersion = latestAxeVersion;
export const axeWebPage = `https://dequeuniversity.com/rules/axe/${latestAxeVersion}/`;

export const saflyIconSelector = `#__safly_icon`;
export const cssQuerySelectors = [
  ':not(a):is([role="link"]',
  'button[onclick])',
  'a:not([href])',
  '[role="button"]:not(a[href])', // Add this line to select elements with role="button" where it is not <a> with href
];

export enum RuleFlags {
  DEFAULT = 'default',
  DISABLE_OOBEE = 'disable-oobee',
  ENABLE_WCAG_AAA = 'enable-wcag-aaa',
}

// Note: Not all status codes will appear as Crawler will handle it as best effort first. E.g. try to handle redirect
export const STATUS_CODE_METADATA: Record<number, string> = {
  // Custom Codes for Oobee's use
  0: 'Page Excluded',
  1: 'Not A Supported Document',
  2: 'Web Crawler Errored',
  3: 'Blocked by Safe Browsing',

  // 599 is set because Crawlee returns response status 100, 102, 103 as 599
  599: 'Uncommon Response Status Code Received',

  // This is Status OK but thrown when the crawler cannot scan the page
  200: 'Oobee was not able to scan the page due to access restrictions or compatibility issues',

  // 1xx - Informational
  100: '100 - Continue',
  101: '101 - Switching Protocols',
  102: '102 - Processing',
  103: '103 - Early Hints',

  // 2xx - Browser Doesn't Support
  204: '204 - No Content',
  205: '205 - Reset Content',

  // 3xx - Redirection
  300: '300 - Multiple Choices',
  301: '301 - Moved Permanently',
  302: '302 - Found',
  303: '303 - See Other',
  304: '304 - Not Modified',
  305: '305 - Use Proxy',
  307: '307 - Temporary Redirect',
  308: '308 - Permanent Redirect',

  // 4xx - Client Error
  400: '400 - Bad Request',
  401: '401 - Unauthorized',
  402: '402 - Payment Required',
  403: '403 - Forbidden',
  404: '404 - Not Found',
  405: '405 - Method Not Allowed',
  406: '406 - Not Acceptable',
  407: '407 - Proxy Authentication Required',
  408: '408 - Request Timeout',
  409: '409 - Conflict',
  410: '410 - Gone',
  411: '411 - Length Required',
  412: '412 - Precondition Failed',
  413: '413 - Payload Too Large',
  414: '414 - URI Too Long',
  415: '415 - Unsupported Media Type',
  416: '416 - Range Not Satisfiable',
  417: '417 - Expectation Failed',
  418: "418 - I'm a teapot",
  421: '421 - Misdirected Request',
  422: '422 - Unprocessable Content',
  423: '423 - Locked',
  424: '424 - Failed Dependency',
  425: '425 - Too Early',
  426: '426 - Upgrade Required',
  428: '428 - Precondition Required',
  429: '429 - Too Many Requests',
  431: '431 - Request Header Fields Too Large',
  451: '451 - Unavailable For Legal Reasons',

  // 5xx - Server Error
  500: '500 - Internal Server Error',
  501: '501 - Not Implemented',
  502: '502 - Bad Gateway',
  503: '503 - Service Unavailable',
  504: '504 - Gateway Timeout',
  505: '505 - HTTP Version Not Supported',
  506: '506 - Variant Also Negotiates',
  507: '507 - Insufficient Storage',
  508: '508 - Loop Detected',
  510: '510 - Not Extended',
  511: '511 - Network Authentication Required',
};

// Elements that should not be clicked or enqueued
// With reference from https://chromeenterprise.google/policies/url-patterns/
export const disallowedListOfPatterns = [
  '#',
  'mailto:',
  'tel:',
  'sms:',
  'skype:',
  'zoommtg:',
  'msteams:',
  'whatsapp:',
  'slack:',
  'viber:',
  'tg:',
  'line:',
  'meet:',
  'facetime:',
  'imessage:',
  'discord:',
  'sgnl:',
  'webex:',
  'intent:',
  'ms-outlook:',
  'ms-onedrive:',
  'ms-word:',
  'ms-excel:',
  'ms-powerpoint:',
  'ms-office:',
  'onenote:',
  'vs:',
  'chrome-extension:',
  'chrome-search:',
  'chrome:',
  'chrome-untrusted:',
  'devtools:',
  'isolated-app:',
];

export const disallowedSelectorPatterns = disallowedListOfPatterns
  .map(pattern => `a[href^="${pattern}"]`)
  .join(',')
  .replace(/\s+/g, '');

export const WCAGclauses = {
  '1.1.1': 'Provide text alternatives',
  '1.2.2': 'Add captions to videos',
  '1.3.1': 'Use proper headings and lists',
  '1.3.5': 'Clearly label common fields',
  '1.4.1': 'Add cues beyond color',
  '1.4.2': 'Control any autoplay audio',
  '1.4.3': 'Ensure text is easy to read',
  '1.4.4': 'Allow zoom without breaking layout',
  '1.4.6': 'Ensure very high text contrast',
  '1.4.12': 'Let users adjust text spacing',
  '2.1.1': 'Everything works by keyboard',
  '2.1.3': 'Everything works only by keyboard',
  '2.2.1': 'Let users extend time limits',
  '2.2.2': 'Let users stop motion',
  '2.2.4': 'Let users control alerts',
  '2.4.1': 'Add skip navigation',
  '2.4.2': 'Write clear page titles',
  '2.4.4': 'Say where links go',
  '2.4.9': 'Links make sense on their own',
  '2.5.8': 'Buttons must be easy to tap',
  '3.1.1': "Declare the page's language",
  '3.1.2': 'Show when language changes',
  '3.1.5': 'Keep content easy to read',
  '3.2.5': "Don't auto-change settings",
  '3.3.2': 'Label fields and options',
  '4.1.2': 'Make buttons and inputs readable',
};

export const a11yRuleStepByStepGuide: Record<string, { check: string; fix: string; review: string; learn: string }> = {
  'aria-meter-name': {
    check: 'Find visual progress or measurement indicators (like storage capacity bars or battery level indicators, volume controls)',
    fix: '(Developer) Add a text alternative that says what the meter measures (e.g., "Storage used")',
    review: 'Use a screen reader to confirm it announces the measurement and what it refers to',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-progressbar-name': {
    check: 'Find progress bars on the page (e.g., file upload, page loading, processing)',
    fix: "(Developer) Add a text alternative that says what's in progress (e.g., \"File upload\")",
    review: 'Trigger the progress action and confirm the screen reader announces both the label and the current percentage',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'image-alt': {
    check: 'Find images on the page that carry meaning (product photos, charts, diagrams, icons that communicate something important). Skip images that are just decoration, like spacers or background patterns.',
    fix: '(Developer) Add short, specific alt text for each meaningful image. If an image is only decoration, mark it so screen readers skip it.',
    review: "Use a screen reader to read each image's description and confirm it adequately explains what the image shows and why it matters.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'input-image-alt': {
    check: "Find buttons that use only images or icons like a delete icon, search icon, menu icon, print icon, etc. Look for buttons where you'd only see a picture, not text.",
    fix: '(Developer) Add a clear label to each image button that describes the ACTION, not just the icon. e.g., "Delete".',
    review: 'Use a screen reader on the button and confirm it announces the action clearly. When you tab to a delete button, the screen reader should say "Delete button" (or similar), not "Trash can image."',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'object-alt': {
    check: "Find embedded objects on the page: PDFs, videos, interactive tools, maps, or other content that's embedded within the page. These are different from regular images.",
    fix: '(Developer) Add a clear, descriptive label or title for each embedded object. The label should identify what the content is and, if helpful, provide additional context (file type, duration, etc.).',
    review: 'Use a screen reader to navigate to each embedded object and confirm the screen reader announces: (1) What the object is (PDF, video, map, etc.) (2) What it contains (3) Any relevant additional info (like duration for videos, file size for PDFs).',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'oobee-confusing-alt-text': {
    check: 'Find images that already have descriptions but the descriptions are too vague or unhelpful. Look for descriptions that just say: "image"',
    fix: '(Developer) Rewrite each vague description to actually describe what the image shows.',
    review: "Use a screen reader to read the updated descriptions and confirm they now adequately convey what the image shows and its purpose. A person listening should understand the image's content and why it matters.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'role-img-alt': {
    check: "Find elements that are marked as images but aren't traditional image elements (like custom icons or graphics).",
    fix: '(Developer) Add a text description that identifies what the element is e.g., "Settings icon"',
    review: "Use a screen reader to read each element's description and confirm it adequately conveys what the element represents and its purpose.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'svg-img-alt': {
    check: 'Find vector graphics (SVG elements) on the page that are marked as images. These might be logos, icons, diagrams, or other graphics',
    fix: '(Developer) Add a text description that identifies what the graphic is. e.g., "Company logo"',
    review: "Use a screen reader to read each SVG's description and confirm it adequately conveys what the graphic shows and its purpose",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'video-caption': {
    check: 'Identify which videos on the page lack captions',
    fix: '(Developer) Add caption tracks (<track> elements) to those videos with synchronized captions that include dialogue and important sounds',
    review: 'Play the video muted to confirm captions display, are synchronized with the video, and cover all spoken dialogue and important sounds',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-required-children': {
    check: 'Open the reported page and locate the element with the missing child elements',
    fix: "(Developer) Add the required child elements as described in the issue's fix. e.g., if a list role is missing list item children, add them",
    review: 'Use a screen reader to navigate the control and confirm it is working as expected—the structure is announced correctly and all items/options are accessible',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-required-parent': {
    check: 'Open the reported page and locate the element that is missing its required parent',
    fix: "(Developer) Move or nest the element inside its required parent element as described in the issue's fix. e.g., if a tab element is incorrectly placed outside a parent, move it inside a 'tab list' parent",
    review: 'Use a screen reader to navigate the control and confirm it is working as expected—the relationship between parent and child is announced correctly and the control functions properly.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'definition-list': {
    check: 'Find glossary or FAQ sections where terms are paired with answers or definitions.',
    fix: '(Developer) Ensure the definition list (<dl>) contains only <dt> (term) and <dd> (definition) elements as direct children. Remove any other HTML elements that may be incorrectly placed directly inside the <dl>',
    review: 'Use a screen reader to navigate the definition list and confirm it correctly announces the term-definition relationships.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  dlitem: {
    check: 'Find any terms or definitions on the page that appear outside a proper glossary structure.',
    fix: '(Developer) Ensure the definition list (<dl>) contains only <dt> (term) and <dd> (definition) elements as direct children. Remove any other HTML elements that may be incorrectly placed directly inside the <dl>',
    review: 'Use a screen reader to navigate the definition list and confirm it correctly announces the term-definition relationships.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  list: {
    check: 'Find bullet point or numbered lists on the page. Check that each item is wrapped in a list item element and that list items have a list container as their parent. Look for any text appearing directly in the list without being in a list item.',
    fix: '(Developer) Wrap each list item in <li> elements. Ensure <li> elements are direct children of <ul> or <ol>—nothing else should be a direct child.',
    review: 'Use a screen reader to navigate the list and confirm it announces the list structure and item count correctly.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  listitem: {
    check: 'Find content formatted as list items (usually bullets or numbers). Verify each item is inside a list container. Look for orphaned list items appearing outside any list.',
    fix: '(Developer) Ensure all <li> elements are direct children of a <ul> or <ol> parent. Move any orphaned <li> elements into the correct list container.',
    review: 'Use a screen reader to navigate and confirm the list announces correctly with proper list structure and item count.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'td-headers-attr': {
    check: 'Pick a table on the page and use a screen reader to read one row cell-by-cell. As you move through cells, the screen reader should announce the relevant header so you understand what each cell represents.',
    fix: '(Developer) Link headers to cells and add any missing header text that helps identify the meaning of the column or row.',
    review: 'Use a screen reader to move cell-by-cell through the table and confirm the correct header is announced for each cell.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'th-has-data-cells': {
    check: 'Pick a table and use a screen reader to read one row cell-by-cell. For each data cell you read, the screen reader should announce the header that applies to it.',
    fix: '(Developer) Ensure headers are properly connected to their data cells. Mark headers correctly as row or column headers. Add any missing header information or remove invalid table headers.',
    review: "Use a screen reader to move cell-by-cell through the table and confirm each data cell's correct header is announced.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'autocomplete-valid': {
    check: 'Find forms that collect common information like names, or emails. Check if fields have autocomplete attributes.',
    fix: '(Developer) Set valid autocomplete attributes on each field. e.g., name → "name", email → "email". Use attributes that match the specification.',
    review: "Test browser autofill: Type information into your browser's profile and see if fields autofill correctly with the matching data.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'link-in-text-block': {
    check: 'Scan text blocks on the page: Can you identify every link without hovering? Look for links that depend only on color to stand out.',
    fix: '(Developer) Add visual distinction beyond color—such as underline, bold, or different font style. Ensure the distinction has good contrast and remains visible.',
    review: 'Check a sample page: Without hovering over text, do links stand out clearly? Try viewing the page in grayscale to verify links are still distinguishable.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'avoid-inline-spacing': {
    check: 'Open the page and increase browser text spacing to between 1.5 and 2 times normal (e.g. using a Chrome Extension). Observe: Does the text spacing actually adjust on the page? Can you still read all the content?',
    fix: '(Developer) Check CSS style sheets and ensure they do not contain fixed line-spacing or text spacing values e.g. !important. Allow browser and user settings to control spacing. Ensure the layout remains functional when spacing is increased.',
    review: 'Confirm that with increased text spacing, no content is cut off and nothing overlaps making it unreadable.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'no-autoplay-audio': {
    check: 'Open pages that contain media (audio, video, animated content). Does sound start automatically when the page loads? If so, does it play longer than 3 seconds?',
    fix: '(Developer) If auto-play exists and plays longer than 3 seconds, either remove the auto-play or add controls e.g. buttons labelled pause/mute/stop so that users can easily find and activate.',
    review: "Reload the page and confirm it is silent by default when it loads. If auto-play audio exists, verify it's only a brief notification (3 seconds or less) or has visible controls.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'color-contrast': {
    check: "Scan the page and note specific text that's hard to read: buttons, image captions, footer text, placeholder text in form fields. If you have to focus hard to read it, contrast is likely too low.",
    fix: '(Developer) Adjust text and background colors to meet AA contrast ratio standards. Check using a contrast checking tool.',
    review: 'Check readability on mobile phones and with dark mode enabled to confirm contrast remains adequate in different viewing conditions.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'color-contrast-enhanced': {
    check: 'Scan the page and note specific text that could be clearer: buttons, captions, footers, labels. Even if contrast is acceptable for AA, AAA requires even higher contrast for better visibility.',
    fix: '(Developer) Adjust text and background colors to meet AAA contrast ratio standards (higher than AA). Use a contrast checking tool that supports AAA verification.',
    review: 'Check readability on mobile phones and with dark mode enabled to confirm enhanced contrast is maintained in different viewing conditions.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'frame-focusable-content': {
    check: 'Use keyboard only (no mouse) to navigate the page. Press Tab to move through interactive elements. When you reach a frame or iframe with interactive content, can you tab into it? Can you interact with content inside it?',
    fix: '(Developer) Ensure frames and iframes with focusable content allow keyboard focus to enter in normal tab order. Do not block keyboard access.',
    review: 'Test with keyboard: Press Tab to navigate to the frame/iframe and verify you can access interactive content inside. Use keyboard shortcuts specific to the frame content. Shift+Tab back out to confirm keyboard can exit the frame.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'server-side-image-map': {
    check: "Open the reported page and locate the image map element. Check if it's a server-side image map (uses server-side processing for clicks).",
    fix: '(Developer) Replace the server-side image map with a client-side image map using HTML <map> and <area> elements, or replace it with standard HTML buttons/links.',
    review: 'Test with keyboard navigation: Use Tab to navigate and Enter to activate different areas of the map. Confirm all previously clickable areas are now keyboard accessible.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'scrollable-region-focusable': {
    check: 'Open the reported page and locate the scrollable region. Use keyboard only to try scrolling within this region. Can you reach all interactive elements (buttons, links) inside the scrollable area using Tab?',
    fix: '(Developer) Make interactive elements within the scrollable region focusable and reachable via keyboard with tabindex=0.',
    review: 'Test with keyboard: Use Tab to reach elements in the scrollable region. Use arrow keys or other keyboard controls to scroll within the region. Confirm all interactive content is accessible.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'oobee-accessible-label': {
    check: 'Find clickable elements on the page (buttons, links, custom buttons created with code). Check if each has a clear label or text describing its action.',
    fix: '(Developer) Add visible labels or programmatic names that match the action. For elements that should not be keyboard accessible (like decorative interactive elements), use tabindex=-1 to remove them from keyboard navigation.',
    review: 'Use Tab to navigate to each clickable element and confirm a meaningful name is announced by screen readers.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'meta-refresh': {
    check: 'Open the page and watch for several minutes: Does the page reload or redirect automatically? If so, how often and without any warning?',
    fix: '(Developer) Remove any timed automatic refresh. If updates are needed, use standard links or buttons that users can click manually.',
    review: 'Wait on the page again to confirm it does not automatically reload or redirect on its own.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  blink: {
    check: 'Scan the page for any text that blinks, flashes, or moves repeatedly on its own. Look for CSS animations that create blinking effects.',
    fix: '(Developer) Remove any blinking elements or CSS animations that create flashing effects. Replace with static content or use non-animated styling.',
    review: 'Confirm nothing on the page blinks, flashes, or moves continuously on its own.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  marquee: {
    check: 'Scan the page for any text that scrolls, moves horizontally, or animates continuously on its own (e.g., a ticker or scrolling banner).',
    fix: '(Developer) Remove marquee elements and CSS animations that create scrolling text effects. Replace with static content or allow users to control the pace of any necessary animation.',
    review: 'Confirm nothing scrolls or animates automatically on the page. All content should be readable at a normal pace.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'meta-refresh-no-exceptions': {
    check: 'Stay on the page for several minutes: Does it reload or redirect automatically without user action? Check if it happens without any user prompts or warnings?',
    fix: '(Developer) Remove meta refresh and any other automatic timed page refresh mechanisms. If users need to access updated content, provide standard links or buttons they can click manually.',
    review: 'Wait on the page again to confirm it does not automatically refresh or redirect on its own under any circumstances.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  bypass: {
    check: 'Use keyboard navigation (Tab key) on the page: Can you quickly reach the main content without tabbing through all navigation? Is there a main content area marked or a skip link available?',
    fix: '(Developer) Implement a bypass method— mark a main content area with a main landmark, headings so screen readers can navigate to it directly.',
    review: 'Test keyboard navigation: Use assistive tools (VoiceOver Rotor, Android or NVDA/JAWS shortcuts) to confirm you can reach the main content and sections of the page area quickly without tabbing through repeated navigation blocks.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'document-title': {
    check: 'Open the page and look at the browser tab: Does the tab title describe the page content? Or does it just say "Untitled" or show the organization name only?',
    fix: '(Developer) Add a short, specific page title that describes the page content. The title should be unique across the site if possible. e.g., "Contact Us"',
    review: 'Reload the page and confirm the browser tab displays a meaningful title that makes sense for the page.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'link-name': {
    check: 'Find links on the page (underlined text or styled links). For each link, ask: "Does the link text clearly explain where it goes or what it does?" Look for vague link text like "click here" or "read more".',
    fix: '(Developer) Add descriptive link text that explains the link\'s destination or purpose. e.g., Instead of "click here", use "Learn more about our services".',
    review: 'Use Tab to navigate through each link and confirm screen readers announce meaningful, descriptive text for each link.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'area-alt': {
    check: 'Find image maps on the page (images with multiple clickable regions). Use a screen reader to check: Does each clickable area have a text label? Or are regions unlabeled?',
    fix: '(Developer) Add descriptive alt text labels to each clickable area in the image map. e.g., for a map with clickable regions for different cities, each region should be labeled "Click for information about Singapore" or similar. All areas must have labels—no decorative or unlabeled areas.',
    review: 'Use a screen reader to navigate the image map and confirm each clickable area is announced with its descriptive label.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'identical-links-same-purpose': {
    check: 'Scan the page for links that use identical text (like multiple "Read more" or "Learn more" links). Note which ones go to different destinations.',
    fix: '(Developer) For each link with identical text going to different pages, add an accessible label (aria-label or title) that specifies the destination. e.g.,: For "Read more" links, add aria-label="Read more about Product A" and aria-label="Read more about Product B".',
    review: 'Use a screen reader to navigate the page and confirm each identical-text link is announced with its unique accessible label showing the destination or purpose.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'target-size': {
    check: 'Try to tap buttons and clickable elements on the page, are they hard to tap? Check spacing between adjacent clickable elements.',
    fix: '(Developer) Increase clickable element sizes to at least 24px, or increase the space/padding between adjacent clickable elements. Ensure minimum spacing recommendations are met.',
    review: 'Test again with touch or by simulating small target areas: Confirm buttons and clickable elements are now easy to tap accurately without accidentally clicking nearby elements.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'html-has-lang': {
    check: 'Identify what language the page is written in (e.g., English).',
    fix: '(Developer) Set the language attribute on the page and on any frames/iframes. Use the correct language code (e.g., lang="en" for English).',
    review: 'Open the page and a localized version if available. Confirm the language attribute is set correctly on each.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'html-lang-valid': {
    check: "Check the page's language declaration. Is it using a valid ISO language code (like \"en\")? Or is it using something invalid or unclear?",
    fix: '(Developer) Fix the language code on the page to use a valid ISO code. e.g., "en" (English), "en-US" (US English)',
    review: 'Verify the corrected language code is saved. Open the page and confirm screen readers and translation tools now recognize the language correctly.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'html-xml-lang-mismatch': {
    check: 'Check if the page or any elements use different lang attributes. If so, verify they use the same language code.',
    fix: '(Developer) Ensure different lang attributes match on the same elements. Set both to the same valid language code. e.g.,: Set both to lang="en" and xml:lang="en".',
    review: 'Verify the matching language declarations are saved. Open the page and test that screen readers recognize the language correctly.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'valid-lang': {
    check: "Identify any content on the page that uses different languages than the page's main language. Check if those elements have language attributes.",
    fix: '(Developer) Add valid language codes to elements in different languages. Use valid ISO codes e.g., "es" for Spanish sections. <p lang="es">Este es un párrafo en español</p>.',
    review: 'Test with a screen reader: Navigate to content in different languages and confirm the screen reader switches to correct pronunciation for each language.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'oobee-grading-text-contents': {
    check: 'Identify the pages with the most complex or dense text content. Highlight sentences that are very long or use jargon/technical terms unfamiliar to general audiences.',
    fix: 'Rewrite complex sections using shorter sentences, common words, and one idea per sentence. Define any necessary technical terms. Remove or simplify jargon.',
    review: 'Ask a colleague unfamiliar with the topic to read the revised content and explain it back in their own words. If they struggle to understand, simplify further.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'form-field-multiple-labels': {
    check: 'Find form fields on the page that display more than one label or title. Check if there are multiple label elements pointing to the same field.',
    fix: '(Developer) Keep only one main label per field. Move extra text (hints, error messages) to separate help or error areas instead of making them labels.',
    review: 'Use a screen reader to tab through form fields and confirm each field announces only one clear label. Click the label and verify it correctly focuses the associated field.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-allowed-attr': {
    check: 'Find the elements identified in the report on the page. Check what ARIA attributes are applied to them.',
    fix: "(Developer) Remove invalid ARIA attributes from elements that don't support them (this will revert the element to its normal implicit behavior), or move the ARIA attributes to the correct element type that supports them.",
    review: 'Use a screen reader to navigate to those elements and confirm they now announce correctly with normal name and type.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-braille-equivalent': {
    check: 'Find controls or elements that have braille-specific abbreviated labels or descriptions (labels written specifically for braille displays).',
    fix: '(Developer) Add a full, non-abbreviated equivalent label. For example, if aria-braillelabel="vol", also provide aria-label="volume" or a full text label that braille and non-braille users both see.',
    review: 'Test with both braille and non-braille screen readers to confirm the control is announced clearly with the full equivalent. Both methods should provide the same information.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-command-name': {
    check: "Find elements like role=\"button\" that don't have visible text or labels. Check: Does each have text inside it, an aria-label, or a title attribute describing what it does?",
    fix: '(Developer) Add accessible labels using one of these methods: (1) Visible text inside the element, (2) aria-label attribute, (3) aria-labelledby pointing to another element with text, or (4) title attribute. e.g.,: role="button" aria-label="Save document" or title="Delete item".',
    review: 'Use a screen reader to Tab through command elements and confirm each announces what it does (e.g., "Save document button").',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-conditional-attr': {
    check: 'Find elements identified in the issue. Check: Do the accessibility attributes on this element match the actual state of the element? Or do they contradict each other? Look for elements where the type and behavior seem confused.',
    fix: '(Developer) Remove or replace conflicting ARIA attributes. Prefer native HTML elements and implicit attributes: Use <button> instead of <div role="button">. Use <a> for links instead of <div role="link">. Remove ARIA attributes that create conflicts or unclear states.',
    review: 'Use a screen reader to navigate the element and confirm it now announces clearly without conflicting information. The element should sound like one clear type (button, link, etc.), not confused or contradictory.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-deprecated-role': {
    check: 'Find elements listed in the issue that use outdated roles. Note the role names. Ask: Are these controlling buttons, tabs, menus, or other interactive elements?',
    fix: '(Developer) Replace each outdated role with a current supported role, or replace with a native HTML element (like real <button> or <a> instead of custom code).',
    review: "Use a screen reader to test: Navigate to each updated element and confirm it's announced correctly (as a button, tab, menu, etc.).",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-hidden-body': {
    check: 'Check if the page body has aria-hidden="true" applied to it. This would hide all content from screen readers.',
    fix: '(Developer) Remove aria-hidden="true" from the body element or any wrapper containing the main page content.',
    review: 'Use a screen reader to confirm the page content is now readable and all text and interactive elements are announced.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-hidden-focus': {
    check: 'Locate the elements identified in the issue that are marked as hidden. Check: Do they contain buttons, links, form fields, or other interactive elements that can be focused via keyboard?',
    fix: '(Developer) Either remove aria-hidden="true" from the container, or remove keyboard focus from the interactive elements inside it using tabindex="-1".',
    review: 'Test with keyboard: Use Tab to navigate through the page. Confirm you cannot tab into content that is hidden from screen readers. If you can tab to it, you can hear it announced.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-input-field-name': {
    check: 'Find custom input fields on the page. Check each one: Does it have a visible label or associated text describing what to enter?',
    fix: '(Developer) Add an accessible label to each input using aria-label or by associating a visible label element. e.g., aria-label="Email address".',
    review: 'Use a screen reader to Tab through input fields and confirm each announces its label/purpose clearly.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-prohibited-attr': {
    check: 'Find the elements identified in the issue. Check: What ARIA attributes do they have applied? Are those attributes appropriate for this element type?',
    fix: '(Developer) Remove ARIA attributes that are not permitted on these elements according to the accessibility specification.',
    review: 'Test with a screen reader: Navigate to the element and confirm it now announces correctly without conflicting or confusing information.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-required-attr': {
    check: 'Identify each interactive element mentioned in the issue (tabs, sliders, etc.). Check: Does it have all the required ARIA attributes for its role?',
    fix: "(Developer) Add the missing required ARIA attributes for each element's role. e.g., For tabs, add aria-selected.",
    review: 'Use a screen reader to interact with the element and confirm it announces the name, state, and current value correctly.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-roles': {
    check: 'Find elements identified in the issue. Check: Do they have ARIA role attributes? Are those role names valid and supported (like "button", "link")? Or are they misspelled or unsupported?',
    fix: '(Developer) Replace invalid role names with valid, supported ones. If no valid role exists for the element, replace with a native HTML element (real <button>, <a>, <input>) instead.',
    review: "Use a screen reader and keyboard to test: Tab to the element and confirm it's announced correctly (as a button, link, or other expected type). Confirm keyboard activation works as expected.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-toggle-field-name': {
    check: 'Find toggle switches or checkbox-style controls on the page. Check: Does each have a visible label or descriptive text? Or is it labeled vaguely like "On/Off" without context?',
    fix: '(Developer) Add descriptive labels or aria-labels to each toggle. e.g., aria-label="Dark mode toggle", or visible text saying "Dark mode".',
    review: 'Use a screen reader to Tab through toggles and confirm each announces what will be toggled.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-tooltip-name': {
    check: 'Find tooltips on the page. Check: Does each have a visible label or aria-label describing what it does?',
    fix: '(Developer) Add labels using visible text, title attribute, or aria-label. e.g., aria-label="Save document", title="Print page"',
    review: 'Use a screen reader to Tab to buttons and confirm each announces what it does (e.g., "Save document button" not just "button").',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-valid-attr': {
    check: 'Find elements identified in the issue. Check their ARIA attributes: Are they spelled correctly? Are they documented, supported attribute names? Look for common typos.',
    fix: '(Developer) Correct any misspelled or unsupported ARIA attribute names to valid, documented ones. Use official ARIA attribute reference to verify correct spelling.',
    review: 'Test with a screen reader and confirm elements now announce correctly with the corrected attributes. No missing or odd announcements.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-valid-attr-value': {
    check: 'Find elements identified in the issue. Check: What values are assigned to their ARIA attributes? Are they valid according to the ARIA specification? Or are they using invalid/unsupported values?',
    fix: '(Developer) Change each ARIA attribute value to a valid, supported value. e.g., Change aria-pressed="yes" to aria-pressed="true"',
    review: 'Use a screen reader to test elements and confirm they now announce the correct state or value.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'button-name': {
    check: "Find buttons on the page that don't have text inside them or visible labels. e.g., icon-only buttons",
    fix: '(Developer) Add descriptive text. This can be: (1) Text inside the button ("Save", "Submit"), (2) aria-label attribute ("Save document", "Submit form"), or (3) title attribute with the action.',
    review: 'Use a screen reader to Tab through buttons and confirm each announces what it does (e.g., "Save button", "Delete item button").',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'duplicate-id-aria': {
    check: 'Find elements that use duplicate IDs—especially form fields and elements referenced by labels or ARIA attributes. Check the page source or use developer tools to identify duplicates.',
    fix: '(Developer) Give each element a unique ID. Update any labels or ARIA references (like aria-labelledby) to point to the correct unique ID. e.g., Change duplicate id="email" to id="email-address" and id="email-subscribe".',
    review: 'Test form fields and labels: Click each label and verify it focuses only its intended field. Use Tab to navigate and confirm focus works correctly.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'frame-title': {
    check: 'Find all frames and iframes on the page—maps, videos, widgets, etc. Check if each has a title attribute. If not, note what it contains.',
    fix: '(Developer) Add a descriptive title attribute to each frame. e.g., title="Location map showing branch offices"',
    review: 'Use a screen reader and Tab through the page. When you reach each frame/iframe, confirm its title is announced.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'frame-title-unique': {
    check: 'Identify pages with multiple frames or iframes of the same type (like multiple videos, etc.). Check: Do they all have the same title? Or unique titles?',
    fix: '(Developer) Give each frame a unique, descriptive title that distinguishes it. e.g., "Product demo video" vs. "Customer testimonial video".',
    review: 'Use a screen reader and Tab to each frame. Confirm each frame announces a unique title that helps distinguish it from other frames.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'input-button-name': {
    check: "Find input-based buttons on the page that don't have visible text or labels. e.g., submit buttons with no text, image buttons without alt text.",
    fix: '(Developer) Add descriptive text: (1) For <input type="submit">, add a value attribute like value="Submit Form". (2) For <input type="image">, add alt text like alt="Search button".',
    review: 'Use a screen reader to Tab through buttons and confirm each announces its purpose (e.g., "Submit form button").',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  label: {
    check: 'Find form fields on the page without visible labels or programmatic labels. Check: Does each field have text near it explaining what to enter? Or can you see what the field is for?',
    fix: "(Developer) Associate labels with fields: (1) Use <label> elements with for attribute pointing to the field's ID, (2) Or use aria-label/aria-labelledby on the field. e.g., <label for=\"email\">Email address:</label> <input id=\"email\">.",
    review: 'Use a screen reader to Tab through form fields and confirm each announces its label. Click visible labels and verify they focus the corresponding field.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'nested-interactive': {
    check: 'Find nested interactive elements on the page. Use Tab or inspect the HTML: Are buttons inside links, links inside buttons, or multiple interactive elements nested together?',
    fix: '(Developer) Restructure the HTML to remove nesting. Make interactive elements siblings instead of nested. If you need multiple actions, use separate buttons/links at the same level.',
    review: "Use Tab and click: Confirm you can click and interact with each interactive element separately. Use a screen reader to verify no confusion about what's clickable.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'select-name': {
    check: "Find select dropdowns on the page. Check each one: Does it have a visible label explaining what the dropdown controls? Or can you tell what it's for without reading surrounding text?",
    fix: "(Developer) Associate labels with select elements: (1) Use <label> elements with for attribute pointing to the select's ID, (2) Or use aria-label/aria-labelledby. e.g., <label for=\"country\">Choose your country:</label> <select id=\"country\">.",
    review: 'Use a screen reader to Tab to each dropdown and confirm it announces the label. Open the dropdown and confirm options are readable.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  accesskeys: {
    check: 'Find elements with accesskey attributes on the page. List which keys are used. Are any duplicates? Do any conflict with common browser/system shortcuts (Ctrl+S, Ctrl+P, etc.)?',
    fix: '(Developer) Remove duplicate access keys. Rename conflicting keys to avoid browser/system conflicts. Document all access keys on a help page so users know they exist. e.g., "Press Alt+S to search".',
    review: "Test: Press each documented access key and confirm only one action fires. Verify access keys don't conflict with browser functions (like opening the File menu).",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-dialog-name': {
    check: 'Open popup or modal dialogs on the page. Check each one: Does it have a visible title at the top explaining its purpose? Or any accessible label? e.g.,  "Confirm delete", "Sign in".',
    fix: '(Developer) Add a clear, descriptive title/label to each dialog: (1) Visible text at the top of the dialog, (2) aria-label or title attribute on the dialog container, or (3) aria-labelledby pointing to the title element.',
    review: 'Use a screen reader to open each dialog and confirm it announces the dialog type and reads the title/purpose clearly.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-text': {
    check: 'Navigate through the page text with a screen reader. Identify if any interactive elements inside text-only role that are not correctly focussed or announced by screen reader.',
    fix: '(Developer) Remove interactive elements from text elements. If interactive content must be inside, remove the text role or make the interactive element focusable at a higher level.',
    review: 'Use a screen reader and navigate through the page and confirm interactive elements (buttons, links, form fields) receive focus.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-treeitem-name': {
    check: 'Find tree or expandable list items on the page (like file explorers or navigation trees). Check each item: Does it have visible text describing what it is? Can you tell items apart?',
    fix: '(Developer) Add accessible labels to each tree item: (1) Visible text inside the item, (2) title, (3) aria-label attribute, or (4) aria-labelledby pointing to text elsewhere. e.g., "Folder: Documents", "File: Report.pdf".',
    review: 'Use a screen reader to navigate through tree items and confirm each announces its name. Use arrow keys to expand/collapse and verify screen reader announces state changes.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'empty-heading': {
    check: 'Skim the page and look for empty heading elements (h1, h2, h3, etc.) that have no text content or only contain images/icons without text.',
    fix: '(Developer) Remove empty headings entirely, remove aria-hidden="true", or add meaningful text that describes the section. e.g.,: "Features", "Contact information".',
    review: "Use a screen reader to review the page's heading structure and confirm no headings are empty. Use a heading navigation list to verify all headings have descriptive text.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'empty-table-header': {
    check: 'Inspect data tables on the page for empty header cells (column or row headers with no text content). Check: Do all headers have labels? Or are some blank?',
    fix: '(Developer) Add descriptive text to each header cell. e.g., Column headers might be "Name". Row headers might be "Q1 Sales".  Remove aria-label/aria-labelledby attribute to each header cell.',
    review: "Use a screen reader to move cell-by-cell through the table and confirm each data cell's header is announced. The announcement should make clear what data is in each cell.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'frame-tested': {
    check: 'Open a page containing embedded frames or iframes (videos, widgets etc). Run a scan and check: Does it report issues in the frames? Or is frame content being skipped?',
    fix: '(Developer) Test frames with Oobee to surface any issues inside them. Configure tools to scan frames when possible. Fix accessibility issues found within frames. Ensure third-party content meets accessibility standards.',
    review: 'Test keyboard navigation across the page: Tab into and out of frames. Confirm all interactive content in frames is keyboard accessible. Retest with Oobee to confirm frame content is now properly analyzed and accessible.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'heading-order': {
    check: "Use the browser's headings outline or a screen reader to view the heading structure. Check: Does the page have one H1 at the top? Do headings increase logically (H1 → H2)? Or are levels skipped or out of order?",
    fix: "(Developer) Restructure headings to follow proper order: Set one H1 for the main page title. Use H2 for primary sections. Use H3 for subsections within H2. Don't skip levels.  Use CSS styles to modify the size of text.",
    review: 'Review the heading outline/list with a screen reader to confirm all headings are in logical, hierarchical order with no gaps or backwards jumps.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'image-redundant-alt': {
    check: 'Find images with alt text on the page. For each image, check: Is there text near the image saying the same thing? Would a screen reader user hear the information twice?',
    fix: '(Developer) For images that are decorative or just illustrate existing text, use empty alt: alt="". For informative images, write alt text that adds new information or context not already in nearby text.',
    review: 'Use a screen reader to read through the page and confirm no information is announced twice. Images should either add new information or have empty alt if decorative.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'label-title-only': {
    check: 'Find form fields on the page. Check each one: Is there visible text labeling the field? Or is the label hidden, only appearing as a tooltip on hover, or in placeholder text only?',
    fix: "(Developer) Add visible text labels next to or above each form field. Use <label> elements properly associated with fields. Place labels where they're always visible—not in tooltips or placeholders.",
    review: 'Visually scan the page and verify every form field has a visible label. Tab through with a screen reader and confirm the label is announced.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'landmark-banner-is-top-level': {
    check: 'Check the page structure: Is the header (banner landmark) at the very top level? Or is it nested inside the main content area or another landmark? Use a landmark navigator or browser inspector to verify.',
    fix: '(Developer) Move the header/banner landmark to the top level of the page, outside the main content and other landmarks. Structure should be: header, then main content, then sidebar, then footer.',
    review: 'Use a landmark navigator (or screen reader landmark commands) to jump between page regions. Confirm you can reach the header directly without being trapped inside other content areas.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'landmark-complementary-is-top-level': {
    check: 'Check the page structure: Is the sidebar (complementary landmark) at the top level? Or nested inside the main content or another landmark? Use a landmark navigator to verify.',
    fix: '(Developer) Move the sidebar/complementary landmark to the top level, outside the main content. Structure should be: header, main content, sidebar, footer.',
    review: 'Use a landmark navigator to jump to the sidebar region. Confirm you can reach it directly at the top level without being trapped inside other areas.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'landmark-contentinfo-is-top-level': {
    check: 'Check the page structure: Is the footer (contentinfo landmark) at the top level? Or nested inside the main content or another landmark? Use a landmark navigator to verify.',
    fix: '(Developer) Move the footer/contentinfo landmark to the top level of the page, outside all other content areas. Proper structure: header, main, sidebar, then footer.',
    review: 'Use a landmark navigator to jump to the footer. Confirm you can reach it at the top level. Use a screen reader skip link or landmark navigation to verify you can access footer content without scrolling through all main content.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'landmark-main-is-top-level': {
    check: 'Check the page structure: Is the main content landmark at the top level? Or nested inside other landmarks or containers? Use a landmark navigator or page outline to verify.',
    fix: '(Developer) Restructure the page so the main content region is at the top level, alongside but separate from header, sidebar, and footer regions.',
    review: 'Use keyboard shortcuts or landmark navigation to jump directly to the main content. Confirm you can reach it quickly from any page location without navigating through other regions first.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'landmark-no-duplicate-banner': {
    check: 'Use a landmark navigator or screen reader to scan the page. Count how many header/banner landmarks exist. Are there multiple headers? Or just one at the top?',
    fix: '(Developer) Combine multiple headers into one. Remove duplicate headers. Keep only one main header/banner landmark at the top of the page.',
    review: 'Verify with a landmark navigator that the page now has exactly one header/banner region. Use screen reader landmark commands to confirm only one header is announced.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'landmark-no-duplicate-contentinfo': {
    check: 'Use a landmark navigator or screen reader to scan the page. Count how many footer/contentinfo landmarks exist. Are there multiple footers? Or just one at the bottom?',
    fix: '(Developer) Combine multiple footers into one. Remove duplicate footers. Keep only one main footer/contentinfo landmark at the bottom of the page.',
    review: 'Verify with a landmark navigator that the page now has exactly one footer/contentinfo region. Use screen reader landmark commands to confirm only one footer is announced.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'landmark-no-duplicate-main': {
    check: 'Use a landmark navigator or screen reader to scan the page. Count how many main content landmarks exist. Is there more than one? Or just one?',
    fix: "(Developer) Consolidate into one main region. Remove or reclassify any duplicate main landmarks. Keep only one main landmark containing the page's primary content. Secondary content should use other landmarks (complementary, regions, etc.).",
    review: 'Verify with a landmark navigator that the page now has exactly one main region. Use screen reader landmark navigation to confirm only one main content area is announced.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'landmark-one-main': {
    check: 'Check if the page has a marked main content region. Is there a <main> element or an element with role="main"? Or does the page lack a clear main content area?',
    fix: '(Developer) Add a main landmark that wraps the primary page content. Use the semantic <main> element, or use role="main" on a <div>. All important page content should be inside this region.',
    review: 'Use a landmark navigator or screen reader to jump to the main region. Confirm you can reach the primary content directly without navigating through other page areas first.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'landmark-unique': {
    check: 'Use a landmark navigator to scan the page. Are there multiple regions with the same type? Do they have different labels, or are they identical and indistinguishable?',
    fix: '(Developer) If there are multiple landmarks of the same type, add unique labels using aria-label or aria-labelledby. e.g., role="complementary" aria-label="Featured products sidebar".',
    review: 'Use a landmark navigator and verify each region is announced with its unique label. Confirm you can distinguish one from another.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'meta-viewport-large': {
    check: 'On a mobile device or mobile browser view, try to pinch-zoom (pinch your fingers apart) to enlarge the page. Does it zoom in? Or is zoom blocked and the page stays the same size?',
    fix: '(Developer) Check the viewport meta tag. It should NOT have user-scalable="no" or maximum-scale="1". Allow zooming by either removing these restrictions or setting maximum-scale to 2 or higher.',
    review: 'Test on mobile: Pinch-zoom to enlarge content to 200%. Confirm text is readable and buttons/controls are still usable and clickable at larger zoom levels.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'page-has-heading-one': {
    check: 'Look at the page and check: Is there a main page title or headline at the top? Is it marked as an H1 heading? Or is there no H1, or multiple H1s?',
    fix: "(Developer) Add one H1 heading at the top of the page content that describes the page's main topic. Use <h1> tags. This should be the page title or main section. After H1, use H2 for main sections, H3 for subsections, etc.",
    review: "Use a screen reader or headings list to verify the page has one or more H1 and it's the first heading on the page.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'presentation-role-conflict': {
    check: 'Tab through the page looking for odd focus behavior. Do you get stuck on decorative elements? Do some elements receive focus but seem to have no purpose?',
    fix: "(Developer) Remove role=\"presentation\" or role=\"none\" from interactive elements, OR remove interactive behavior and focus from purely decorative elements. Don't mark the element as aria-hidden=\"true\" if role=\"presentation\" or role=\"none\" is used as this changes the semantic meaning of the element for screen reader users.",
    review: 'Test with Tab: Navigate through the page in order from top to bottom. Confirm only meaningful interactive elements (buttons, links, form fields) receive focus. Decorative elements should be skipped.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  region: {
    check: 'Check the page structure: Is all meaningful content inside marked regions or landmarks? Or is there orphaned content outside any landmark? Look for text, buttons, or links that float outside structural areas.',
    fix: '(Developer) Wrap all page content in appropriate landmarks: <header>, <main>, <footer>, <nav>, <aside>, or <section role="region">. Every piece of content should belong to one of these sections.',
    review: 'Use a landmark navigator to verify all page content is inside landmarks. Keyboard navigation should move smoothly between regions with no orphaned content.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'scope-attr-valid': {
    check: 'Pick a table with headers. Check the headers: Do they use scope="col" for column headers? Do they use scope="row" for row headers? Or are scope values missing or incorrect?',
    fix: '(Developer) Add correct scope attributes to all table headers. Use scope="col" for headers that label columns. Use scope="row" for headers that label rows.',
    review: 'Use a screen reader to navigate the table cell-by-cell. Confirm that as you move through cells, the correct header (column or row) is announced.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'skip-link': {
    check: 'Open the page and press Tab immediately. Does a "Skip to content" or similar link appear? If yes, click it or press Enter. Does it jump to the main content? Or does it fail?',
    fix: '(Developer) Ensure the skip link exists as the first focusable element. Make sure the hyperlink points to a valid target of the same page (e.g. href="#maincontent"). Test that clicking the link actually moves focus to that target.',
    review: 'Test with keyboard: Press Tab to reveal the skip link. Press Enter to activate it. Confirm your focus moves to the main content area and you can continue from there.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  tabindex: {
    check: 'Use Tab to navigate through the page from top to bottom. Does focus move in a logical, left-to-right, top-to-bottom order? Or does it jump around randomly?',
    fix: "(Developer) Search the HTML for any tabindex attributes with positive numbers (1, 2, 3, etc.) and remove them. Use tabindex=\"0\" to include elements in normal tab order, or tabindex=\"-1\" to exclude them. Finally check that HTML elements are ordered semantically in a logical way from top to bottom, and re-order them where necessary.",
    review: 'Test keyboard navigation again: Tab from the top of the page down. Focus should move in a logical, predictable order. No random jumps or skips.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'table-duplicate-name': {
    check: 'Find tables with both a <caption> element and a summary attribute (or aria-label). Check: Do they say the same thing? Or does each add different information?',
    fix: '(Developer) If caption and summary are identical, remove one. Keep the caption. Or revise the summary to add context or explanation not already in the caption.',
    review: 'Use a screen reader to navigate the table and confirm the caption/summary is announced once with clear information. No duplicate announcements.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'meta-viewport': {
    check: 'Test zooming: On desktop, use browser zoom (Ctrl and +, or Cmd and +). On laptop or mobile, pinch your fingers apart to zoom. Can you enlarge the page content? Or is zoom blocked and nothing happens?',
    fix: "(Developer) Check the <meta name=\"viewport\"> tag in the page's HTML. Remove or change: user-scalable=\"no\" (change to user-scalable=\"yes\"), and maximum-scale=\"1\" (increase to at least 2). e.g., <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=2, user-scalable=yes\">.",
    review: 'Test zoom on both desktop and mobile: Zoom to 200% (double size). Confirm text is readable, buttons are clickable, and page functionality works normally at larger zoom levels.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'aria-allowed-role': {
    check: 'Find elements that look clickable on the page. Ask: Does this do something (like submit a form or open a menu)? Or does it go somewhere else (navigate to a new page)? Check if the label matches what it actually does.',
    fix: '(Developer) Use real <button> for actions, real <a> for navigation links. If you must use code to create buttons or links, make sure they actually behave like real ones—buttons activate with Space/Enter, links activate with Enter and navigate away.',
    review: "Test with keyboard: Try clicking buttons (Space or Enter), try clicking links (Enter). Use a screen reader to hear if it's announced as a button or link. Confirm what it announces matches what it actually does.",
    learn: 'Review and learn more about this issue on A11y Playground',
  },
  'summary-name': {
    check: 'Find summary elements and check if text is inside, or has a label (aria-label or title). Check: Do they convey meaningful information about the details?',
    fix: '(Developer) If summary elements are missing text or labels, add one to provide context or explanation to the details.',
    review: 'Use a screen reader to navigate to the summary and ensure it is announced with clear information.',
    learn: 'Review and learn more about this issue on A11y Playground',
  },
};
