/* This file is a part of @mdn/browser-compat-data
 * See LICENSE file for more information. */

import { styleText } from 'node:util';

/** @import {Linter, LinterData} from '../types.js' */
/** @import {Logger} from '../utils.js' */
/** @import {CompatStatement} from '../../types/types.js' */

/**
 * Process the data for prefix errors
 * @param {CompatStatement} data The data to test
 * @param {string} category The category the data belongs to
 * @param {string} feature The full feature path
 * @param {Logger} logger The logger to output errors to
 * @returns {void}
 */
const processData = (data, category, feature, logger) => {
  /** @type {string[]} */
  let prefixes = [];

  if (category === 'api') {
    prefixes = [
      'moz',
      'Moz',
      'MOZ_',
      'webkit',
      'WebKit',
      'webKit',
      'WEBKIT_',
      'ms',
      'MS',
      'o',
      'O',
    ];
  }
  if (category === 'css') {
    prefixes = ['-webkit-', '-moz-', '-ms-', '-o-', '-khtml-'];
  }

  if (prefixes.length === 0) {
    // Prefixes aren't enforced for other categories
    return;
  }

  // "as string" cast performed because we know that -1 will always be a valid index
  const featureName = /** @type {string} */ (feature.split('.').at(-1));
  const strippedFeatureName = featureName.replace(/_(event|static)/, '');

  for (const support of Object.values(data.support)) {
    const supportStatements = Array.isArray(support) ? support : [support];

    for (const statement of supportStatements) {
      if (!statement) {
        continue;
      }
      if (statement.prefix && statement.alternative_name) {
        logger.error(
          'Both prefix and alternative name are defined, which is not allowed.',
        );
      }
      if (
        statement.prefix &&
        !prefixes.some((p) => statement.prefix?.startsWith(p))
      ) {
        logger.error(
          `Prefix is set to ${styleText('bold', statement.prefix)}, which is invalid for ${category}`,
        );
      }
      if (statement.alternative_name) {
        const altNameMatchesPrefix = prefixes.find((p) => {
          const regex = new RegExp(
            `^:?:?${p}(${strippedFeatureName[0].toLowerCase()}|${strippedFeatureName[0].toUpperCase()})${strippedFeatureName.slice(
              1,
            )}$`,
            'g',
          );
          return statement.alternative_name?.match(regex);
        });
        if (altNameMatchesPrefix) {
          logger.error(
            `Use ${styleText('bold', `"prefix": "${altNameMatchesPrefix}"`)} instead of ${styleText('bold', `"alternative_name": "${statement.alternative_name}"`)}`,
          );
        }
      }
    }
  }
};

/** @type {Linter} */
export default {
  name: 'Prefix',
  description: 'Ensure that prefixes in support statements are valid',
  scope: 'feature',
  /**
   * Test the data
   * @param {Logger} logger The logger to output errors to
   * @param {LinterData} root The data to test
   */
  check: (logger, { data, path: { category, full } }) => {
    processData(/** @type {CompatStatement} */ (data), category, full, logger);
  },
};
