// @flow
// Adopted from https://github.com/iqnivek/react-circular-progressbar
import React, { useState, useEffect, useMemo } from 'react';
import styled from 'styled-components';
import {
  MAX_X,
  MAX_Y,
  DEFAULT_SIZE,
  DEFAULT_FG_COLOR,
  DEFAULT_BG_COLOR,
  DEFAULT_STROKE_WIDTH,
  ANIMATION_DELAY,
  CENTER_X,
  CENTER_Y,
  FULL_RADIUS,
  MAX_PERCENTAGE,
  MIN_PERCENTAGE,
} from './circular-progress-bar.constants';
import { Box } from '../../../primitives/box';
import { getPathColor, getTrailColor } from './circular-progress-bar.css-rules';
import type { CircularProgressBarProps } from './circular-progress-bar.proptypes';

const SVG = styled.svg`
  width: 100%;
`;

const Path = styled.path`
  stroke: ${getPathColor};
  stroke-linecap: square;
  stroke-width: ${(props) => props.strokeWidth};
  transition: stroke-dashoffset 0.5s ease 0s;
`;

const Trail = styled.path`
  stroke: ${getTrailColor};
  stroke-width: ${(props) => props.strokeWidth};
`;

const FixedSizeBox = styled(Box)`
  height: ${({ size }) => size || DEFAULT_SIZE};
  width: ${({ size }) => size || DEFAULT_SIZE};
`;

const getPathProps = (
  strokeWidth: number,
  counterClockwise: boolean,
  percentage: number,
) => {
  // the radius of the path is defined to be in the middle, so in order for the path to
  // fit perfectly inside the 100x100 viewBox, need to subtract half the strokeWidth
  const pathRadius = FULL_RADIUS - strokeWidth / 2;
  const rotation = counterClockwise ? 1 : 0;

  // Move to center of canvas
  // Relative move to top canvas
  // Relative arc to bottom of canvas
  // Relative arc to top of canvas
  const pathDescription = `
      M ${CENTER_X},${CENTER_Y}
      m 0,-${pathRadius}
      a ${pathRadius},${pathRadius} ${rotation} 1 1 0,${2 * pathRadius}
      a ${pathRadius},${pathRadius} ${rotation} 1 1 0,-${2 * pathRadius}
    `;

  // Calculate pathStyles
  const diameter = Math.PI * 2 * pathRadius;
  const truncatedPercentage = Math.min(
    Math.max(percentage, MIN_PERCENTAGE),
    MAX_PERCENTAGE,
  );
  const dashoffset = ((100 - truncatedPercentage) / 100) * diameter;
  const pathStyles = {
    strokeDasharray: `${diameter}px ${diameter}px`,
    strokeDashoffset: `${counterClockwise ? -dashoffset : dashoffset}px`,
    strokeLinecap: 'butt',
  };

  return {
    pathDescription,
    pathStyles,
  };
};

export const CircularProgressBar = ({
  color = DEFAULT_FG_COLOR,
  bgColor = DEFAULT_BG_COLOR,
  percentage,
  className,
  strokeWidth = DEFAULT_STROKE_WIDTH,
  initialAnimation = false,
  counterClockwise = false,
  size,
}: CircularProgressBarProps) => {
  const [_percentage, setPercentage] = useState(
    initialAnimation ? 0 : percentage,
  );

  useEffect(() => {
    const timeout = setTimeout(
      () => setPercentage(percentage),
      ANIMATION_DELAY,
    );
    return () => clearTimeout(timeout);
  }, [percentage]);

  const { pathDescription, pathStyles } = useMemo(
    () => getPathProps(strokeWidth, counterClockwise, _percentage),
    [_percentage, counterClockwise, strokeWidth],
  );

  return (
    <FixedSizeBox
      size={size}
      role="progressbar"
      aria-valuenow={percentage}
      aria-valuemin="0"
      aria-valuemax="100"
    >
      <SVG className={className} viewBox={`0 0 ${MAX_X} ${MAX_Y}`}>
        <Trail
          d={pathDescription}
          strokeWidth={strokeWidth}
          fillOpacity={0}
          bgColor={bgColor}
        />

        <Path
          d={pathDescription}
          strokeWidth={strokeWidth}
          fillOpacity={0}
          style={pathStyles}
          color={color}
        />
      </SVG>
    </FixedSizeBox>
  );
};

CircularProgressBar.displayName = 'CircularProgressBar';
