import { format } from "date-fns";
import React from "react";
import {
    VictoryAxis, VictoryChart, VictoryGroup, VictoryLabel, VictoryLegend, VictoryLine,
    VictoryScatter, VictoryTheme, VictoryTooltip, VictoryVoronoiContainer
} from "victory";

import { QuestionWithHistoryFragment } from "../../fragments.generated";

interface Props {
  question: QuestionWithHistoryFragment;
}

let formatOptionName = (name: string) => {
  return name.length > 20 ? name.slice(0, 17) + "..." : name;
};

let getLength = (str: string): number => {
  // TODO - measure with temporary DOM element instead?
  const capitalLetterLengthMultiplier = 1.25;
  const smallLetterMultiplier = 0.8;
  const numUpper = (str.match(/[A-Z]/g) || []).length;
  const numSmallLetters = (str.match(/[fijlrt]/g) || []).length;
  const numSpaces = (str.match(/[\s]/g) || []).length;
  const length =
    str.length +
    -numUpper -
    numSmallLetters +
    numUpper * capitalLetterLengthMultiplier +
    (numSmallLetters + numSpaces) * smallLetterMultiplier;
  return length;
};

type DataSet = { date: Date; probability: number; name: string }[];

const dataAsXy = (data: DataSet) =>
  data.map((datum) => ({
    x: format(datum.date, "yyyy-MM-dd"),
    y: datum.probability,
    name: datum.name,
  }));

const colors = ["dodgerblue", "crimson", "seagreen", "darkviolet", "turquoise"];
// can't be replaced with React component, VictoryChart requires VictoryGroup elements to be immediate children
const getVictoryGroup = ({ data, i }: { data: DataSet; i: number }) => {
  return (
    <VictoryGroup color={colors[i] || "darkgray"} data={dataAsXy(data)} key={i}>
      <VictoryScatter
        name={`scatter-${i}`}
        size={({ active }) => (active ? 3.75 : 3)}
      />

      <VictoryLine name={`line-${i}`} />
    </VictoryGroup>
  );
};

export const HistoryChart: React.FC<Props> = ({ question }) => {
  let dataSetsNames = question.options
    .sort((a, b) => (a.probability < b.probability ? -1 : 1))
    .map((o) => o.name);
  dataSetsNames = [...new Set(dataSetsNames)].slice(0, 5); // take the first 5

  const isBinary =
    (dataSetsNames[0] === "Yes" && dataSetsNames[1] === "No") ||
    (dataSetsNames[0] === "No" && dataSetsNames[1] === "Yes");
  if (isBinary) {
    dataSetsNames = ["Yes"];
  }
  let dataSets: DataSet[] = [];
  let maxProbability = 0;
  let longestNameLength = 0;

  for (const name of dataSetsNames) {
    let newDataset: DataSet = [];
    let previousDate = -Infinity;
    for (let item of question.history) {
      const relevantItemsArray = item.options.filter((x) => x.name === name);
      const date = new Date(item.timestamp * 1000);
      if (
        relevantItemsArray.length == 1 &&
        item.timestamp - previousDate > 12 * 60 * 60
      ) {
        let relevantItem = relevantItemsArray[0];
        let result = {
          date,
          probability: relevantItem.probability,
          name: relevantItem.name,
        };
        maxProbability =
          relevantItem.probability > maxProbability
            ? relevantItem.probability
            : maxProbability;
        let length = getLength(formatOptionName(relevantItem.name));
        longestNameLength =
          length > longestNameLength ? length : longestNameLength;
        newDataset.push(result);
        previousDate = item.timestamp;
      }
    }
    dataSets.push(newDataset);
  }

  const letterLength = 7;
  const labelLegendStart = 45;

  const domainMax =
    maxProbability < 0.5 ? Math.round(10 * (maxProbability + 0.05)) / 10 : 1;
  const dataSetsLength = dataSets.length;
  const goldenRatio = (1 + Math.sqrt(5)) / 2;
  const width = 750;
  const height = width / goldenRatio;
  const padding = {
    top: 20,
    bottom: 50,
    left: 60,
    right: labelLegendStart + letterLength * longestNameLength,
  };

  const legendData = Array.from(Array(dataSetsLength).keys()).map((i) => ({
    name: formatOptionName(dataSetsNames[i]),
    symbol: { fill: colors[i] },
  }));

  return (
    <VictoryChart
      domainPadding={20}
      padding={padding}
      theme={VictoryTheme.material}
      height={height}
      width={width}
      containerComponent={
        <VictoryVoronoiContainer
          labels={({ datum }) => `Not shown`}
          labelComponent={
            <VictoryTooltip
              constrainToVisibleArea
              pointerLength={0}
              dy={-12}
              text={({ datum }) =>
                `${datum.name}: ${Math.round(datum.y * 100)}%`
              }
              style={{
                fontSize: 15,
                fill: "black",
                strokeWidth: 0.05,
              }}
              flyoutStyle={{
                stroke: "black",
                fill: "white",
              }}
              cornerRadius={0}
              flyoutPadding={7}
            />
          }
          voronoiBlacklist={
            ["line-0", "line-1", "line-2", "line-3", "line-4"]
            //Array.from(Array(5).keys()).map((x, i) => `line${i}`)
            // see: https://github.com/FormidableLabs/victory/issues/545
          }
        />
      }
      domain={{
        y: [0, domainMax],
      }}
    >
      <VictoryLegend
        x={width - labelLegendStart - letterLength * longestNameLength}
        y={height / 2 - 18 - (dataSetsLength - 1) * 13}
        orientation="vertical"
        gutter={20}
        style={{ border: { stroke: "white" }, labels: { fontSize: 15 } }}
        data={legendData}
      />

      {dataSets
        .slice(0, 5)
        .map((dataset, i) => getVictoryGroup({ data: dataset, i }))}
      <VictoryAxis
        // tickValues specifies both the number of ticks and where
        // they are placed on the axis
        // tickValues={dataAsXy.map((datum) => datum.x)}
        // tickFormat={dataAsXy.map((datum) => datum.x)}
        tickCount={7}
        style={{
          grid: { stroke: null, strokeWidth: 0.5 },
        }}
        //axisLabelComponent={
        //  <VictoryLabel dy={40} style={{ fontSize: 10, fill: "gray" }} />
        //}
        tickLabelComponent={
          <VictoryLabel
            dy={10}
            angle={-30}
            style={{ fontSize: 15, fill: "gray" }}
          />
        }
      />
      <VictoryAxis
        dependentAxis
        // tickFormat specifies how ticks should be displayed
        tickFormat={(x) => `${x * 100}%`}
        style={{
          grid: { stroke: "#D3D3D3", strokeWidth: 0.5 },
        }}
        tickLabelComponent={
          <VictoryLabel dy={0} style={{ fontSize: 15, fill: "gray" }} />
        }
      />
    </VictoryChart>
  );
};
