import Big from "big.js";
import { fromUnixTime, intlFormat } from "date-fns";
import { createMemo, JSX } from "solid-js";

export const sleep = (timeout: number) =>
  new Promise((res) => setTimeout(res, timeout));

// Source: https://stackoverflow.com/a/34591063
export const roundToStep = (value: number, step = 1.0) => {
  const inv = new Big(1.0).div(step);
  return inv.mul(value).round().div(inv).toNumber();
};

export const getDisplayDate = function (date: Date) {
  return intlFormat(
    date,
    {
      day: "2-digit",
      month: "2-digit",
      year: "numeric",
    },
    {
      locale: "de-CH",
    }
  );
};

export const getDisplayDateFromUnix = function (unix: number) {
  return getDisplayDate(fromUnixTime(unix));
};

export const resetInput =
  (defaultValue: any, eventName = "input") =>
  (evt: FocusEvent) => {
    const el = evt.target as HTMLInputElement | null;
    if (!el) {
      return;
    }

    if (el.value !== "") {
      return;
    }

    el.value = defaultValue;
    const event = new Event(eventName, { bubbles: true });
    el.dispatchEvent(event);
  };

export const getDomain = () =>
  import.meta.env.SSR ? process.env.DOMAIN || "localhost" : location.hostname;

export const getHost = () => `https://${getDomain()}`;

export const onClickFocus: JSX.EventHandlerUnion<
  HTMLAnchorElement,
  MouseEvent
> = (evt) => {
  const el = evt.currentTarget!;
  const id = el.getAttribute("href");
  if (id == null) {
    return;
  }
  const targetEl = document.querySelector<HTMLElement>(id);
  if (targetEl == null) {
    return;
  }

  targetEl.focus();
};

export const externalLink = { target: "_blank", rel: "noopener" };

export const createOptionalNumberInputHandler = (
  onInput: (v: number | undefined) => void
) => {
  return (e: InputEvent & { currentTarget: HTMLInputElement }) => {
    if (e.currentTarget.validity.badInput) {
      return;
    }

    let value =
      e.currentTarget.value == ""
        ? undefined
        : parseNumberInput(e.currentTarget.value);

    if (Number.isNaN(value)) {
      return;
    }

    onInput(value);
  };
};

const parseNumberInput = (v: string): number => parseFloat(v.replace(",", "."));

export const createNativeInputValue = (
  getEl: () => HTMLInputElement,
  signal: () => any
) =>
  createMemo(function (prev) {
    const value = signal();
    const el = getEl();

    if (!el) {
      return value != null ? value : "";
    }

    const elValue = parseNumberInput(el.value);

    // If the element value and signal value are equal, we can skip triggering the memo change by reusing the prev value
    let result = elValue == value ? prev : value;

    // NaN is always != NaN in js, we have to replace it with a value which has a proper identity
    if (Number.isNaN(result)) {
      result = undefined;
    }

    if (result == null) {
      result = "";
    }

    // If both the value and prev value are the same, but the element value is different, we have to update it manually
    if (value === prev && elValue != value) {
      el.value = result;
    }

    return result;
  });
