import React, { useState, useRef, useEffect } from 'react';
import { Svg, G } from 'react-native-svg';
import { Dimensions, PanResponder, StyleSheet } from 'react-native';
// import { svgPathProperties } from 'svg-path-properties';
import { NavigationProp } from '@react-navigation/native';
import { Line } from './line';
import { Area } from './area';
import { YAxis } from './y-axis';
import { XAxis } from './x-axis';
import { SBox } from '../../common/s-components/layout/s-box';
import { TDualCoordinates, TArea, TScaleQuantile } from '../types';
import { fp as _ } from '../../common/utils/fp';
import { XAxisLabels } from './x-axis-labels';
import { YAxisLabels } from './y-axis-labels';
import { theme } from '../../app/theme';
import { SH6 } from '../../common/s-components/typography/s-h6';
import { SText } from '../../common/s-components/typography/s-text';
import { formatStream, getWeekday } from '../../common/transducers';
import {
  buildScalesMulti,
  buildPath,
  buildPathArea,
  getGraphTotal,
  formatDate,
  getGraphPercentage,
  getTotalFromAreas,
} from '../transducers';
import { SLineDot } from '../s-components/s-line-dot';
import { SHoverLine } from '../s-components/s-hover-line';
import { SDot } from '../s-components/s-dot';
import {
  Noopable,
  TCoordinate,
  TCoordinates,
  TGraphMaxes,
} from '../../common/types';
import { SPointDate } from '../s-components/s-point-date';
import { STotal } from '../s-components/s-total';

type TProps = {
  navigation: NavigationProp<any>;
  areas: TArea[];
  areasReversed: TArea[];
  totals: Record<string, number>;
  maxes: TGraphMaxes;
  ticks: number[];
  label?: string;
};

export const CumulativeGraphAnimated: React.FC<TProps> = props => {
  const {
    areas,
    areasReversed,
    maxes: { xMin, xMax, yMax },
    ticks,
    label,
  } = props;
  const [labels, setLabels] = useState<Record<string, string>>({});
  const [pointDate, setPointDate] = useState(null);
  const [total, setTotal] = useState(null);
  const graphWidth = useRef(Dimensions.get('window').width);
  const graphHeight = useRef(Dimensions.get('window').height);

  const xScalesQuantRef = useRef([]);

  const cursors: Record<string, React.RefObject<any>> = {};

  const line = useRef();

  const bgLayer = useRef();

  const getSizes = () => {
    const padding = 56;
    const paddingTop = 27;
    const paddingBottom = 123;
    const svgHeight = graphHeight.current - 38;
    const innerWidth = graphWidth.current - padding * 2 - 171;
    const innerHeight = svgHeight - paddingBottom;

    return {
      height: svgHeight,
      width: graphWidth.current,
      padding,
      paddingTop,
      paddingBottom,
      innerHeight,
      innerWidth,
      xAxisOffset: padding + 61,
      xAxisOffsetRight: innerWidth,
      offsetTop: 15,
    };
  };

  const getScales = () => {
    return buildScalesMulti({
      ...getSizes(),
      xMin,
      xMax,
      yMax,
      areas,
      // areasRaw,
    });
  };

  const setXScalesQuant = () => {
    const { xScalesQuant } = getScales();
    // @ts-ignore
    xScalesQuantRef.current = xScalesQuant;
  };

  useEffect(() => {
    setXScalesQuant();
  }, [areas, graphWidth.current]);

  const setDotPosition = (ref: React.RefObject<any>, target: TCoordinate) => {
    const { xAxisOffset, offsetTop } = getSizes();

    ref.current.setNativeProps({
      style: {
        transform: [
          { translateX: target.x - 12 / 2 - xAxisOffset },
          {
            // @ts-ignore
            translateY: _.pathOr(0, 'y', target) - 12 / 2 + offsetTop + 1,
          },
        ],
        opacity: 1,
      },
    });
  };

  const setLinePosition = (target: TCoordinate) => {
    const { xAxisOffset, offsetTop } = getSizes();

    const targetX = target.x;
    // @ts-ignore
    line.current!.setNativeProps({
      style: {
        transform: [
          { translateX: targetX - 1 - xAxisOffset },
          { translateY: offsetTop + 1 },
        ],
        opacity: 1,
      },
    });
  };

  const setZeroOpacity = (ref: React.RefObject<any>) => {
    ref.current.setNativeProps({
      style: {
        opacity: 0,
      },
    });
  };

  const setZeroOpacityAll = () => {
    _.forEach(setZeroOpacity)(cursors);
    setZeroOpacity(line);
    setZeroOpacity(bgLayer);
  };

  const setLabelDate = (pageX: number | null) => {
    const { xScalesQuant } = getScales();
    const target = xScalesQuant[0](pageX!);
    const value = formatDate((target || {}).x);

    if (pointDate !== value) {
      // @ts-ignore
      setPointDate(pageX ? value : null);
    }
  };

  const getTotal = (pageX: number, totals: Record<string, number>) => {
    let total;

    if (!pageX && !totals) {
      total = null;
    } else {
      const { xScalesQuant } = getScales();
      const target = pageX ? xScalesQuant[0](pageX) || {} : null;
      total = getTotalFromAreas(target)(totals);
    }

    return total;
  };

  const setNewTotal = (
    pageX: number | null,
    totals: Record<string, number> | null
  ): void => {
    let total;

    if (!pageX && !totals) {
      total = null;
    } else {
      const { xScalesQuant } = getScales();
      const target = pageX ? xScalesQuant[0](pageX) || {} : null;
      total = getGraphTotal(target)(totals!);
    }
    // @ts-ignore
    setTotal(total);
  };

  const setNewLabels = (newLabels: Record<string, string>) => {
    if (!_.isEqual(labels, newLabels)) {
      setLabels(newLabels);
    }
  };

  const removeZeroOpacity = (ref: React.RefObject<any>) => {
    ref.current.setNativeProps({
      style: {
        opacity: 1,
      },
    });
  };

  const getPathArea = (data: TDualCoordinates) => {
    const { yScale, xScale } = getScales();
    return buildPathArea({ xScale, yScale, data });
  };

  const getPath = (data: TCoordinates) => {
    const { xScale, yScale } = getScales();
    return buildPath({ xScale, yScale, data });
  };

  const getFormattedLabel = (
    pageX: number,
    xScaleQuant: Noopable<TScaleQuantile>
  ) => {
    return formatStream((xScaleQuant(pageX) || {}).y);
  };

  const panResponder = PanResponder.create({
    onStartShouldSetPanResponder: _.T,
    onStartShouldSetPanResponderCapture: _.T,
    onMoveShouldSetPanResponder: _.T,
    onMoveShouldSetPanResponderCapture: _.T,
    onPanResponderGrant: evt => {
      const { pageX } = evt.nativeEvent;
      const { xScalesArea } = getScales();
      const { areasReversed: areas, totals } = props;
      const targets = _.map((scale: Noopable<TScaleQuantile>) => scale(pageX))(
        xScalesArea
      );
      const lastIndex = _.length(targets) - 1;

      let exampleTarget: TCoordinate;
      let labels: Record<string, string> = {};

      targets.forEach((target, i) => {
        const { source } = areas[i];

        if (target && !exampleTarget) {
          exampleTarget = target;
        }

        if (target) {
          setDotPosition(cursors[source], target);
        }

        if (target) {
          labels[source] = getFormattedLabel(
            pageX,
            xScalesQuantRef.current[lastIndex - i]
          );
          labels[`${source}Percent`] = getGraphPercentage(
            getTotal(pageX, totals),
            // @ts-ignore
            (xScalesQuantRef.current[lastIndex - i](pageX) || {}).y
          );
        } else {
          labels[`${source}Percent`] = '';
          labels[source] = '';
        }
      });

      // @ts-ignore
      if (exampleTarget) {
        setLinePosition(exampleTarget);
        setNewLabels(labels);
        setLabelDate(pageX);
        setNewTotal(pageX, totals);
        removeZeroOpacity(bgLayer);
      }
    },
    onPanResponderMove: evt => {
      const { pageX } = evt.nativeEvent;
      const { xScalesArea } = getScales();
      const { areasReversed: areas, totals } = props;
      const targets = _.map((scale: Noopable<TScaleQuantile>) => scale(pageX))(
        xScalesArea as Noopable<TScaleQuantile>[]
      );
      const lastIndex: number = _.length(targets) - 1;

      let exampleTarget: TCoordinate;
      let labels: Record<string, string> = {};

      targets.forEach((target, i) => {
        const { source } = areas[i];

        if (target && !exampleTarget) {
          exampleTarget = target;
        }
        if (target) {
          setDotPosition(cursors[source], target);
        }

        if (target) {
          labels[source] = getFormattedLabel(
            pageX,
            xScalesQuantRef.current[lastIndex - i]
          );
          labels[`${source}Percent`] = getGraphPercentage(
            getTotal(pageX, totals),
            // @ts-ignore
            (xScalesQuantRef.current[lastIndex - i](pageX) || {}).y
          );
        } else {
          labels[source] = '';
          labels[`${source}Percent`] = '';
        }
      });

      // @ts-ignore
      if (exampleTarget) {
        setLinePosition(exampleTarget);
        setLabelDate(pageX);
        setNewTotal(pageX, totals);
        setNewLabels(labels);
        removeZeroOpacity(bgLayer);
      } else {
        setZeroOpacityAll();
        setLabelDate(null);
        setNewTotal(null, null);
        setNewLabels({});
      }
    },
    onPanResponderTerminationRequest: _.T,
    // onPanResponderRelease: () => {
    //   this.setZeroOpacityAll();
    //   this.setLabelDate(null);
    //   this.setNewTotal(null, null);
    //   this.setNewLabels({});
    // },
    onShouldBlockNativeResponder: _.T,
  });

  const {
    height,
    innerWidth,
    innerHeight,
    padding,
    xAxisOffset,
    xAxisOffsetRight,
    offsetTop,
  } = getSizes();
  const { xScale, yScale } = getScales();
  const yTicks = [0, yMax / 2, yMax];
  const hideGraph = _.any(_.isNil)([yMax, xMax, xMin]);

  return (
    <SBox bg="fiord" position="relative">
      <SBox position="absolute" left={56} top={3}>
        <SH6 color={theme.colors.periwinkleGray}>{label}</SH6>
      </SBox>

      <SBox
        position="absolute"
        width={251}
        right={0}
        top={-11}
        mr={16}
        px={16}
        pt={13}
        pb={7}
        borderRadius={4}
        overflow="hidden"
      >
        <SBox
          style={StyleSheet.absoluteFill}
          opacity={0}
          bg="ebonyClay"
          ref={bgLayer}
        />
        <SBox
          color="periwinkleGray"
          flexDirection="row"
          justifyContent="space-between"
          alignItems="baseline"
          height={24}
        >
          <SPointDate>
            {pointDate ? `${getWeekday(pointDate!)}, ${pointDate}:` : null}
          </SPointDate>
          <STotal testID="cumulative-graph-total">
            {total ? `${total}` : null}
          </STotal>
        </SBox>
        <SBox flexDirection="column">
          {areas.map(area => {
            return (
              <SBox
                key={area.source}
                flexDirection="row"
                justifyContent="space-between"
                width={1}
                padding={0}
                mb={5.3}
                height={14.7}
              >
                <SBox
                  flexDirection="row"
                  alignItems="center"
                  padding={0}
                  flexShrink={1}
                  flexGrow={1}
                  flexBasis="auto"
                  pr={6}
                >
                  <SDot size={10} bg={area.color} mr={10} />
                  <SBox flex={1}>
                    <SText numberOfLines={1} sm>
                      {area.sourceName}
                    </SText>
                  </SBox>
                </SBox>
                {labels[area.source] ? (
                  <SText testID="cumulative-graph-value">
                    <SText sm bold>
                      {labels[area.source]}{' '}
                    </SText>
                    <SText sm normal>
                      {labels[`${area.source}Percent`]}
                    </SText>
                  </SText>
                ) : null}
              </SBox>
            );
          })}
        </SBox>
      </SBox>

      <XAxisLabels
        ticks={ticks}
        scale={xScale}
        yOffset={innerHeight + offsetTop}
        variant="large"
      />

      <YAxisLabels
        ticks={yTicks}
        scale={yScale}
        offsetLeft={padding}
        offsetTop={1}
        variant="big"
      />

      <SBox pt={offsetTop}>
        <Svg height={height - 4} width={innerWidth}>
          {!hideGraph ? (
            <>
              <YAxis
                ticks={yTicks}
                scale={yScale}
                xOffsetRight={xAxisOffsetRight}
                xOffset={padding}
              />

              <XAxis ticks={ticks} scale={xScale} yOffset={innerHeight} />

              {areasReversed.map(area => {
                // if (i !== 0) return null;

                if (area.isEmpty) {
                  return null;
                }

                return (
                  <G transform="translate(0, 0)" key={area.source}>
                    <Area
                      path={getPathArea(area.dataWithGaps)}
                      color={area.color}
                      id={area.source}
                    />
                  </G>
                );
              })}

              {areasReversed.map(area => {
                // if (i !== 0) return null;

                if (area.isEmpty) {
                  return null;
                }

                return (
                  <G transform="translate(0, 0)" key={area.source}>
                    <Line
                      // testID="cumulative-graph-line"
                      path={getPath(area.lineWithGaps)}
                      color={area.color}
                    />
                  </G>
                );
              })}
            </>
          ) : null}
        </Svg>
      </SBox>

      <SBox
        style={StyleSheet.absoluteFill}
        ml={xAxisOffset - 1}
        mr={padding - 1}
        {...panResponder.panHandlers}
        // Pan responder doesn't work with views without color on android
        // https://stackoverflow.com/a/51833007/6190198
        bg="transparent"
      >
        {areasReversed.map(area => {
          if (!cursors[area.source]) {
            cursors[area.source] = useRef();
          }
          return (
            <SLineDot
              testID="cumulative-graph-line-dots"
              ref={cursors[area.source]}
              bg={area.color}
              key={area.source}
            />
          );
        })}
        <SBox mt={-23}>
          <SHoverLine ref={line} height={innerHeight - 27} />
        </SBox>
      </SBox>
    </SBox>
  );
};

CumulativeGraphAnimated.defaultProps = {
  label: 'Streams',
};

// static navigationOptions = {
//   header: null,
// };
