/*
Copyright (c) 2018-2019 Uber Technologies, Inc.

This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/

/* global document cancelIdleCallback requestIdleCallback */
import * as React from 'react';
import axe from 'axe-core';
import { Layer, TetherBehavior, TETHER_PLACEMENT } from '../layer/index.js';
import { Paragraph3, Caption1 } from '../typography/index.js';
import { styled } from '../styles/index.js';
import { ThemeContext } from '../styles/theme-provider.js';

function validateNode(node) {
  return new Promise((resolve, reject) => {
    axe.run(node, {
      reporter: 'v2'
    }, (error, results) => {
      if (error) reject(error);
      resolve(results.violations);
    });
  });
}

function segmentViolationsByNode(violations) {
  const nodes = violations.reduce((map, violation) => {
    violation.nodes.forEach(node => {
      if (!map[node.target]) {
        map[node.target] = [violation];
      } else {
        map[node.target] = map[node.target].push(violation);
      }
    });
    return map;
  }, {});
  return Object.entries(nodes);
}

const ViolationContainer = styled('div', ({
  $theme,
  $top,
  $left
}) => {
  return {
    backgroundColor: $theme.colors.mono100,
    boxShadow: $theme.lighting.shadow600,
    position: 'absolute',
    padding: $theme.sizing.scale400,
    top: $top,
    left: $left
  };
});
ViolationContainer.displayName = "ViolationContainer";

function Violation(props) {
  const [offset, setOffset] = React.useState({
    top: 0,
    left: 0
  });
  const [anchor, setAnchor] = React.useState(null);
  const [popper, setPopper] = React.useState(null);
  const [isHovered, setIsHovered] = React.useState(false);
  const theme = React.useContext(ThemeContext);

  const handleMouseEnter = () => setIsHovered(true);

  const handleMouseLeave = () => setIsHovered(false);

  React.useEffect(() => {
    const node = document.querySelector(props.target);

    if (node) {
      setAnchor(node);
      node.setAttribute('style', `border: solid 1px ${theme.colors.negative300};`);
      node.addEventListener('mouseenter', handleMouseEnter);
      node.addEventListener('mouseleave', handleMouseLeave);
    }

    return () => {
      if (node) {
        node.removeEventListener('mouseenter', handleMouseEnter);
        node.removeEventListener('mouseleave', handleMouseLeave);
      }
    };
  }, [props.target]);
  if (!isHovered) return null;
  return React.createElement(Layer, null, React.createElement(TetherBehavior, {
    anchorRef: anchor,
    popperRef: popper,
    onPopperUpdate: update => setOffset(update.popper),
    placement: TETHER_PLACEMENT.bottom
  }, React.createElement(ViolationContainer, {
    ref: setPopper,
    $top: `${offset.top}px` || '0px',
    $left: `${offset.left}px` || '0px'
  }, React.createElement(Caption1, null, props.target), props.violations.map((violation, index) => React.createElement(Paragraph3, {
    key: index
  }, violation.description)))));
}

export default function A11y(props) {
  const [violations, setViolations] = React.useState([]);
  const [idleID, setIdleID] = React.useState(null);
  const child = React.useRef(null);
  React.useEffect(() => {
    if (child.current) {
      if (idleID) {
        cancelIdleCallback(idleID);
        setIdleID(null);
      }

      const id = requestIdleCallback(() => {
        validateNode(child.current).then(setViolations);
      });
      setIdleID(id);
    }
  }, [props.children]);
  const violationsByNode = segmentViolationsByNode(violations);
  return React.createElement(React.Fragment, null, React.createElement("span", {
    ref: child
  }, props.children), React.createElement("div", null, violationsByNode.map(([node, violations], index) => // eslint-disable-next-line flowtype/no-weak-types
  React.createElement(Violation, {
    target: node,
    violations: violations,
    key: index
  }))));
}