import {computedStyle} from '#core/dom/style';

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

import {getParentWindowFrameElement} from './service-helpers';

const AD_CONTAINER_PROP = '__AMP__AD_CONTAINER';

/**
 * Tags that are allowed to have fixed positioning
 * @const {!{[key: string]: boolean}}
 */
const CONTAINERS = {
  'AMP-FX-FLYING-CARPET': true,
  'AMP-LIGHTBOX': true,
  'AMP-STICKY-AD': true,
  'AMP-LIGHTBOX-GALLERY': true,
};

/**
 * Determines if an element is fixed-positioned.
 * OK to use, because it's only called from onLayoutMeasure
 * @param {!Element} el
 * @param {!Window} win
 * @return {boolean}
 */
function isPositionFixed(el, win) {
  const {position} = computedStyle(win, el);
  // We consider sticky positions as fixed, since they can be fixed.
  return position == 'fixed' || position == 'sticky';
}

/**
 * @param {!Element} element
 * @param {!Window} win
 * @return {boolean} whether the element position is allowed. If the element
 * belongs to CONTAINERS, it is allowed to be position fixed.
 * If the element has a position fixed ancestor, it is not allowed.
 * This should only be called when a layout on the page was just forced
 * anyway.
 */
export function isAdPositionAllowed(element, win) {
  let hasFixedAncestor = false;
  let containers = 0;
  let el = element;
  do {
    if (CONTAINERS[el.tagName]) {
      // The containers must not themselves be contained in a fixed-position
      // element. Continue the search.
      containers++;
      hasFixedAncestor = false;
    } else if (isPositionFixed(dev().assertElement(el), win)) {
      // Because certain blessed elements may contain a position fixed
      // container (which contain an ad), we continue to search the
      // ancestry tree.
      hasFixedAncestor = true;
    }
    el = el.parentElement;
  } while (el && el.tagName != 'BODY');
  return !hasFixedAncestor && containers <= 1;
}

/**
 * Returns the blessed container element tagName if the ad is contained by one.
 * This is called during layout measure.
 * @param {!Element} element
 * @return {?string}
 */
export function getAdContainer(element) {
  if (element[AD_CONTAINER_PROP] === undefined) {
    let el = element.parentElement;
    while (el && el.tagName != 'BODY') {
      if (CONTAINERS[el.tagName]) {
        return (element[AD_CONTAINER_PROP] = el.tagName);
      }
      el = el.parentElement;
    }
    element[AD_CONTAINER_PROP] = null;
  }
  return element[AD_CONTAINER_PROP];
}

/**
 * Gets the resource ID of the amp-ad element containing the passed node.
 * If there is no containing amp-ad tag, then null will be returned.
 * TODO(jonkeller): Investigate whether non-A4A use case is needed. Issue 11436
 * @param {!Element} node
 * @param {!Window} topWin
 * @return {?string}
 */
export function getAmpAdResourceId(node, topWin) {
  try {
    const frameParent = getParentWindowFrameElement(node, topWin).parentElement;
    if (frameParent.nodeName == 'AMP-AD') {
      return String(frameParent.getResourceId());
    }
  } catch (e) {}
  // Whether we entered the catch above (e.g. due to attempt to access
  // across xdomain boundary), or failed to enter the if further above, the
  // node is not within a friendly amp-ad tag. So, there is no amp-ad
  // resource ID. How to handle that is up to the caller, but see TODO above.
  return null;
}
