import { useTheme } from "@emotion/react";
import { Box, Typography, useMediaQuery } from "@mui/material";
import * as d3 from "d3";
import React, { useEffect, useRef } from "react";

const CandleStickChart = (props) => {
  const theme = useTheme();
  const isWindowXS = useMediaQuery(theme.breakpoints.down("sm"));
  const {
    compareToEntry = "Positive",
    fontSize = 9,
    data = {},
    volatilityData = {},
    lineData = [],
    supplyAndDemandLines = [],
    untouchedLines = [],
    chartClass = "candlestickChart1",
    colors = {
      gradientBlue: ["#5D5FEF", "#41E7B0"],
      gradientPink: ["#5D5FEF", "#EF5DA8"],
      gradientYellow: ["#FFEB3A", "#FF8F50"],
      blue: "#5D5FEF",
      up: ["#5D5FEF", "#EF5DA8"],
      middle: ["#FFEB3A", "#FF8F50"],
      down: ["#5D5FEF", "#41E7B0"],
      line: "#707070",
      green: "#10C041",
      red: "#DA3737",
    },
    arrowSize = 30,
    tickerName = "",
    timeFrame = "",
    maxCandlesticksInView = 400,
    onDrag,
  } = props;

  const entryValue =
    lineData.find((f) => f.name === "entry") === undefined
      ? 0
      : Number(lineData.find((f) => f.name === "entry").value);
  const targetValue =
    lineData.find((f) => f.name === "target") === undefined
      ? 0
      : Number(lineData.find((f) => f.name === "target").value);
  const stopLossValue =
    lineData.find((f) => f.name === "stop loss") === undefined
      ? 0
      : Number(lineData.find((f) => f.name === "stop loss").value);

  const ref = useRef();
  const containerRef = useRef();
  const targetRef = useRef(targetValue);
  const entryRef = useRef(entryValue);
  const stopLossRef = useRef(stopLossValue);

  const myClass = chartClass; // this can be passed in so multiple instances on same page
  let svg = useRef(null);
  let chartData = data?.price_data || [];
  chartData.map((m) => (m.datetime = new Date(m.datetime)));
  const minHeight = 0;
  const minWidth = 0;
  const CHART_OFFSET = 15;
  const margins = {
    left: 20,
    right: 90,
    top: 10,
    bottom: 40,
    volatility: 80,
    volatilityChart: 50,
    lineRight: 90,
  };

  /**
   * Usage for 'onDrag' callback prop:
   * - You can add this prop inside the 'drag' listener and pass the values for target, entry, and stoploss
   *
   *  onDrag?.(target, entry, stopLoss);
   */

  useEffect(() => {
    //copied this from another project
    svg.current = d3.select(ref.current);
    initiateChart();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [
    ref,
    svg,
    data,
    volatilityData,
    lineData,
    supplyAndDemandLines,
    untouchedLines,
    timeFrame,
    maxCandlesticksInView,
    arrowSize,
    props,
  ]);

  chartData.map((m) => (m.low = +m.low));
  chartData.map((m) => (m.high = +m.high));
  chartData.map((m) => (m.close = +m.close));
  chartData.map((m) => (m.open = +m.open));
  lineData.map((m) => (m.value = +m.value));

  const untouchedLineData = untouchedLines;

  const tempSupplyAndDemandLines = [];
  supplyAndDemandLines.forEach((d) => {
    const lowestValue = Math.min(d.support_price, d.resistance_price);
    const highestValue = Math.max(d.support_price, d.resistance_price);

    tempSupplyAndDemandLines.push({
      type: "highest",
      value: highestValue,
      stroke: "#971DA6",
      strokeWidth: d.strokeWidth,
      strokeDashArray: d.strokeDashArray,
      label: d.interval,
    });
    tempSupplyAndDemandLines.push({
      type: "lowest",
      value: lowestValue,
      stroke: "#00EA41",
      strokeWidth: d.strokeWidth,
      strokeDashArray: d.strokeDashArray,
      label: d.interval,
    });
  });

  const initiateChart = () => {
    const parentElement = svg.current.node().parentElement;
    const width = Math.max(parentElement.clientWidth, minWidth);
    const height = Math.max(parentElement.clientHeight, minHeight);
    let arrowHeight = arrowSize;
    const lineWidth = width - CHART_OFFSET;
    volatilityData.atr.map((m) => (m.datetime = new Date(m.datetime)));
    margins.volatility = Math.min(height * 0.2, 80);
    margins.volatilityChart = margins.volatility - 30;
    margins.lineRight =
      (width - margins.left - margins.right) * (isWindowXS ? 0.175 : 0.085);
    const chartWidth =
      width - margins.left - margins.right - margins.lineRight - CHART_OFFSET;

    const chartHeight =
      height - margins.top - margins.bottom - margins.volatility;
    let xTicks = [];
    chartData = chartData.sort((a, b) => d3.ascending(a.datetime, b.datetime));
    chartData.forEach((d) => xTicks.push(d.datetime));
    const allTicksCount = xTicks.length;
    const xTicksAll = JSON.parse(JSON.stringify(xTicks)).map(
      (m) => (m = new Date(m))
    );
    let xScaleAll = d3.scaleBand().domain(xTicks).range([0, chartWidth]);
    const xScaleLinearAll = d3
      .scaleLinear()
      .domain([0, xTicks.length - 1])
      .range([0, chartWidth]);

    let xScaleLinear = d3
      .scaleLinear()
      .domain([0, xTicks.length - 1])
      .range([0, chartWidth]);
    let xBandwidth = xScaleAll.bandwidth();
    let yMax = d3.max(chartData, (d) =>
      d3.max([d.open, d.close, d.high, d.low])
    );
    let yMin = d3.min(chartData, (d) => {
      return d3.min([d.open, d.close, d.high, d.low]);
    });

    let visibleYMin = yMin;
    let visibleYMax = yMax;

    let yScale = d3
      .scaleLinear()
      .domain([yMin, yMax])
      .range([chartHeight - margins.top, margins.top]);

    const yScaleAll = d3
      .scaleLinear()
      .domain([yMin, yMax])
      .range([chartHeight - margins.top, margins.top]);
    const volatilityYExtent = d3.extent(
      volatilityData.atr || [],
      (d) => d.value
    );

    let yScaleVolatility = d3
      .scaleLinear()
      .domain(volatilityYExtent)
      .range([margins.volatilityChart, 0]);

    let yScaleVolatilityRect = d3
      .scaleLinear()
      .domain([0, volatilityYExtent[1] - volatilityYExtent[0]])
      .range([0, margins.volatilityChart]);

    const icons = { Positive: "\uf3bf", Negative: "\uf3be" };

    let currentFormat = d3.timeFormat(`%a %d %b '%y`);

    svg.current
      .select(".horizontalLine")
      .attr("x1", margins.left)
      .attr("x2", chartWidth + margins.lineRight)
      .attr("stroke", "#141227")
      .attr("stroke-width", 0.5)
      .attr("stroke-dasharray", "2,2")
      .attr("pointer-events", "none")
      .attr("display", "none");

    svg.current
      .select(".verticalLine")
      .attr("y1", margins.top)
      .attr("y2", chartHeight)
      .attr("stroke", "#141227")
      .attr("stroke-width", 0.5)
      .attr("stroke-dasharray", "2,2")
      .attr("pointer-events", "none")
      .attr("display", "none");

    svg.current
      .select(".dateRect")
      .attr(
        "width",
        measureWidth(currentFormat(xTicks[0]), timeFrame === "hourly" ? 25 : 15)
      )
      .attr("height", fontSize * 2)
      .attr("fill", "#A0A0A0")
      .attr("rx", 2)
      .attr("ry", 2)
      .attr(
        "transform",
        "translate(" +
          -measureWidth(
            currentFormat(xTicks[0]),
            timeFrame === "hourly" ? 25 : 15
          ) /
            2 +
          "," +
          (chartHeight + 5) +
          ")"
      )
      .attr("display", "none");

    svg.current
      .select(".dateRectLabel")
      .attr("fill", "white")
      .attr("text-anchor", "middle")
      .attr("transform", "translate(0," + (chartHeight + fontSize * 1.7) + ")")
      .attr("font-size", fontSize)
      .attr("display", "none");

    svg.current
      .select(".valueRect")
      .attr("width", margins.right * 0.8)
      .attr("height", fontSize * 2)
      .attr("fill", "url(#blueGradient)")
      .attr("rx", 5)
      .attr("ry", 5)
      .attr(
        "transform",
        "translate(" +
          (lineWidth - margins.right + CHART_OFFSET / 2 + 3) +
          ",-6)"
      )
      .attr("display", "none");

    svg.current
      .select(".valueLabel")
      .attr("x", 9)
      .attr("font-size", fontSize)
      .attr("text-anchor", "middle")
      .attr("fill", "white")
      .attr("padding", 12)
      .attr(
        "transform",
        "translate(" +
          (chartWidth +
            margins.left +
            margins.lineRight -
            3 +
            (margins.right - 3) / 2) +
          ",10.5)"
      )
      .text("hello")
      .attr("display", "none");

    svg.current.on("mousemove", (event) => {
      if (
        event.offsetX > margins.left &&
        event.offsetX < chartWidth + margins.left &&
        event.offsetY > margins.top &&
        event.offsetY < chartHeight + margins.left
      ) {
        const candlestickCrossHairLine = d3.selectAll(
          ".candlestickCrossHairLine"
        );

        candlestickCrossHairLine.attr("display", (d) => {
          const xScale = parseFloat(xScaleAll(d.datetime)) + 21;
          const offsetX = parseFloat(event.offsetX);
          const bandWidth = (xBandwidth * 0.85) / 2;
          const range1 = offsetX - bandWidth;
          const range2 = offsetX + bandWidth;

          if (xScale >= range1 && xScale <= range2) {
            return "block";
          }

          return "none";
        });

        d3.selectAll(".mouseoverItem").attr("display", "block");

        d3.select(".horizontalLine")
          .attr("y1", event.offsetY)
          .attr("y2", event.offsetY);

        d3.select(".verticalLine")
          .attr("x1", event.offsetX)
          .attr("x2", event.offsetX);

        d3.select(".dateRect").attr("x", event.offsetX);

        if (timeFrame === "hourly")
          currentFormat = d3.timeFormat(`%a %d %b '%y %H:%M`);

        const xValue =
          xTicks[xScaleLinear.invert(event.offsetX - margins.left).toFixed(0)];
        d3.select(".dateRectLabel")
          .attr("x", event.offsetX)
          .text(currentFormat(xValue));

        d3.select(".valueRect").attr("y", event.offsetY - 2);

        d3.select(".valueLabel")
          .attr("y", event.offsetY)
          .text(
            Number(
              d3.format(".1~f")(yScale.invert(event.offsetY - margins.top))
            ).toFixed(1)
          );
      } else {
        d3.selectAll(".mouseoverItem").attr("display", "none");
      }
    });

    svg.current
      .selectAll(".linearGradients")
      .attr("x1", "0%")
      .attr("x2", "100%")
      .attr("y1", "0%")
      .attr("y2", "0%");

    const gradientStopsPink = [
      { offset: "0%", color: colors.gradientPink[0] },
      { offset: "100%", color: colors.gradientPink[1] },
    ];

    const gradientStopsBlue = [
      { offset: "0%", color: colors.gradientBlue[0] },
      { offset: "100%", color: colors.gradientBlue[1] },
    ];
    const gradientStopsYellow = [
      { offset: "0%", color: colors.gradientYellow[0] },
      { offset: "100%", color: colors.gradientYellow[1] },
    ];
    const gradientStopsUp = [
      { offset: "0%", color: colors.up[0] },
      { offset: "100%", color: colors.up[1] },
    ];
    const gradientStopsMiddle = [
      { offset: "0%", color: colors.middle[0] },
      { offset: "100%", color: colors.middle[1] },
    ];

    const gradientStopsDown = [
      { offset: "0%", color: colors.down[0] },
      { offset: "100%", color: colors.down[1] },
    ];

    svg.current
      .select("#upGradient")
      .selectAll("stop")
      .data(gradientStopsUp)
      .join("stop")
      .attr("offset", (d) => d.offset)
      .attr("stop-color", (d) => d.color);
    svg.current
      .select("#middleGradient")
      .selectAll("stop")
      .data(gradientStopsMiddle)
      .join("stop")
      .attr("offset", (d) => d.offset)
      .attr("stop-color", (d) => d.color);
    svg.current
      .select("#downGradient")
      .selectAll("stop")
      .data(gradientStopsDown)
      .join("stop")
      .attr("offset", (d) => d.offset)
      .attr("stop-color", (d) => d.color);

    svg.current
      .select("#yellowGradient")
      .selectAll("stop")
      .data(gradientStopsYellow)
      .join("stop")
      .attr("offset", (d) => d.offset)
      .attr("stop-color", (d) => d.color);

    svg.current
      .select("#pinkGradient")
      .selectAll("stop")
      .data(gradientStopsPink)
      .join("stop")
      .attr("offset", (d) => d.offset)
      .attr("stop-color", (d) => d.color);

    svg.current
      .select("#blueGradient")
      .selectAll("stop")
      .data(gradientStopsBlue)
      .join("stop")
      .attr("offset", (d) => d.offset)
      .attr("stop-color", (d) => d.color);

    const zoom = d3
      .zoom()
      .scaleExtent([0, 200])
      .translateExtent([
        [0, 0],
        [
          chartWidth + margins.lineRight - 3 + (margins.right - 3) / 2,
          height - margins.top - margins.bottom,
        ],
      ])
      .on("zoom", zoomed);

    const zoomY = d3.zoom().scaleExtent([1, 10]).on("zoom", zoomedY);

    const startZoomLeft = xTicks.length - maxCandlesticksInView;
    const startZoomRight = xTicks.length - 1;

    svg.current.attr("width", width).attr("height", height);

    svg.current
      .select(".zoomRect")
      .attr("fill", "transparent")
      .attr("width", chartWidth)
      .attr("height", height - margins.top - margins.bottom)
      .attr("transform", "translate(" + margins.left + "," + margins.top + ")");

    svg.current
      .select(".zoomRectY")
      .attr("fill", "transparent")
      .attr("width", margins.lineRight + margins.right)
      .attr("height", chartHeight)
      .attr(
        "transform",
        "translate(" +
          (width - margins.right - margins.lineRight) +
          "," +
          margins.top +
          ")"
      );

    svg.current
      .select(".zoomRect")
      .call(zoom)
      .call(
        zoom.transform,
        d3.zoomIdentity
          .scale(
            chartWidth /
              (xScaleLinear(startZoomRight) - xScaleLinear(startZoomLeft))
          )
          .translate(-xScaleLinear(startZoomLeft), 0)
      );

    let gradientData = [],
      myOffset = {};
    const yVolatilityRange = volatilityYExtent[1] - volatilityYExtent[0];
    const volatilityRectFills = [
      "url(#downGradient)",
      "white",
      "url(#middleGradient)",
      "white",
      "url(#upGradient)",
    ];
    const volatilityBackgroundRectData = [];
    let previousKey = "";
    //always low to high

    Object.keys(volatilityData.quintile).forEach((d, i) => {
      if (i > 0) {
        volatilityBackgroundRectData.push({
          start: volatilityData.quintile[d],
          end: volatilityData.quintile[previousKey],
          fill: volatilityRectFills[i - 1],
        });
      }
      previousKey = d;
    });

    const volatilityBackgroundGroup = svg.current
      .select(".volatilityBackgroundGroup")
      .selectAll(".vBRGroup" + myClass)
      .data(volatilityBackgroundRectData)
      .join(function (group) {
        const enter = group.append("g").attr("class", "vBRGroup" + myClass);
        enter.append("rect").attr("class", "vBackgroundRect");
        return enter;
      });

    volatilityBackgroundGroup
      .select(".vBackgroundRect")
      .attr("x", margins.left)
      .attr(
        "y",
        (d) =>
          height -
          margins.bottom -
          margins.volatilityChart +
          yScaleVolatility(d.start)
      )
      .attr("width", chartWidth)
      .attr("height", (d) => yScaleVolatilityRect(d.start - d.end))
      .attr("fill", (d) => d.fill);

    const sDLineGroup = svg.current
      .select(".extraLines")
      .selectAll(".sDLineGroup" + myClass)
      .data(tempSupplyAndDemandLines)
      .join(function (group) {
        const enter = group.append("g").attr("class", "sDLineGroup" + myClass);
        enter.append("line").attr("class", "sDLine");
        enter.append("text").attr("class", "sDLabel");
        return enter;
      });

    sDLineGroup
      .select(".sDLine")
      .attr("x1", margins.left)
      .attr("x2", margins.left + chartWidth)
      .attr("stroke", (d) => d.stroke)
      .attr("stroke-width", (d) => d.strokeWidth)
      .attr("stroke-dasharray", (d) => d.strokeDashArray)
      .attr("y1", (d) => yScale(d.value) + margins.top)
      .attr("y2", (d) => yScale(d.value) + margins.top);

    sDLineGroup
      .select(".sDLabel")
      .attr("text-anchor", "end")
      .attr("font-size", fontSize)
      .attr("fill", (d) => d.stroke)
      .attr("stroke-width", (d) => d.strokeWidth)
      .attr("stroke-dasharray", (d) => d.strokeDashArray)
      .attr("y", (d) => yScale(d.value) + margins.top + 2)
      .attr("x", margins.left)
      .text((d) => d.label);

    const untouchedLineGroup = svg.current
      .selectAll(".untouchedLineGroup" + myClass)
      .data(untouchedLineData)
      .join(function (group) {
        const enter = group
          .append("g")
          .attr("class", "untouchedLineGroup" + myClass);
        enter.append("line").attr("class", "untouchedLine");
        enter.append("text").attr("class", "untouchedLabel");
        return enter;
      });

    untouchedLineGroup
      .select(".untouchedLine")
      .attr("x1", (d) => xScaleAll(new Date(d.startDate)))
      .attr("x2", margins.left + chartWidth)
      .attr("stroke", (d) => d.stroke)
      .attr("stroke-width", (d) => d.strokeWidth)
      .attr("stroke-dasharray", (d) => d.strokeDashArray)
      .attr("y1", (d) => yScale(d.value) + margins.top)
      .attr("y2", (d) => yScale(d.value) + margins.top);

    untouchedLineGroup
      .select(".untouchedLabel")
      .attr("font-size", fontSize)
      .attr("fill", (d) => d.stroke)
      .attr("stroke-width", (d) => d.strokeWidth)
      .attr("stroke-dasharray", (d) => d.strokeDashArray)
      .attr("y", (d) => yScale(d.value) + margins.top - 1)
      .attr("x", (d) => xScaleAll(new Date(d.startDate)))
      .text((d) => d.label);

    let janAlready = {};

    drawCandlestick();

    function zoomedY(event) {
      yScale = event.transform.rescaleY(yScaleAll);
      yScale.domain([
        yScale.domain()[0] < yMin ? yMin : yScale.domain()[0],
        yScale.domain()[1] > yMax ? yMax : yScale.domain()[1],
      ]);
      if (event.sourceEvent !== null) {
        drawCandlestick();
      }
    }

    function zoomed(event) {
      const candlestickCrossHairLine = d3.selectAll(
        ".candlestickCrossHairLine"
      );
      const mouseoverItem = d3.selectAll(".mouseoverItem");

      candlestickCrossHairLine.attr("display", "none");
      mouseoverItem.attr("display", "none");

      xScaleLinear = event.transform.rescaleX(xScaleLinearAll);
      xScaleLinear.domain(getXDomain(xScaleLinear.domain()));

      const zoomTicks = xTicks.filter(
        (f, i) => i >= xScaleLinear.domain()[0] && i <= xScaleLinear.domain()[1]
      );
      xScaleAll.domain(zoomTicks);
      xBandwidth = xScaleAll.bandwidth();
      const visibleData = chartData.filter(
        (f) =>
          f.datetime >= zoomTicks[0] &&
          f.datetime <= zoomTicks[zoomTicks.length - 1]
      );

      visibleYMin = d3.min(visibleData, (d) =>
        Math.min(d.high, d.low, d.open, d.close)
      );
      visibleYMax = d3.max(visibleData, (d) =>
        Math.max(d.high, d.low, d.open, d.close)
      );

      if (lineData.length > 0) {
        visibleYMin = Math.min(
          visibleYMin,
          d3.min(lineData, (d) => d.value)
        );
        visibleYMax = Math.max(
          visibleYMax,
          d3.max(lineData, (d) => d.value)
        );
      }
      if (tempSupplyAndDemandLines.length > 0) {
        visibleYMin = Math.min(
          visibleYMin,
          d3.min(tempSupplyAndDemandLines, (d) => d.value)
        );
        visibleYMax = Math.max(
          visibleYMax,
          d3.max(tempSupplyAndDemandLines, (d) => d.value)
        );
      }
      if (untouchedLineData.length > 0) {
        visibleYMin = Math.min(
          visibleYMin,
          d3.min(untouchedLineData, (d) => d.value)
        );
        visibleYMax = Math.max(
          visibleYMax,
          d3.max(untouchedLineData, (d) => d.value)
        );
      }

      arrowHeight = getArrowHeight(
        yScale(entryRef.current),
        yScale(targetRef.current)
      );

      svg.current
        .select(".zoomRectY")
        .call(zoomY.transform, d3.zoomIdentity)
        .call(
          zoomY.transform,
          d3.zoomIdentity
            .scale(
              Math.floor(
                chartHeight / (yScaleAll(visibleYMin) - yScaleAll(visibleYMax))
              )
            )
            .translate(0, -yScale(visibleYMax))
        );

      drawCandlestick();

      function getXDomain(myDomain) {
        myDomain[0] = parseInt(myDomain[0]) < 0 ? 0 : parseInt(myDomain[0]);
        myDomain[1] =
          parseInt(myDomain[1]) > allTicksCount
            ? allTicksCount
            : parseInt(myDomain[1]);

        return myDomain;
      }
    }

    function drawCandlestick() {
      janAlready = {};
      const line = d3
        .line()
        .defined((d) => xScaleAll(d.datetime) !== undefined)
        .x((d) => xScaleAll(d.datetime))
        .y((d) => yScaleVolatility(d.value));

      svg.current
        .select(".volatilityXAxis")
        .call(
          d3.axisBottom(xScaleLinear).tickSizeOuter(0).tickFormat(getTickFormat)
        )
        .attr("font-size", fontSize)
        .attr(
          "transform",
          "translate(" + margins.left + "," + (height - margins.bottom) + ")"
        );

      svg.current.selectAll(".volatilityXAxis line").attr("y1", 3);

      svg.current
        .selectAll(".volatilityXAxis text")
        .attr("transform", "translate(-20,11) rotate(-40)");

      svg.current
        .select(".volatilityXAxis path")
        .attr("d", "M0,0H" + (width - margins.left - margins.right));

      janAlready = {};
      svg.current
        .select(".xAxis")
        .call(d3.axisBottom(xScaleLinear).tickSizeOuter(0).tickFormat(""))
        .attr(
          "transform",
          "translate(" +
            margins.left +
            "," +
            (margins.top + chartHeight + 4) +
            ")"
        );

      svg.current.selectAll(".xAxis line").attr("y1", 3);

      svg.current
        .select(".xAxis path")
        .attr("d", "M0,0H" + (width - margins.left - margins.right));

      svg.current
        .select(".volatilityYAxis")
        .call(d3.axisRight(yScaleVolatility).tickSizeOuter(0).ticks(3))
        .attr(
          "transform",
          "translate(" +
            (width - margins.right) +
            "," +
            (height - margins.bottom - margins.volatilityChart) +
            ")"
        );

      svg.current.selectAll(".yAxis line").attr("x1", 3);

      svg.current.selectAll(".volatilityYAxis line").attr("x1", 3);

      svg.current
        .select(".volatilityBackgroundRect")
        .attr("width", chartWidth)
        .attr("height", margins.volatilityChart)
        .attr("fill", "url(#volatilityGradient)")
        .attr("transform", "translate(0,-10)")
        .attr(
          "transform",
          "translate(" +
            margins.left +
            "," +
            (height - margins.bottom - margins.volatilityChart) +
            ")"
        );

      svg.current
        .select(".volatilityClipPathRect")
        .attr("width", chartWidth)
        .attr("height", margins.volatility)
        .attr("fill", "none")
        .attr("transform", "translate(0,-10)");

      svg.current
        .select(".chartClipPathRect")
        .attr("width", width)
        .attr("height", chartHeight + margins.top + 5)
        .attr("fill", "none")
        .attr("transform", "translate(0,0)");

      svg.current
        .select(".volatilityPath")
        .attr("clip-path", "url(#volatilityClipPath)")
        .attr("fill", "none")
        .attr("stroke", colors.line)
        .attr("stroke-width", 3)
        .attr("d", line(volatilityData.atr || []))
        .attr(
          "transform",
          "translate(" +
            margins.left +
            "," +
            (height - margins.bottom - margins.volatilityChart) +
            ")"
        );

      svg.current
        .select(".yAxis")
        .attr("clip-path", "url(#chartClipPath)")
        .attr("font-size", fontSize)
        .call(d3.axisRight(yScale).tickSizeOuter(0).ticks(8))
        .attr(
          "transform",
          "translate(" +
            (width - margins.right - CHART_OFFSET) +
            "," +
            margins.top +
            ")"
        );

      svg.current
        .selectAll(".yAxis .tick text")
        .style("pointer-events", "none");

      svg.current.selectAll(".yAxis line").attr("x1", 3);

      svg.current.selectAll(".yAxis path").attr("display", "none");

      svg.current.selectAll(".axis text").style("fill", "#141227");

      svg.current
        .selectAll(".axis path")
        .style("stroke", "#141227")
        .style("stroke-width", 2)
        .style("stroke-linecap", "square");

      svg.current
        .selectAll(".extraLines")
        .attr("clip-path", "url(#chartClipPath)");

      const sDLineGroup = svg.current
        .selectAll(".extraLines")
        .selectAll(".sDLineGroup" + myClass)
        .data(tempSupplyAndDemandLines)
        .join(function (group) {
          const enter = group
            .append("g")
            .attr("class", "sDLineGroup" + myClass);
          enter.append("line").attr("class", "sDLine");
          enter.append("text").attr("class", "sDLabel");
          return enter;
        });

      sDLineGroup
        .select(".sDLine")
        .attr("x1", margins.left)
        .attr("x2", margins.left + chartWidth)
        .attr("stroke", (d) => d.stroke)
        .attr("stroke-width", (d) => d.strokeWidth)
        .attr("stroke-dasharray", (d) => d.strokeDashArray)
        .attr("y1", (d) => yScale(d.value) + margins.top)
        .attr("y2", (d) => yScale(d.value) + margins.top);

      sDLineGroup
        .select(".sDLabel")
        .attr("text-anchor", "end")
        .attr("font-size", fontSize)
        .attr("fill", (d) => d.stroke)
        .attr("stroke-width", (d) => d.strokeWidth)
        .attr("stroke-dasharray", (d) => d.strokeDashArray)
        .attr("y", (d) => yScale(d.value) + margins.top + 2)
        .attr("x", margins.left)
        .text((d) => d.label);

      const untouchedLineGroup = svg.current
        .selectAll(".extraLines")
        .selectAll(".untouchedLineGroup" + myClass)
        .data(untouchedLineData)
        .join(function (group) {
          const enter = group
            .append("g")
            .attr("class", "untouchedLineGroup" + myClass);
          enter.append("line").attr("class", "untouchedLine");
          enter.append("text").attr("class", "untouchedLabel");
          return enter;
        });

      untouchedLineGroup
        .select(".untouchedLine")
        .attr("x1", (d) => xScaleAll(new Date(d.startDate)))
        .attr("x2", margins.left + chartWidth)
        .attr("stroke", (d) => d.stroke)
        .attr("stroke-width", (d) => d.strokeWidth)
        .attr("stroke-dasharray", (d) => d.strokeDashArray)
        .attr("y1", (d) => yScale(d.value) + margins.top)
        .attr("y2", (d) => yScale(d.value) + margins.top);

      untouchedLineGroup
        .select(".untouchedLabel")
        .attr("font-size", fontSize)
        .attr("fill", (d) => d.stroke)
        .attr("stroke-width", (d) => d.strokeWidth)
        .attr("stroke-dasharray", (d) => d.strokeDashArray)
        .attr("y", (d) => yScale(d.value) + margins.top - 1)
        .attr("x", (d) => xScaleAll(new Date(d.startDate)))
        .text((d) => d.label);

      const candleStickGroup = svg.current
        .selectAll(".extraLines")
        .selectAll(".candleStickGroup" + myClass)
        .data(
          chartData.filter(
            (f) =>
              f.datetime >= xScaleAll.domain()[0] &&
              f.datetime <= xScaleAll.domain()[xScaleAll.domain().length - 1]
          )
        )
        .join(function (group) {
          const enter = group
            .append("g")
            .attr("class", "candleStickGroup" + myClass);
          enter.append("line").attr("class", "candlestickCrossHairLine");
          enter.append("line").attr("class", "candlestickLine");
          enter.append("rect").attr("class", "candlestickRect");
          return enter;
        });

      candleStickGroup.attr(
        "transform",
        "translate(" + margins.left + "," + margins.top + ")"
      );
      candleStickGroup
        .select(".candlestickRect")
        .attr("rx", xBandwidth * 0.05)
        .attr("ry", xBandwidth * 0.05)
        .attr("x", (d) => xScaleAll(d.datetime) - xBandwidth * 0.425)
        .attr("y", (d) => yScale(Math.max(d.open, d.close)))
        .attr(
          "height",
          (d) =>
            yScale(visibleYMin) -
            yScale(Math.abs(d.open - d.close) + visibleYMin)
        )
        .attr("width", xBandwidth * 0.85)
        .attr("fill", (d) => (d.close > d.open ? colors.green : colors.red))
        .attr("pointer-events", "none")
        .on("mouseover", (event, d) => {
          console.log(d);
        });

      candleStickGroup
        .select(".candlestickLine")
        .attr("rx", xBandwidth * 0.05)
        .attr("ry", xBandwidth * 0.05)
        .attr("x1", (d) => xScaleAll(d.datetime))
        .attr("x2", (d) => xScaleAll(d.datetime))
        .attr("y1", (d) => yScale(d.low))
        .attr("y2", (d) => yScale(d.high))
        .attr("pointer-events", "none")
        .attr("fill", "none")
        .attr("stroke-width", 1)
        .attr("stroke", colors.line);

      candleStickGroup
        .select(".candlestickCrossHairLine")
        .attr("x1", (d) => xScaleAll(d.datetime))
        .attr("x2", (d) => xScaleAll(d.datetime))
        .attr("y1", margins.top)
        .attr("y2", chartHeight)
        .attr("stroke", "#141227")
        .attr("stroke-width", 0.5)
        .attr("stroke-dasharray", "2,2")
        .attr("pointer-events", "none")
        .attr("display", "none");

      svg.current
        .select(".arrowIcon")
        .attr("clip-path", "url(#chartClipPath)")
        .attr("font-size", arrowHeight)
        .attr(
          "y",
          (compareToEntry === "Positive"
            ? yScale(entryRef.current)
            : yScale(targetRef.current)) -
            arrowHeight * 0.2
        )
        .attr(
          "fill",
          compareToEntry === "Positive"
            ? "url(#blueGradient)"
            : "url(#pinkGradient)"
        )
        .text(
          compareToEntry === "Positive"
            ? icons["Positive"]
            : compareToEntry === "Negative"
            ? icons["Negative"]
            : ""
        )
        .attr(
          "transform",
          "translate(" +
            (lineWidth - margins.right - margins.lineRight * 0.75) +
            "," +
            margins.top +
            ")"
        );

      const yScaleLine = (val) => yScale(val) + 2;

      const lineGroup = svg.current
        .selectAll(".extraLines")
        .selectAll(".lineGroup" + myClass)
        .data(lineData)
        .join(function (group) {
          const enter = group.append("g").attr("class", "lineGroup" + myClass);
          enter.append("line").attr("class", "additionalLine");
          enter.append("rect").attr("class", "additionalRect");
          enter.append("text").attr("class", "additionalLabel");
          enter.append("text").attr("class", "additionalRectLabel");
          enter.append("rect").attr("class", "additionalDragRect");
          return enter;
        });

      lineGroup
        .attr("transform", (d) => "translate(0," + (yScale(d.value) + 10) + ")")
        .call(d3.drag().on("drag", dragged).on("end", dragended));

      lineGroup
        .select(".additionalLine")
        .attr("x1", lineWidth - margins.right - margins.lineRight)
        .attr("x2", lineWidth - margins.right + CHART_OFFSET)
        // .attr("y1", (d) => {
        //   let y = yScaleLine(d.value) + 8;

        //   if(d.name === 'target') targetY = y;
        //   if(d.name === 'entry') entryY = y;

        //   return y
        // })
        // .attr("y2", (d) => yScaleLine(d.value) + 8)
        .attr("stroke", colors.blue)
        .attr("stroke-width", 1);

      lineGroup
        .select(".additionalRect")
        .attr("x", lineWidth - margins.right + CHART_OFFSET / 2 + 3)
        .attr("y", -10)
        .attr("width", margins.right * 0.8)
        .attr("height", fontSize * 2)
        .attr("padding", 12)
        .attr("rx", 5)
        .attr("ry", 5)
        .attr("fill", (d) => "url(#" + d.fill + ")");

      lineGroup
        .select(".additionalRectLabel")
        .attr("pointer-events", "none")
        .attr("x", lineWidth - margins.right / 2 + 3)
        .attr("y", fontSize - 5)
        .attr("text-align", "center")
        .attr("text-anchor", "middle")
        .attr("font-size", fontSize)
        .attr("text-align", "center")
        .attr("fill", "white")
        .text((d) => Number(d.value).toFixed(1));

      lineGroup
        .select(".additionalLabel")
        .attr("pointer-events", "none")
        .attr("x", lineWidth - margins.right - margins.lineRight)
        .attr("y", fontSize)
        .attr("font-size", fontSize)
        .attr("fill", (d) => colors[d.textFill])
        .text((d) => d.name.toUpperCase());

      // to do
      // on end, get the right y value
      // send to Diana's function

      function dragged(event, d) {
        const newValue = yScale.invert(event.y - margins.top);
        d3.select(this).attr(
          "transform",
          (d) => "translate(0," + event.y + ")"
        );
        d3.select(this)
          .select(".additionalRectLabel")
          .text((d) => Number(newValue).toFixed(1));
      }

      function dragended(event, d) {
        const newValue = yScale.invert(event.y - margins.top);
        d.value = newValue;

        let value = {
          target: targetRef.current,
          entry: entryRef.current,
          stopLoss: stopLossRef.current,
        };
        let val = Number(event?.subject.value.toFixed(1));

        if (event?.subject.name === "target") {
          value = { ...value, target: val };
          targetRef.current = val;
        }
        if (event?.subject.name === "entry") {
          value = { ...value, entry: val };
          entryRef.current = val;
        }
        if (event?.subject.name === "stop loss") {
          value = { ...value, stopLoss: val };
          stopLossRef.current = val;
        }

        arrowHeight = getArrowHeight(
          yScale(entryRef.current),
          yScale(targetRef.current)
        );

        svg.current
          .select(".arrowIcon")
          .attr("font-size", arrowHeight)
          .attr(
            "y",
            (compareToEntry === "Positive"
              ? yScale(entryRef.current)
              : yScale(targetRef.current)) -
              arrowHeight * 0.2
          );

        onDrag?.(value);
      }
      function getTickFormat(d) {
        const currentTicks = xScaleAll.domain();
        if (xTicksAll[d] === undefined) {
          const decimal = Number("0." + String(d).split(".")[1]);
          const secondsPerDay = 86400;
          const startTime = new Date("01/01/2000 00:00");
          const hourSpan = d3.timeHour.count(
            currentTicks[0],
            currentTicks[currentTicks.length - 1]
          );

          currentFormat = d3.timeFormat(hourSpan > 2 ? "%H:00" : "%H:%M");
          return d3.timeFormat(hourSpan > 2 ? "%H:00" : "%H:%M")(
            d3.timeSecond.offset(startTime, decimal * secondsPerDay)
          );
        } else {
          let formatString = "";
          let currentFormatString = "";
          const daySpan = d3.timeDay.count(
            currentTicks[0],
            currentTicks[currentTicks.length - 1]
          );

          if (daySpan < 365) {
            //under a year, day then month
            formatString = "%d %b";
            currentFormatString = `%a %d %b '%y`;
            if (
              xTicksAll[d].getMonth() === 0 &&
              janAlready[xTicksAll[d].getFullYear()] === undefined
            ) {
              janAlready[xTicksAll[d].getFullYear()] = true;
              formatString = "%Y";
            }
          } else {
            //over a year - month only
            formatString = "%b";
            currentFormatString = `%a %d %b '%y`;
            if (
              xTicksAll[d].getMonth() === 0 &&
              janAlready[xTicksAll[d].getFullYear()] === undefined
            ) {
              janAlready[xTicksAll[d].getFullYear()] = true;
              formatString = "%Y";
            }
          }
          currentFormat = d3.timeFormat(currentFormatString);
          return d3.timeFormat(formatString)(xTicksAll[d]);
        }
      }
    }
  };

  function measureWidth(myText, myFontSize) {
    const context = document.createElement("canvas").getContext("2d");
    context.font = myFontSize + "px Poppins";
    return context.measureText(myText).width + 30;
  }

  function getArrowHeight(entryY, targetY) {
    let height = arrowSize;
    height = Math.abs(entryY - targetY);
    height = height * 0.7;

    if (height < arrowSize) height = arrowSize;
    if (height >= margins.lineRight) height = margins.lineRight;

    return height;
  }

  const getTimeFrame = (val) => {
    if (val === "hourly") return "1H";
    if (val === "daily") return "1D";
    if (val === "weekly") return "1W";
  };

  const enableScroll = () => {
    document.removeEventListener("wheel", preventDefault, false);
  };

  const disableScroll = () => {
    document.addEventListener("wheel", preventDefault, {
      passive: false,
    });
  };

  const preventDefault = (el) => {
    const e = el || window.event;
    if (e.preventDefault) {
      e.preventDefault();
    }
    e.returnValue = false;
  };

  return (
    <Box
      ref={containerRef}
      position="relative"
      width="inherit"
      height="inherit"
      onMouseEnter={disableScroll}
      onMouseLeave={enableScroll}
    >
      <Typography
        position="absolute"
        fontWeight="bold"
        ml={1}
        variant="caption"
        fontSize={`${fontSize}px`}
      >
        {tickerName} . {getTimeFrame(timeFrame)}
      </Typography>
      <svg ref={ref}>
        <defs>
          <linearGradient className={"volatilityGradient"}></linearGradient>
          <linearGradient
            id={"pinkGradient"}
            className={"linearGradients"}
          ></linearGradient>
          <linearGradient
            id={"blueGradient"}
            className={"linearGradients"}
          ></linearGradient>
          <linearGradient
            id={"upGradient"}
            className={"LinearGradients"}
          ></linearGradient>
          <linearGradient
            id={"middleGradient"}
            className={"LinearGradients"}
          ></linearGradient>
          <linearGradient
            id={"downGradient"}
            className={"LinearGradients"}
          ></linearGradient>
        </defs>
        <rect className={"zoomRect"}></rect>
        <rect className={"zoomRectY"}></rect>
        <g className={"axis xAxis"}></g>
        <g className={"axis yAxis"}></g>
        {/* <clipPath id={"chartClipPath"}>
          <rect className={"chartClipPathRect"}></rect>
        </clipPath>
        <clipPath id={"volatilityClipPath"}>
          <rect className={"volatilityClipPathRect"}></rect>
        </clipPath>
        <rect className={"volatilityBackgroundRect"}></rect>
        <g className={"volatilityBackgroundGroup"}></g>
        <path className={"volatilityPath"}></path>
        <g className={"axis volatilityXAxis"}></g>
        <g className={"axis volatilityYAxis"}></g> */}
        <text className={"arrowIcon fa"}></text>
        <g className={"extraLines"}></g>
        <line className={"mouseoverItem horizontalLine"}></line>
        {/* <line className={"mouseoverItem verticalLine"}></line> */}
        <rect className={"mouseoverItem dateRect"}></rect>
        <rect className={"mouseoverItem valueRect"}></rect>
        <text className={"mouseoverItem valueLabel"}></text>
        <text className={"mouseoverItem dateRectLabel"}></text>
      </svg>
    </Box>
  );
};

export default CandleStickChart;
