/**
 * Default translucent card color used for tinting card surfaces when no specific color is provided.
 * @type {{r: number, g: number, b: number, a: number}}
 */
export const DEFAULT_TRANSLUCENT_CARD_COLOR = {
  r: 22,
  g: 119,
  b: 255,
  a: 0.5
};

/**
 * Rounds a color channel value to two decimal places.
 * @param value
 * @returns {number}
 */
const roundColorChannel = (value) => Math.round(value * 100) / 100;

/**
 * Rounds a tint percentage value to two decimal places.
 * @param value
 * @returns {number}
 */
const roundTintPercentage = (value) => Math.round(value * 100) / 100;

/**
 * Clamps an alpha value to the range [0, 1] and rounds it to two decimal places.
 * @param value
 * @returns {number}
 */
const clampAlpha = (value) => {
  const numericValue = Number(value);

  if (!Number.isFinite(numericValue)) return 1;
  if (numericValue <= 0) return 0;
  if (numericValue >= 1) return 1;

  return numericValue;
};

/**
 * Converts an RGB color object to a hexadecimal color string.
 * @param param0
 * @param param0.r
 * @param param0.g
 * @param param0.b
 * @returns {`#${string}`}
 */
const rgbToHex = ({ r, g, b }) =>
  `#${[r, g, b].map((channel) => Math.round(channel).toString(16).padStart(2, "0")).join("")}`;

/**
 * Converts an RGB color object to an HSL color object.
 * @param param0
 * @param param0.r
 * @param param0.g
 * @param param0.b
 * @param param0.a
 * @returns {{h: number, s: number, l: number, a: number}|{h: number, s: number, l: number, a: number}}
 */
const rgbToHsl = ({ r, g, b, a = 1 }) => {
  const red = r / 255;
  const green = g / 255;
  const blue = b / 255;
  const max = Math.max(red, green, blue);
  const min = Math.min(red, green, blue);
  const delta = max - min;
  const lightness = (max + min) / 2;

  if (delta === 0) {
    return { h: 0, s: 0, l: roundColorChannel(lightness), a };
  }

  const saturation = lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min);
  let hue;

  switch (max) {
    case red:
      hue = (green - blue) / delta + (green < blue ? 6 : 0);
      break;
    case green:
      hue = (blue - red) / delta + 2;
      break;
    default:
      hue = (red - green) / delta + 4;
      break;
  }

  return {
    h: roundColorChannel(hue * 60),
    s: roundColorChannel(saturation),
    l: roundColorChannel(lightness),
    a
  };
};

/**
 * Converts an RGB color object to an HSV color object.
 * @param param0
 * @param param0.r
 * @param param0.g
 * @param param0.b
 * @param param0.a
 * @returns {{h: number, s: number, v: number, a: number}}
 */
const rgbToHsv = ({ r, g, b, a = 1 }) => {
  const red = r / 255;
  const green = g / 255;
  const blue = b / 255;
  const max = Math.max(red, green, blue);
  const min = Math.min(red, green, blue);
  const delta = max - min;
  const saturation = max === 0 ? 0 : delta / max;
  let hue = 0;

  if (delta !== 0) {
    switch (max) {
      case red:
        hue = (green - blue) / delta + (green < blue ? 6 : 0);
        break;
      case green:
        hue = (blue - red) / delta + 2;
        break;
      default:
        hue = (red - green) / delta + 4;
        break;
    }
  }

  return {
    h: roundColorChannel(hue * 60),
    s: roundColorChannel(saturation),
    v: roundColorChannel(max),
    a
  };
};

/**
 * Builds a comprehensive color value object for a color picker component based on an input RGB color object.
 * @param rgb
 * @returns {{hex: `#${string}`, rgb: *, hsl: {h: number, s: number, l: number, a: number}, hsv: {h: number, s: number, v: number, a: number}, oldHue: number, source: string}}
 */
const buildPickerColorValue = (rgb) => {
  const hsl = rgbToHsl(rgb);

  return {
    hex: rgbToHex(rgb),
    rgb: { ...rgb },
    hsl,
    hsv: rgbToHsv(rgb),
    oldHue: hsl.h,
    source: "rgb"
  };
};

/**
 * Default color value object for the color picker component, derived from the default translucent card color.
 * @type {{hex: `#${string}`, rgb: *, hsl: {h: number, s: number, l: number, a: number}, hsv: {h: number, s: number, v: number, a: number}, oldHue: number, source: string}}
 */
export const DEFAULT_TRANSLUCENT_PICKER_COLOR = buildPickerColorValue(DEFAULT_TRANSLUCENT_CARD_COLOR);

/**
 * Parses a color string that may be a JSON representation of a color object. If the string is valid JSON and represents
 * a color, it returns the parsed object; otherwise, it returns the original string.
 * @param color
 * @returns {*|string}
 */
const parseJsonColorString = (color) => {
  if (typeof color !== "string") return color;

  const trimmedColor = color.trim();
  if (!trimmedColor.startsWith("{") && !trimmedColor.startsWith("[")) return color;

  try {
    return JSON.parse(trimmedColor);
  } catch {
    return color;
  }
};

/**
 * Parses a hexadecimal color string (e.g., "#RRGGBB" or "#RRGGBBAA") and returns an object containing the corresponding
 * RGB color value and alpha transparency. Supports both 3/4-digit and 6/8-digit hex formats.
 * @param color
 * @returns {{colorCssValue: string, alpha: number}|null}
 */
const parseHexColor = (color) => {
  if (typeof color !== "string") return null;

  const normalizedHex = color.trim().replace(/^#/, "");

  if (![3, 4, 6, 8].includes(normalizedHex.length) || /[^0-9a-f]/i.test(normalizedHex)) {
    return null;
  }

  const expandedHex =
    normalizedHex.length <= 4
      ? normalizedHex
          .split("")
          .map((character) => `${character}${character}`)
          .join("")
      : normalizedHex;

  const hasAlpha = expandedHex.length === 8;
  const red = Number.parseInt(expandedHex.slice(0, 2), 16);
  const green = Number.parseInt(expandedHex.slice(2, 4), 16);
  const blue = Number.parseInt(expandedHex.slice(4, 6), 16);
  const alpha = hasAlpha ? Number.parseInt(expandedHex.slice(6, 8), 16) / 255 : 1;

  return {
    colorCssValue: `rgb(${red}, ${green}, ${blue})`,
    alpha: clampAlpha(alpha)
  };
};

/**
 * Parses an RGB or RGBA color string (e.g., "rgb(255, 0, 0)" or "rgba(255, 0, 0, 0.5)") and returns an object
 * containing the corresponding RGB color value and alpha transparency. Supports both integer and percentage formats for
 * color channels and alpha.
 * @param color
 * @returns {{colorCssValue: string, alpha: number}|null}
 */
const parseRgbColor = (color) => {
  if (typeof color !== "string") return null;

  const rgbMatch = color.trim().match(/^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)(?:\s*,\s*([\d.]+))?\s*\)$/i);

  if (!rgbMatch) return null;

  const [, red, green, blue, alpha = 1] = rgbMatch;

  return {
    colorCssValue: `rgb(${red}, ${green}, ${blue})`,
    alpha: clampAlpha(alpha)
  };
};

/**
 * Normalizes a color input into a consistent descriptor object containing a CSS color value and an alpha transparency
 * level.
 * @param color
 * @returns {{colorCssValue: string, alpha: number}|{colorCssValue: string, alpha: number}|*|{colorCssValue: string, alpha: number}|null}
 */
const getNormalizedColorDescriptor = (color) => {
  if (!color) return null;

  const normalizedColor = parseJsonColorString(color);

  if (typeof normalizedColor === "string") {
    return (
      parseHexColor(normalizedColor) ||
      parseRgbColor(normalizedColor) || {
        colorCssValue: normalizedColor,
        alpha: 1
      }
    );
  }

  if (typeof normalizedColor === "object" && normalizedColor.rgb) {
    return getNormalizedColorDescriptor(normalizedColor.rgb);
  }

  if (typeof normalizedColor === "object" && typeof normalizedColor.hex === "string") {
    return getNormalizedColorDescriptor(normalizedColor.hex);
  }

  if (
    typeof normalizedColor === "object" &&
    normalizedColor.r !== undefined &&
    normalizedColor.g !== undefined &&
    normalizedColor.b !== undefined
  ) {
    return {
      colorCssValue: `rgb(${normalizedColor.r}, ${normalizedColor.g}, ${normalizedColor.b})`,
      alpha: clampAlpha(normalizedColor.a)
    };
  }

  return null;
};

/**
 * Generates CSS styles for tinting card surfaces based on a provided color input. The function normalizes the input
 * color,
 * @param color
 * @returns {{surfaceBg: string, surfaceHeaderBg: string, surfaceBorderColor: string}|{}}
 */
export const getTintedCardSurfaceStyles = (color) => {
  const normalizedColor = getNormalizedColorDescriptor(color);
  if (!normalizedColor?.colorCssValue) return {};

  const tintStrength = clampAlpha(normalizedColor.alpha);
  if (tintStrength === 0) return {};

  const backgroundTint = roundTintPercentage(10 * tintStrength);
  const headerTint = roundTintPercentage(18 * tintStrength);
  const borderTint = roundTintPercentage(30 * tintStrength);

  return {
    surfaceBg: `color-mix(in srgb, ${normalizedColor.colorCssValue} ${backgroundTint}%, var(--imex-form-surface))`,
    surfaceHeaderBg: `color-mix(in srgb, ${normalizedColor.colorCssValue} ${headerTint}%, var(--imex-form-surface-head))`,
    surfaceBorderColor: `color-mix(in srgb, ${normalizedColor.colorCssValue} ${borderTint}%, var(--imex-form-surface-border))`
  };
};
