import React, { useState } from 'react';
import { Container, SvgWrapper } from 'app/shared/modules/chart/core';
import { generateStackedBars } from 'app/shared/modules/chart/bar/compute';
import { Grid } from 'app/shared/modules/chart/core/axes';
import { Axis } from 'app/shared/modules/chart/core/axes/Axis';
import { computeAxisTicks, computeTicksValues } from 'app/shared/modules/chart/core/axes/compute/cartesian';
import { Group } from 'app/shared/modules/chart/core/containers/Group';
import { maxBy, round } from 'lodash';
import { FillableBar } from 'app/shared/modules/chart/bar/FillableBar';
import { useDispatch, useSelector } from 'react-redux';
import { selectForecastChartData } from '../../store/forecast.selectors';
import { forecastSelectAggregation } from '../../store/forecast.actions';
import { FORECAST } from '../../store/forecast.constants';
import { Arrow } from 'app/shared/modules/chart/arrow/arrow';
import moneyPipe from 'app/shared/pipes/money.pipe';
import datePipe from 'app/shared/pipes/date.pipe';
import { Tooltip } from 'app/shared/modules/chart/core/tooltip/Tooltip';
import { ForecastChartTooltip } from './forecastChartTooltip';
import { getScreenWidth } from 'app/shared/store/ui/uiSelectors';
import { UI } from 'app/shared/store/ui/ui.constants';

export interface Props {
  margin?: { top: number, right: number, bottom: number, left: number };
  tickCount?: number;
  gridPadding?: number;
  paddingRight?: number;
  width: number;
  height: number;
}

const ForecastChart = ({
  margin = { top: 10, right: 15, bottom: 30, left: 40 },
  gridPadding = 16,
  tickCount = 4,
  paddingRight = 16,
  width,
  height,
}: Props) => {
  const chartData = useSelector(selectForecastChartData);
  const { maxValue, barSeries, selectedId } = chartData;
  const isLargeScreen = useSelector(getScreenWidth) >= UI.BREAK_POINTS.LG;
  const [ tooltip, setTooltip ] = useState({
    isVisible: false,
    content: {
      currentForecast: '',
      previousForecast: '',
      date: '',
      ratio: '',
      trend: '',
    },
    position: {},
  });

  const showTooltip = (data) => {
    setTooltip({
      isVisible: true,
      content: data.tooltipContent,
      position: data.tooltipPosition,
    });
  };

  const hideTooltip = () => {
    setTooltip({
      ...tooltip,
      isVisible: false,
    });
  };

  const calcWidth = width - margin.left - margin.right - gridPadding;
  const calcHeight = height - margin.top - margin.bottom - gridPadding;

  const xScaleStartOffset = 10;
  const yAxisTextOffset = 3;

  const barWidth = 16;

  const yTicksValues = computeTicksValues(maxValue, tickCount);
  const maxTickValue = maxBy(yTicksValues);
  const maxScale = maxValue > maxTickValue? maxValue : maxTickValue;
  const options = {
    barSeries,
    keys: [ 'value' ],
    minValue: 0,
    maxValue: maxScale,
    width: calcWidth,
    height: calcHeight,
    padding: 0,
    innerPadding: 0,
  };

  const barChart = generateStackedBars(options);
  const centerOffset = (calcWidth - (barChart.xScale((barSeries.length - 1).toString())! + barChart.xScale('0')!)) / 2 - xScaleStartOffset;

  const horizontalTicks = computeAxisTicks({
    width: calcWidth,
    height: calcHeight,
    scale: barChart.xScale,
    values: barChart.xScale.domain(),
    getLabel: (index) => barSeries[index].label.toString(),
    position: 'bottom',
    isSelected: (value) => barSeries[value].id === selectedId,
  });

  const verticalTicks = computeAxisTicks({
    width: calcWidth,
    height: calcHeight,
    scale: barChart.yScale,
    values: yTicksValues,
    getLabel: (value) => value.toLocaleString('en-US'),
    position: 'left',
  });

  const tooltipOffset = {
    bottom: 60,
    left: centerOffset + gridPadding + margin.left + barWidth / 2 + 8,
    right: centerOffset + gridPadding + margin.right + barWidth / 2,
  };

  const bars = barChart.bars.map((bar, index) => {
    const roundedFilledHeight = round(bar.height * round(bar.data.data.filledRatio / 100, 2));
    bar.filledHeight = roundedFilledHeight !== null && roundedFilledHeight !== undefined
      ? roundedFilledHeight
      : bar.height;

    const onEnter = () => {
      const tooltipPosition = isLargeScreen || index < 1 ? {
        left: bar.x + tooltipOffset.left,
        bottom: tooltipOffset.bottom,
      } : {
        right: width - bar.x - tooltipOffset.right,
        bottom: tooltipOffset.bottom,
      };

      const tooltipContent = {
        currentForecast: moneyPipe.format(bar.data.value),
        previousForecast: moneyPipe.format(bar.data.data.previousValue),
        date: datePipe.formatShortMonthWithYear(bar.data.data.date),
        ratio: bar.data.data.filledRatio,
        trend: (bar.data.value - bar.data.data.previousValue) / bar.data.value,
      };
      showTooltip({ tooltipPosition, tooltipContent });
    };

    const onLeave = () => {
      hideTooltip();
    };

    const dispatch = useDispatch();

    if (bar.data.data.type === FORECAST.TYPE_PAST) {
      return (
        <FillableBar
          key={bar.key}
          index={index}
          {...bar}
          extraClass={'c-chart__bar--unselectable c-chart__main-bar js-visible-bar'}
          animate
          onEnter={onEnter}
          onLeave={onLeave}
          width={barWidth}
        />
      );
    }

    return (
      <FillableBar
        key={bar.key}
        index={index}
        onClick={(data) => dispatch(forecastSelectAggregation(data.data.id))}
        {...bar}
        selected={bar.data.data.id === selectedId}
        animate
        onEnter={onEnter}
        onLeave={onLeave}
        width={barWidth}
      />
    );
  });

  const arrows = barChart.bars.map((bar) => {
    const { x, y, key } = bar;
    const { trend } = bar.data.data;
    return (
      <Arrow key={key} x={x} y={y} trend={trend}/>
    );
  });

  return (
    <div>
      <Container>
        {() => (
          <div>
            {/* @ts-ignore*/}
            <SvgWrapper height={height} margin={margin} width={width}>
              <pattern id="pattern-stripe"
                width="8" height="8"
                patternUnits="userSpaceOnUse"
                patternTransform="rotate(45)">
                <rect className="u-pattern-stripes" width="1" height="8" transform="translate(0,0)"/>
                <rect className="u-pattern-stripes" width="1" height="8" transform="translate(4,0)"/>
              </pattern>
              <pattern id="pattern-stripe-selected"
                width="8" height="8"
                patternUnits="userSpaceOnUse"
                patternTransform="rotate(45)">
                <rect className="u-pattern-stripes--selected" width="1" height="8" transform="translate(0,0)"/>
                <rect className="u-pattern-stripes--selected" width="1" height="8" transform="translate(4,0)"/>
              </pattern>
              <Axis {...verticalTicks} />
              <Group x={gridPadding} y={0}>
                <Group x={centerOffset} y={gridPadding}>
                  <Axis {...horizontalTicks} />
                </Group>
                <Grid width={calcWidth + paddingRight} height={calcHeight} xScale={null} yScale={barChart.yScale} yTicks={yTicksValues}/>
                <Group x={centerOffset} y={0}>
                  <g className="c-chart__bars">{bars}</g>
                  <g className="c-chart__arrows u-animate-fade-in">
                    {arrows}
                  </g>
                </Group>
              </Group>
              <Group x={-margin.left + gridPadding} y={calcHeight + margin.top + yAxisTextOffset}>
                <text className={'c-chart__unit-label'}>
                  <tspan>USD</tspan>
                  <tspan x={0} dy={'1em'}>/DAY</tspan>
                </text>
              </Group>
            </SvgWrapper>
          </div>
        )}
      </Container>
      {/* @ts-ignore*/}
      <Tooltip isVisible={tooltip.isVisible} position={tooltip.position} aside>
        {() => <ForecastChartTooltip
          currentForecast={tooltip.content.currentForecast}
          previousForecast={tooltip.content.previousForecast}
          date={tooltip.content.date}
          ratio={tooltip.content.ratio}
          trend={tooltip.content.trend}
        />}
      </Tooltip>
    </div>
  );
};

export { ForecastChart };
