import { createSeriesHighlight } from "./highlight.js";
import { createSeriesLoader } from "./loader.js";
import { renderPlot } from "./plot.js";
import { createScrubber } from "./scrubber/index.js";
import { createSvgElement } from "./svg.js";
import { createChartFrame, VIEWBOX_WIDTH } from "./viewbox.js";

/**
 * @param {Object} args
 * @param {SVGSVGElement} args.svg
 * @param {LegendReadout} args.readout
 * @param {HTMLElement} args.menu
 * @param {(HTMLElement | null)[]} args.items
 * @param {HTMLElement} args.status
 * @param {Chart} args.chart
 * @param {() => ChartView} args.getView
 * @param {() => ChartScale} args.getScale
 * @param {() => ChartOrder} args.getOrder
 * @param {() => TimeframeValue} args.getTimeframe
 */
export function createChartRenderer({
  svg,
  readout,
  menu,
  items,
  status,
  chart,
  getView,
  getScale,
  getOrder,
  getTimeframe,
}) {
  const group = createSvgElement("g");
  const highlight = createSeriesHighlight(items, menu);
  const loadSeries = createSeriesLoader(chart);
  /** @type {LoadedSeries[]} */
  let loadedSeries = [];
  /** @type {ReturnType<typeof createScrubber> | undefined} */
  let scrubber;
  const resizeObserver = new ResizeObserver(renderCurrent);
  let active = false;
  let loadId = 0;

  svg.append(group);

  function clearStatus() {
    status.textContent = "";
    svg.removeAttribute("aria-busy");
  }

  /** @param {string} message */
  function setStatus(message) {
    status.textContent = message;
  }

  function renderCurrent() {
    if (!active || !loadedSeries.length) return;

    const frame = createChartFrame(svg);

    svg.setAttribute("viewBox", `0 0 ${VIEWBOX_WIDTH} ${frame.height}`);
    group.replaceChildren();
    highlight.clearNodes();
    scrubber ??= createScrubber(svg, readout, highlight, chart.unit.format);
    scrubber.setSeries(
      renderPlot(getView(), {
        group,
        loadedSeries,
        frame,
        highlight,
        scale: getScale(),
        order: getOrder(),
      }),
      frame,
    );
  }

  async function loadCurrent() {
    const id = (loadId += 1);
    const loadingTimer = setTimeout(() => {
      if (id === loadId && active) setStatus("Loading");
    }, 250);

    setStatus("");
    svg.setAttribute("aria-busy", "true");

    try {
      const nextSeries = await loadSeries(getTimeframe());

      if (id !== loadId || !active) return;

      loadedSeries = nextSeries;
      renderCurrent();
      clearStatus();
    } catch (error) {
      if (id !== loadId) return;
      console.error(error);
      setStatus("Chart unavailable");
    } finally {
      clearTimeout(loadingTimer);
      if (id === loadId) svg.removeAttribute("aria-busy");
    }
  }

  function resume() {
    if (active) return;

    active = true;
    resizeObserver.observe(svg);
    void loadCurrent();
  }

  function suspend() {
    if (!active) return;

    active = false;
    loadedSeries = [];
    loadId += 1;
    resizeObserver.disconnect();
    group.replaceChildren();
    highlight.clearNodes();
    scrubber?.clear();
    clearStatus();
  }

  return {
    loadCurrent,
    renderCurrent,
    resume,
    suspend,
  };
}
