import { useContext, useEffect, useRef } from "react";
import h from "./hyper";
import { select } from "d3-selection";
import { axisLeft } from "d3-axis";
import { ScaleContinuousNumeric, scaleLinear, ScaleLinear } from "d3-scale";
import { useColumn } from "./context";

interface ColumnAxisProps {
  ticks?: number;
  tickArguments?: any;
  tickValues?: any;
  tickFormat?: any;
  tickSize?: any;
  tickSizeInner?: any;
  tickSizeOuter?: any;
  tickPadding?: any;
  tickSpacing?: number;
  showLabel?: (d: any) => boolean;
  showDomain?: boolean;
  className?: string;
}

interface AgeAxisProps extends ColumnAxisProps {
  scale: ScaleContinuousNumeric<number, number>;
  minTickSpacing?: number;
}

const __d3axisKeys = [
  "ticks",
  "tickArguments",
  "tickValues",
  "tickFormat",
  "tickSize",
  "tickSizeInner",
  "tickSizeOuter",
  "tickPadding",
];

export function ColumnAxis(props: ColumnAxisProps) {
  const { scale } = useColumn();
  return h(AgeAxis, { scale, ...props });
}

export function AgeAxis(props: AgeAxisProps) {
  const {
    showLabel,
    className,
    showDomain = true,
    tickSpacing = 60,
    minTickSpacing = 20,
    scale,
  } = props;

  const range = scale.range();

  const pixelHeight = Math.abs(range[0] - range[range.length - 1]);

  let tickValues: number[] | undefined = undefined;

  let ticks = Math.round(pixelHeight / tickSpacing);
  if (pixelHeight < 3 * tickSpacing) {
    // Push ticks towards extrema (we need more than 2 to be resolved)

    let t0: number[] = [];
    while (t0.length <= 2) {
      ticks += 1;
      t0 = scale.ticks(ticks);
    }

    tickValues = t0;
    if (pixelHeight < 2 * tickSpacing) {
      // Only show first and last ticks
      tickValues = [t0[0], t0[t0.length - 1]];
    }
  }

  if (pixelHeight < minTickSpacing) {
    ticks = 1;
    tickValues = scale.ticks(1);
    // Get the last tick value only
    tickValues = [tickValues[0]];
  }

  const defaultProps = {
    ticks,
    // Suppress domain endpoints
    tickSizeOuter: 0,
    tickValues,
  };

  const ref = useRef(null);
  const axisRef = useRef(axisLeft());

  const deps = __d3axisKeys.map((k) => props[k]);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;

    axisRef.current.scale(scale);
    for (let k of __d3axisKeys) {
      const val = props[k] ?? defaultProps[k];
      if (val == null) continue;
      axisRef.current[k](val);
    }

    const ax = select(el).call(axisRef.current);

    if (!showDomain) {
      ax.select(".domain").remove();
    }

    ax.selectAll(".tick text").each(function (d) {
      if (!(showLabel?.(d) ?? true)) {
        select(this).attr("visibility", "hidden");
      }
    });

    return () => {
      select(el).selectAll("*").remove();
    };
  }, [scale, ref.current, showDomain, showLabel, ...deps]);

  return h("g.y.axis.column-axis", { className, ref });
}
