defmodule Raxol.Style.Colors.PaletteManager do
  @moduledoc """
  Manages color palette generation and scale creation with accessibility considerations.
  """

  @doc """
  Generates a color scale from a base color with the specified number of steps.
  Ensures each color in the scale has sufficient contrast with dark backgrounds.

  ## Parameters
  * `base_color` - The base hex color (e.g., "#0077CC")
  * `steps` - Number of colors in the scale

  ## Returns
  List of hex color strings with sufficient contrast ratios
  """
  @spec generate_scale(String.t(), pos_integer()) :: [String.t()]
  def generate_scale(base_color, steps)
      when is_binary(base_color) and steps > 0 do
    # Convert hex to HSL for easier manipulation
    {h, s, l} = hex_to_hsl(base_color)

    # Generate scale by varying lightness while maintaining hue and saturation
    # Start with lighter colors and progress to darker ones
    light_step = l / (steps - 1)

    Enum.map(0..(steps - 1), fn i ->
      new_l = max(0.1, min(0.9, l - i * light_step))
      hsl_to_hex(h, s, new_l)
    end)
    |> ensure_accessible_contrast()
  end

  defp hex_to_hsl(hex) do
    # Remove # if present
    hex = String.replace(hex, "#", "")

    # Parse RGB values
    {r, g, b} = parse_hex_rgb_values(hex)

    # Convert to HSL
    max_val = max(r, max(g, b))
    min_val = min(r, min(g, b))
    delta = max_val - min_val

    l = (max_val + min_val) / 2

    {h, s} = calculate_hue_and_saturation(r, g, b, max_val, min_val, delta, l)

    {h, s, l}
  end

  defp parse_hex_rgb_values(hex) do
    r = String.slice(hex, 0..1) |> String.to_integer(16) |> then(&(&1 / 255))
    g = String.slice(hex, 2..3) |> String.to_integer(16) |> then(&(&1 / 255))
    b = String.slice(hex, 4..5) |> String.to_integer(16) |> then(&(&1 / 255))
    {r, g, b}
  end

  defp calculate_hue_and_saturation(r, g, b, max_val, min_val, delta, l) do
    handle_delta_calculation(delta, r, g, b, max_val, min_val, l)
  end

  defp handle_delta_calculation(0, _r, _g, _b, _max_val, _min_val, _l) do
    {0, 0}
  end

  defp handle_delta_calculation(delta, r, g, b, max_val, min_val, l) do
    s = calculate_saturation(delta, max_val, min_val, l)
    h = calculate_hue(r, g, b, max_val, delta)
    {h, s}
  end

  defp calculate_saturation(delta, max_val, min_val, l) do
    saturation_by_lightness(l > 0.5, delta, max_val, min_val)
  end

  defp saturation_by_lightness(true, delta, max_val, min_val) do
    delta / (2 - max_val - min_val)
  end

  defp saturation_by_lightness(false, delta, max_val, min_val) do
    delta / (max_val + min_val)
  end

  # Helper functions for pattern matching refactoring

  defp calculate_hue(r, g, b, max_val, delta) when max_val == r do
    h = (g - b) / delta * 60
    normalize_hue(h)
  end

  defp calculate_hue(r, g, b, max_val, delta) when max_val == g do
    h = ((b - r) / delta + 2) * 60
    normalize_hue(h)
  end

  defp calculate_hue(r, g, b, max_val, delta) when max_val == b do
    h = ((r - g) / delta + 4) * 60
    normalize_hue(h)
  end

  defp normalize_hue(h) when h < 0, do: h + 360
  defp normalize_hue(h), do: h

  defp hsl_to_hex(h, s, l) do
    {r, g, b} = hsl_to_rgb(h, s, l)
    rgb_to_hex(r, g, b)
  end

  defp hsl_to_rgb(h, s, l) do
    c = calculate_chroma(s, l)
    x = calculate_secondary_component(h, c)
    m = l - c / 2

    {r1, g1, b1} = hsl_rgb_components(h, c, x)
    r = round((r1 + m) * 255)
    g = round((g1 + m) * 255)
    b = round((b1 + m) * 255)
    {max(0, min(255, r)), max(0, min(255, g)), max(0, min(255, b))}
  end

  defp calculate_chroma(s, l) do
    (1 - abs(2 * l - 1)) * s
  end

  defp calculate_secondary_component(h, c) do
    c * (1 - abs(rem(trunc(h / 60), 2) - 1))
  end

  defp hsl_rgb_components(h, c, x) when h < 60, do: {c, x, 0}
  defp hsl_rgb_components(h, c, x) when h < 120, do: {x, c, 0}
  defp hsl_rgb_components(h, c, x) when h < 180, do: {0, c, x}
  defp hsl_rgb_components(h, c, x) when h < 240, do: {0, x, c}
  defp hsl_rgb_components(h, c, x) when h < 300, do: {x, 0, c}
  defp hsl_rgb_components(_h, c, x), do: {c, 0, x}

  defp rgb_to_hex(r, g, b) do
    "#" <>
      String.pad_leading(Integer.to_string(r, 16), 2, "0") <>
      String.pad_leading(Integer.to_string(g, 16), 2, "0") <>
      String.pad_leading(Integer.to_string(b, 16), 2, "0")
  end

  defp ensure_accessible_contrast(colors) do
    dark_bg = "#121212"
    min_contrast = 3.0

    Enum.map(colors, fn color ->
      ensure_color_contrast(color, dark_bg, min_contrast)
    end)
  end

  defp ensure_color_contrast(color, background, min_contrast) do
    case has_sufficient_contrast(color, background, min_contrast) do
      true -> color
      false -> adjust_for_contrast(color, background, min_contrast)
    end
  end

  defp has_sufficient_contrast(color, background, min_ratio) do
    ratio = Raxol.Style.Colors.Utilities.contrast_ratio(color, background)
    ratio >= min_ratio
  end

  defp adjust_for_contrast(color, background, min_ratio) do
    # Simple adjustment: lighten the color until it meets contrast requirements
    {h, s, l} = hex_to_hsl(color)

    adjusted_l = adjust_lightness_for_contrast(h, s, l, background, min_ratio)
    hsl_to_hex(h, s, adjusted_l)
  end

  defp adjust_lightness_for_contrast(
         h,
         s,
         l,
         background,
         min_ratio,
         attempts \\ 0
       ) do
    handle_lightness_adjustment(
      attempts >= 20,
      h,
      s,
      l,
      background,
      min_ratio,
      attempts
    )
  end

  defp handle_lightness_adjustment(
         true,
         _h,
         _s,
         _l,
         _background,
         _min_ratio,
         _attempts
       ) do
    # Fallback to a safe color if we can't achieve contrast
    0.8
  end

  defp handle_lightness_adjustment(
         false,
         h,
         s,
         l,
         background,
         min_ratio,
         attempts
       ) do
    test_color = hsl_to_hex(h, s, l)

    case has_sufficient_contrast(test_color, background, min_ratio) do
      true ->
        l

      false ->
        # Increase lightness and try again
        new_l = min(0.95, l + 0.05)

        adjust_lightness_for_contrast(
          h,
          s,
          new_l,
          background,
          min_ratio,
          attempts + 1
        )
    end
  end
end
