import {user} from '#utils/log';

import {
  ExternalCorePubVars,
  MIN_PUB_CONTROL_WIDTH_OF_DESKTOP,
  getAutoConfig,
  getPubControlConfig,
} from './a4a/shared/content-recommendation';

/**
 * Approved height for AdSense full-width responsive ads.
 * @const {number}
 */
export const ADSENSE_RSPV_ALLOWED_HEIGHT = 320;

/**
 * The attribute value for AdSense data-auto-format tag.
 * For full-width responsive ad: data-auto-format='rspv'.
 * For full-width matched content responsive ad: data-auto-format='mcrspv'
 * @const {string}
 */
export const ADSENSE_RSPV_TAG = 'rspv';
export const ADSENSE_MCRSPV_TAG = 'mcrspv';

/**
 * Given the amp-ad data attribute containing the multi-size dimensions, and a
 * set of primary dimensions, this function will return all valid multi-size
 * [width, height] pairs in an array.
 *
 * @param {string} multiSizeDataStr The amp-ad data attribute containing the
 *   multi-size dimensions.
 * @param {number} primaryWidth The primary width of the ad slot.
 * @param {number} primaryHeight The primary height of the ad slot.
 * @param {boolean} multiSizeValidation A flag that if set to true will enforce
 *   the rule that ensures multi-size dimensions are no less than 2/3rds of
 *   their primary dimension's counterpart.
 * @param {boolean=} isFluidPrimary Indicates whether the ad slot's primary
 *   size is fluid.
 * @return {?Array<!Array<number>>} An array of dimensions.
 */
export function getMultiSizeDimensions(
  multiSizeDataStr,
  primaryWidth,
  primaryHeight,
  multiSizeValidation,
  isFluidPrimary = false
) {
  const dimensions = [];
  const arrayOfSizeStrs = multiSizeDataStr.split(',');

  for (let i = 0; i < arrayOfSizeStrs.length; i++) {
    const sizeStr = arrayOfSizeStrs[i];
    if (sizeStr.toLowerCase() == 'fluid') {
      // Fluid dummy sizes should be appended to the front of the request
      // parameter, so they must be handled elsewhere.
      continue;
    }
    const size = sizeStr.split('x');

    // Make sure that each size is specified in the form WxH.
    if (size.length != 2) {
      user().error('AMP-AD', `Invalid multi-size data format '${sizeStr}'.`);
      continue;
    }

    const width = Number(size[0]);
    const height = Number(size[1]);

    // Make sure that both dimensions given are positive numbers.
    if (
      !validateDimensions(
        width,
        height,
        (w) => isNaN(w) || w <= 0,
        (h) => isNaN(h) || h <= 0,
        (badParams) =>
          badParams
            .map(
              (badParam) =>
                `Invalid ${badParam.dim} of ${badParam.val} ` +
                'given for secondary size.'
            )
            .join(' ')
      )
    ) {
      continue;
    }

    // Check that secondary size is not larger than primary size.
    if (
      !isFluidPrimary &&
      !validateDimensions(
        width,
        height,
        (w) => w > primaryWidth,
        (h) => h > primaryHeight,
        (badParams) =>
          badParams
            .map(
              (badParam) =>
                `Secondary ${badParam.dim} ${badParam.val} ` +
                `can't be larger than the primary ${badParam.dim}.`
            )
            .join(' ')
      )
    ) {
      continue;
    }

    // Check that if multi-size-validation is on, that the secondary sizes
    // are at least minRatio of the primary size.
    if (multiSizeValidation) {
      // The minimum ratio of each secondary dimension to its corresponding
      // primary dimension.
      const minRatio = 2 / 3;
      const minWidth = minRatio * primaryWidth;
      const minHeight = minRatio * primaryHeight;
      if (
        !validateDimensions(
          width,
          height,
          (w) => w < minWidth,
          (h) => h < minHeight,
          (badParams) =>
            badParams
              .map(
                (badParam) =>
                  `Secondary ${badParam.dim} ${badParam.val} is ` +
                  `smaller than 2/3rds of the primary ${badParam.dim}.`
              )
              .join(' ')
        )
      ) {
        continue;
      }
    }

    // Passed all checks! Push additional size to dimensions.
    dimensions.push([width, height]);
  }

  return dimensions;
}

/**
 * A helper function for determining whether a given width or height violates
 * some condition.
 *
 * Checks the width and height against their corresponding conditions. If
 * either of the conditions fail, the errorBuilder function will be called with
 * the appropriate arguments, its result will be logged to user().error, and
 * validateDimensions will return false. Otherwise, validateDimensions will
 * only return true.
 *
 * @param {(number|string)} width
 * @param {(number|string)} height
 * @param {function((number|string)): boolean} widthCond
 * @param {function((number|string)): boolean} heightCond
 * @param {function(!Array<{dim: string, val: (number|string)}>): string=} errorBuilder
 * A function that will produce an informative error message.
 * @return {boolean}
 */
function validateDimensions(
  width,
  height,
  widthCond,
  heightCond,
  errorBuilder
) {
  const badParams = [];
  if (widthCond(width)) {
    badParams.push({dim: 'width', val: width});
  }
  if (heightCond(height)) {
    badParams.push({dim: 'height', val: height});
  }
  if (badParams.length) {
    user().warn('AMP-AD', errorBuilder(badParams));
  }
  return !badParams.length;
}

/**
 * Calculates height of responsive matched content slot based on its width.
 * This logic should be kept as close to possible to the logic inside
 * adsbygoogle.js.
 *
 * @param {number} availableWidth
 * @param {!Element} element <amp-ad> tag which contains publisher settings
 *     if any.
 * @return {number} height to use for the matched content slot.
 */
export function getMatchedContentResponsiveHeightAndUpdatePubParams(
  availableWidth,
  element
) {
  const pubControlParams = {
    numberOfRows: element.getAttribute(ExternalCorePubVars.ROWS_NUM),
    numberOfColumns: element.getAttribute(ExternalCorePubVars.COLUMNS_NUM),
    layoutType: element.getAttribute(ExternalCorePubVars.UI_TYPE),
  };
  let config;
  if (
    pubControlParams.numberOfRows ||
    pubControlParams.numberOfColumns ||
    pubControlParams.layoutType
  ) {
    // Publisher provided at least 1 param  which means we are in
    // "pub controlled matched content" mode.
    config = getPubControlConfig(availableWidth, pubControlParams);
  } else {
    // Publisher didn't provide any matched content params so use auto mode.
    config = getAutoConfig(
      availableWidth,
      availableWidth <= MIN_PUB_CONTROL_WIDTH_OF_DESKTOP
    );
  }
  if (config.validationError) {
    user().error('AMP-AD', config.validationError);
    // There was an error in pub params and we logged it in console.
    // Return 0 as height to hide slot.
    return 0;
  }
  element.setAttribute(ExternalCorePubVars.ROWS_NUM, config.numberOfRows);
  element.setAttribute(ExternalCorePubVars.COLUMNS_NUM, config.numberOfColumns);
  element.setAttribute(ExternalCorePubVars.UI_TYPE, config.layoutType);

  return config.slotHeight;
}
