import type { RgbaColor } from 'colord';
import { colord } from 'colord';

export type HsvaColor = {
  h: number;
  s: number;
  v: number;
  a: number;
};

export const round = (number: number, digits = 0, base = 10 ** digits): number => Math.round(base * number) / base;

export const clamp = (number: number, min = 0, max = 1): number => {
  if (number > max) return max;
  if (number < min) return min;
  return number;
};

export const normalizeHsva = (hsva: HsvaColor, disableAlpha?: boolean): HsvaColor => ({
  h: round(clamp(hsva.h, 0, 360)),
  s: round(clamp(hsva.s, 0, 100)),
  v: round(clamp(hsva.v, 0, 100)),
  a: disableAlpha ? 1 : round(clamp(hsva.a, 0, 1), 2),
});

export const equalHsva = (a: HsvaColor, b: HsvaColor): boolean =>
  a.h === b.h && a.s === b.s && a.v === b.v && a.a === b.a;

export const hsvaToHsla = ({ h, s, v, a }: HsvaColor) => {
  const hh = ((200 - s) * v) / 100;

  return {
    h: round(h),
    s: round(hh > 0 && hh < 200 ? ((s * v) / 100 / (hh <= 100 ? hh : 200 - hh)) * 100 : 0),
    l: round(hh / 2),
    a: round(a, 2),
  };
};

export const hsvaToHslString = (hsva: HsvaColor): string => {
  const { h, s, l } = hsvaToHsla(hsva);
  return `hsl(${h}, ${s}%, ${l}%)`;
};

export const hsvaToHslaString = (hsva: HsvaColor): string => {
  const { h, s, l, a } = hsvaToHsla(hsva);
  return `hsla(${h}, ${s}%, ${l}%, ${a})`;
};

export const hsvaToRgba = ({ h, s, v, a }: HsvaColor): RgbaColor => {
  const hh = (h / 360) * 6;
  const ss = s / 100;
  const vv = v / 100;

  const sector = Math.floor(hh);
  const b = vv * (1 - ss);
  const c = vv * (1 - (hh - sector) * ss);
  const d = vv * (1 - (1 - hh + sector) * ss);
  const moduleKey = sector % 6;

  return {
    r: round([vv, c, b, b, d, vv][moduleKey] * 255),
    g: round([d, vv, vv, c, b, b][moduleKey] * 255),
    b: round([b, b, d, vv, vv, c][moduleKey] * 255),
    a: round(a, 2),
  };
};

export const rgbaToHsva = ({ r, g, b, a }: RgbaColor): HsvaColor => {
  const max = Math.max(r, g, b);
  const delta = max - Math.min(r, g, b);

  // eslint-disable-next-line no-nested-ternary
  const hh = delta ? (max === r ? (g - b) / delta : max === g ? 2 + (b - r) / delta : 4 + (r - g) / delta) : 0;

  return normalizeHsva({
    h: round(60 * (hh < 0 ? hh + 6 : hh)),
    s: round(max ? (delta / max) * 100 : 0),
    v: round((max / 255) * 100),
    a,
  });
};

export const rgbaToString = ({ r, g, b, a }: RgbaColor): string => `rgba(${r}, ${g}, ${b}, ${round(a, 2)})`;

export const toHex = (rgba: RgbaColor, disableAlpha?: boolean) =>
  colord({ ...rgba, a: disableAlpha ? 1 : rgba.a }).toHex();

export const alphaToPercentages = (n: number) => (n * 1000) / 10;

export const blendColor = (opacity: number, bgColor: string) => {
  const initialOpacity = opacity * 100;

  return `color-mix(in srgb, currentColor ${initialOpacity}%, ${bgColor} ${100 - initialOpacity}%)`;
};
