import React, { useState, useRef, Fragment } from "react";
import { createRoot } from "react-dom/client";
import { AgCharts } from "ag-charts-react";
import {
  AgCartesianChartOptions,
  AgChartLegendPosition,
  AgChartOptions,
  AgChartsInstance,
} from "ag-charts-enterprise";
import { getData } from "./data";
import "ag-charts-enterprise";
import clone from "clone";

const legendPositions: Array<AgChartLegendPosition> = [
  "bottom",
  "left",
  "right",
  "top",
];
const stackGroups = ["Devices", "Devices", "Devices", "Wearables", "Series"];
const modes = ["standalone", "integrated"] as const;
let mode = modes[0];
function toIntegratedData(key: string, d: any[]) {
  const result = [];
  let id = 0;
  for (const next of d) {
    result.push({
      ...next,
      [key]: {
        id: id++,
        value: next[key],
        toString() {
          return next[key];
        },
      },
    });
  }
  return result;
}
let data = toIntegratedData("quarter", getData());
const series: NonNullable<AgCartesianChartOptions["series"]> = [
  {
    type: "bar",
    direction: "horizontal",
    xKey: "quarter",
    yKey: "iphone",
    yName: "iPhone",
    stackGroup: "Devices",
    label: {
      color: "white",
    },
  },
  {
    type: "bar",
    direction: "horizontal",
    xKey: "quarter",
    yKey: "mac",
    yName: "Mac",
    stackGroup: "Devices",
    label: {
      color: "white",
    },
  },
  {
    type: "bar",
    direction: "horizontal",
    xKey: "quarter",
    yKey: "ipad",
    yName: "iPad",
    stackGroup: "Devices",
    label: {
      color: "white",
    },
  },
  {
    type: "bar",
    direction: "horizontal",
    xKey: "quarter",
    yKey: "wearables",
    yName: "Wearables",
    label: {
      color: "white",
    },
  },
  {
    type: "bar",
    direction: "horizontal",
    xKey: "quarter",
    yKey: "services",
    yName: "Services",
    label: {
      color: "white",
    },
  },
];

const ChartExample = () => {
  const chartRef = useRef<AgChartsInstance>(null);
  const [options, setOptions] = useState<
    AgCartesianChartOptions & { mode: (typeof modes)[number] }
  >({
    theme: "ag-default",
    mode,

    animation: {
      enabled: true,
    },
    data,
    series,
    legend: {},
  });

  const reset = () => {
    const nextOptions = clone(options);

    data = toIntegratedData("quarter", getData());
    nextOptions.data = data;
    nextOptions.series = [...series];
    chartRef.current!.update(options as any);

    setOptions(nextOptions);
  };

  const randomise = () => {
    const nextOptions = clone(options);

    nextOptions.data = [
      ...data.map((d: any) => ({
        ...d,
        iphone: d.iphone + Math.floor(Math.random() * 50 - 25),
      })),
    ];
    chartRef.current!.update(options as any);

    setOptions(nextOptions);
  };

  const removeData = () => {
    const nextOptions = clone(options);

    nextOptions.data = nextOptions.data?.slice(0, nextOptions.data.length - 1);
    chartRef.current!.update(options as any);

    setOptions(nextOptions);
  };

  const removeSeries = () => {
    const nextOptions = clone(options);

    nextOptions.series = series.slice(0, nextOptions.series!.length - 1);
    chartRef.current!.update(options as any);

    setOptions(nextOptions);
  };

  const addSeries = () => {
    const nextOptions = clone(options);

    nextOptions.series = series.slice(0, nextOptions.series!.length + 1);
    chartRef.current!.update(options as any);

    setOptions(nextOptions);
  };

  const switchDirection = () => {
    const nextOptions = clone(options);

    nextOptions.series?.forEach(
      (s: any) =>
        (s.direction =
          s.direction === "horizontal" ? "vertical" : "horizontal"),
    );
    if (nextOptions.mode === "integrated") {
      chartRef.current!.resetAnimations();
    }
    chartRef.current!.update(options as any);

    setOptions(nextOptions);
  };

  const switchToGrouped = () => {
    const nextOptions = clone(options);

    nextOptions.series?.forEach((s: any) => delete s["stackGroup"]);
    if (nextOptions.mode === "integrated") {
      chartRef.current!.resetAnimations();
    }
    chartRef.current!.update(options as any);

    setOptions(nextOptions);
  };

  const switchToStacked = () => {
    const nextOptions = clone(options);

    nextOptions.series?.forEach((s: any, i) => {
      s.stackGroup = stackGroups[i];
    });
    if (nextOptions.mode === "integrated") {
      chartRef.current!.resetAnimations();
    }
    chartRef.current!.update(options as any);

    setOptions(nextOptions);
  };

  const moveLegend = () => {
    const nextOptions = clone(options);

    const currentPosition = legendPositions.indexOf(
      nextOptions.legend?.position ?? "bottom",
    );
    nextOptions.legend ??= {};
    nextOptions.legend.position = legendPositions[(currentPosition + 1) % 4];
    if (nextOptions.mode === "integrated") {
      chartRef.current!.skipAnimations();
    }
    chartRef.current!.update(options as any);

    setOptions(nextOptions);
  };

  const changeTheme = () => {
    const nextOptions = clone(options);

    const themes = ["ag-default", "ag-sheets", "ag-polychroma"] as const;
    const idx = themes.indexOf(nextOptions.theme as any);
    nextOptions.theme = themes[(idx + 1) % themes.length];
    if (nextOptions.mode === "integrated") {
      chartRef.current!.skipAnimations();
    }

    setOptions(nextOptions);
  };

  const toggleMode = () => {
    const nextOptions = clone(options);

    const nextMode =
      modes[(modes.indexOf(nextOptions.mode) + 1) % modes.length];
    nextOptions.mode = nextMode;
    modeButton.textContent = `Mode: ${nextMode}`;

    setOptions(nextOptions);
  };

  return (
    <Fragment>
      <div className="toolbar">
        <button onClick={reset}>Reset</button>
        <button onClick={toggleMode} id="modeButton">
          Mode: standalone
        </button>
        <button onClick={randomise}>Randomise</button>
        <button onClick={removeData}>Remove Data</button>
        <button onClick={removeSeries}>Remove Series</button>
        <button onClick={addSeries}>Add Series</button>
        <button onClick={switchDirection}>Switch Direction</button>
        <button onClick={switchToGrouped}>Switch to Grouped</button>
        <button onClick={switchToStacked}>Switch to Stacked</button>
        <button onClick={moveLegend}>Move legend</button>
        <button onClick={changeTheme}>Change Theme</button>
      </div>
      <AgCharts ref={chartRef} options={options as any} />
    </Fragment>
  );
};

const root = createRoot(document.getElementById("root")!);
root.render(<ChartExample />);

/** DARK MODE START **/
import { AgCharts as __chartAPI } from "ag-charts-community";

let darkmode =
  (localStorage["documentation:darkmode"] ||
    String(matchMedia("(prefers-color-scheme: dark)").matches)) === "true";

const isAgThemeOrUndefined = (theme) => {
  return (
    theme == null || (typeof theme === "string" && theme.startsWith("ag-"))
  );
};

const getDarkmodeTheme = (theme = "ag-default", preset) => {
  const baseTheme =
    preset === "price-volume" ? "ag-financial" : theme.replace(/-dark$/, "");
  return darkmode ? baseTheme + "-dark" : baseTheme;
};

__chartAPI.optionsMutationFn = function update(options, preset) {
  const nextOptions = { ...options };
  const theme = options.theme;
  if (isAgThemeOrUndefined(theme)) {
    nextOptions.theme = getDarkmodeTheme(theme, preset);
  } else if (
    typeof theme === "object" &&
    isAgThemeOrUndefined(theme.baseTheme)
  ) {
    nextOptions.theme = {
      ...options.theme,
      baseTheme: getDarkmodeTheme(theme.baseTheme, preset),
    };
  }
  return nextOptions;
};

const applyDarkmode = () => {
  document.documentElement.setAttribute("data-dark-mode", darkmode);
  const charts = document.querySelectorAll("[data-ag-charts]");
  charts.forEach((element) => {
    const chart = __chartAPI.getInstance(element.parentElement);
    if (chart == null) return;
    // This is just needed to trigger the theme update
    chart.update(chart.getOptions());
  });
  return charts.length !== 0;
};

if (!applyDarkmode()) {
  /* React defers updates. Rather than try and hook into the API, just wait until the darkmode is applied. */
  const observer = new MutationObserver(() => {
    if (applyDarkmode()) {
      observer.disconnect();
    }
  });
  observer.observe(document.body, {
    attributes: true,
    childList: true,
    subtree: true,
  });
}
window.addEventListener("message", (event) => {
  if (event.data && event.data.type === "color-scheme-change") {
    darkmode = event.data.darkmode;
    applyDarkmode();
  }
});
/** DARK MODE END **/
