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

const series: NonNullable<AgChartOptions["series"]> = [
  {
    type: "line",
    xKey: "date",
    yKey: "petrol",
    marker: {},
    label: {},
  },
  {
    type: "line",
    xKey: "date",
    yKey: "diesel",
    marker: {},
    label: {},
  },
];
function genDataPoint(
  ref:
    | Date
    | {
        date: Date;
        petrol: number;
        diesel: number;
      },
  offsetDays: number,
) {
  const {
    date,
    petrol = 120,
    diesel = 125,
  } = ref instanceof Date ? { date: ref } : ref;
  return {
    date: new Date(date.getTime() + offsetDays * 3600 * 24 * 1000),
    petrol: petrol + Math.random() * 4 - 2,
    diesel: diesel + Math.random() * 4 - 2,
  };
}
function times<T>(cb: () => T, count: number) {
  const result: T[] = [];
  for (; count > 0; count--) {
    result.push(cb());
  }
  return result;
}
let tick: NodeJS.Timeout;

const ChartExample = () => {
  const [options, setOptions] = useState<AgCartesianChartOptions>({
    animation: {
      enabled: true,
    },
    legend: {},
    data: getData(),
    series,
    axes: [
      {
        position: "bottom",
        type: "time",
      },
      {
        position: "left",
        type: "number",
        label: {
          autoRotate: false,
        },
      },
    ],
  });

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

    nextOptions.data = getData();

    setOptions(nextOptions);
  };

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

    nextOptions.series = [
      ...nextOptions.series!,
      series[nextOptions.series!.length % series.length],
    ] as any;

    setOptions(nextOptions);
  };

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

    nextOptions.series = nextOptions.series!.slice(
      0,
      nextOptions.series!.length - 1,
    );

    setOptions(nextOptions);
  };

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

    nextOptions.data = [...(nextOptions.data ?? [])];
    nextOptions.data.splice(nextOptions.data.length / 2 - 5, 10);

    setOptions(nextOptions);
  };

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

    nextOptions.data = [...(nextOptions.data ?? []).slice(1)];

    setOptions(nextOptions);
  };

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

    nextOptions.data = [...(nextOptions.data ?? []).slice(0, -1)];

    setOptions(nextOptions);
  };

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

    const data = nextOptions.data ?? [];
    const { length } = data;
    nextOptions.data = data.slice(
      Math.floor((length * 1) / 4),
      Math.floor((length * 3) / 4),
    );

    setOptions(nextOptions);
  };

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

    nextOptions.data = [...(nextOptions.data ?? [])];
    const { length } = nextOptions.data;
    for (const idx of [length / 4, length / 2, (length * 3) / 4]) {
      const dataIdx = Math.floor(idx);
      const [datum, nextDatum] = nextOptions.data.slice(dataIdx, dataIdx + 2);
      const date = new Date(
        (datum.date.getTime() + nextDatum.date.getTime()) / 2,
      );
      nextOptions.data.splice(
        dataIdx + 1,
        0,
        genDataPoint({ ...datum, date }, 0),
      );
    }

    setOptions(nextOptions);
  };

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

    nextOptions.data = [...(nextOptions.data ?? [])];
    const ref = nextOptions.data[0];
    nextOptions.data.splice(
      0,
      0,
      genDataPoint(ref, -14),
      genDataPoint(ref, -7),
    );

    setOptions(nextOptions);
  };

  const actionAddPointsAfter = (count = 2) => {
    const nextOptions = clone(options);

    nextOptions.data = [...(nextOptions.data ?? [])];
    const [ref] = nextOptions.data.slice(-1);
    for (let idx = 0; idx < count; idx++) {
      nextOptions.data.push(genDataPoint(ref, (idx + 1) * 7));
    }

    setOptions(nextOptions);
  };

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

    const data = nextOptions.data ?? [];
    const { length } = data;
    const count = Math.ceil(length / 4);
    let start = genDataPoint(data[0], -7 * (count + 1));
    let [end] = data.slice(-1);
    nextOptions.data = [
      ...times(() => (start = genDataPoint(start, 7)), count),
      ...data,
      ...times(() => (end = genDataPoint(end, 7)), count),
    ];

    setOptions(nextOptions);
  };

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

    nextOptions.data = (nextOptions.data ?? []).map((d: any) => ({
      ...d,
      petrol: d.petrol + Math.random() * 4 - 2,
      diesel: d.diesel + Math.random() * 4 - 2,
    }));

    setOptions(nextOptions);
  };

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

    nextOptions.data = (nextOptions.data ?? []).map((d: any) => ({
      ...d,
      petrol: Math.random() > 0.9 ? undefined : d.petrol,
      diesel: Math.random() > 0.9 ? undefined : d.diesel,
    }));

    setOptions(nextOptions);
  };

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

    const data = nextOptions.data ?? [];
    const [ref] = data.slice(-1);
    nextOptions.data = [...data.slice(1), genDataPoint(ref, 7)];

    setOptions(nextOptions);
  };

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

    const data = nextOptions.data ?? [];
    const [ref] = data.slice(0);
    nextOptions.data = [genDataPoint(ref, -7), ...data.slice(0, -1)];

    setOptions(nextOptions);
  };

  const actionTickStart = () => {
    if (tick) clearInterval(tick);
    tick = setInterval(() => actionAddPointsAfter(1), 1000);
  };

  const actionTickStop = () => {
    if (tick) clearInterval(tick);
  };

  return (
    <Fragment>
      <div className="toolbar">
        <button onClick={actionReset}>Reset</button>
        <button onClick={actionAddSeries}>Add Series</button>
        <button onClick={actionRemoveSeries}>Remove Series</button>
        <hr />
        <button onClick={actionRemovePoints}>Remove points middle</button>
        <button onClick={actionRemoveFirstPoint}>Remove the first point</button>
        <button onClick={actionRemoveLastPoint}>Remove the last point</button>
        <button onClick={actionRemoveHalf}>Remove half of points</button>
        <hr />
        <button onClick={actionAddPoints}>Add points middle</button>
        <button onClick={actionAddPointsBefore}>Add points before</button>
        <button onClick={actionAddPointsAfter}>Add points after</button>
        <button onClick={actionAddDouble}>Add double of points</button>
        <hr />
        <button onClick={actionUpdatePoints}>Update points</button>
        <button onClick={actionUpdatePointUndefined}>
          Update points to undefined
        </button>
        <hr />
        <button onClick={actionShiftLeft}>Shift left</button>
        <button onClick={actionShiftRight}>Shift right</button>
        <hr />
        <button onClick={actionTickStart}>Start ticking</button>
        <button onClick={actionTickStop}>Stop ticking</button>
      </div>
      <AgCharts 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 **/
