// IWYU pragma: no_include <bits/std_abs.h>
#pragma once

#include <SDL3/SDL.h>  // Para Uint8

#include <algorithm>  // Para max, min
#include <array>      // Para array
#include <cstdlib>    // Para size_t, abs
#include <string>     // Para string
#include <vector>     // Para vector

// --- Estructura HSV: define un color en formato HSV ---
struct HSV {
    float h;  // Matiz (Hue)
    float s;  // Saturación (Saturation)
    float v;  // Valor (Value)
};

// --- Estructura Color: define un color RGBA ---
struct Color {
   private:
    static constexpr int DEFAULT_LIGHTEN_AMOUNT = 50;
    static constexpr int DEFAULT_DARKEN_AMOUNT = 50;
    static constexpr int DEFAULT_APPROACH_STEP = 1;
    static constexpr size_t HEX_RGB_LENGTH = 6;
    static constexpr size_t HEX_RGBA_LENGTH = 8;
    static constexpr int HEX_BASE = 16;
    static constexpr size_t HEX_COMPONENT_LENGTH = 2;

   public:
    static constexpr Uint8 MAX_COLOR_VALUE = 255;
    static constexpr Uint8 MIN_COLOR_VALUE = 0;
    static constexpr Uint8 DEFAULT_ALPHA = 255;
    static constexpr Uint8 MAX_ALPHA_VALUE = 255;
    static constexpr Uint8 MIN_ALPHA_VALUE = 0;

    Uint8 r, g, b, a;

    constexpr Color()
        : r(MIN_COLOR_VALUE),
          g(MIN_COLOR_VALUE),
          b(MIN_COLOR_VALUE),
          a(DEFAULT_ALPHA) {}

    explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = DEFAULT_ALPHA)
        : r(red),
          g(green),
          b(blue),
          a(alpha) {}

    [[nodiscard]] constexpr auto INVERSE() const -> Color {
        return Color(MAX_COLOR_VALUE - r, MAX_COLOR_VALUE - g, MAX_COLOR_VALUE - b, a);
    }

    [[nodiscard]] constexpr auto LIGHTEN(int amount = DEFAULT_LIGHTEN_AMOUNT) const -> Color {
        return Color(
            std::min(static_cast<int>(MAX_COLOR_VALUE), r + amount),
            std::min(static_cast<int>(MAX_COLOR_VALUE), g + amount),
            std::min(static_cast<int>(MAX_COLOR_VALUE), b + amount),
            a);
    }

    [[nodiscard]] constexpr auto DARKEN(int amount = DEFAULT_DARKEN_AMOUNT) const -> Color {
        return Color(
            std::max(static_cast<int>(MIN_COLOR_VALUE), r - amount),
            std::max(static_cast<int>(MIN_COLOR_VALUE), g - amount),
            std::max(static_cast<int>(MIN_COLOR_VALUE), b - amount),
            a);
    }

    // Método estático para crear Color desde string hexadecimal
    static auto fromHex(const std::string& hex_str) -> Color;

    // Conversiones de formato de color
    [[nodiscard]] constexpr static auto RGB_TO_HSV(Color color) -> HSV;
    [[nodiscard]] constexpr static auto HSV_TO_RGB(HSV hsv) -> Color;

    [[nodiscard]] constexpr auto IS_EQUAL_TO(const Color& other) const -> bool {
        return r == other.r && g == other.g && b == other.b && a == other.a;
    }

    [[nodiscard]] constexpr auto APPROACH_TO(const Color& target, int step = DEFAULT_APPROACH_STEP) const -> Color {
        auto approach_component = [step](Uint8 current, Uint8 target_val) -> Uint8 {
            if (std::abs(current - target_val) <= step) {
                return target_val;
            }
            return (current < target_val) ? current + step : current - step;
        };

        Uint8 new_r = approach_component(r, target.r);
        Uint8 new_g = approach_component(g, target.g);
        Uint8 new_b = approach_component(b, target.b);
        Uint8 new_a = approach_component(a, target.a);

        return Color(new_r, new_g, new_b, new_a);
    }

    // Interpolación lineal hacia otro color (t=0.0: this, t=1.0: target)
    [[nodiscard]] constexpr auto LERP(const Color& target, float t) const -> Color {
        // Asegurar que t esté en el rango [0.0, 1.0]
        t = std::clamp(t, 0.0F, 1.0F);

        // Interpolación lineal para cada componente
        auto lerp_component = [t](Uint8 start, Uint8 end) -> Uint8 {
            return static_cast<Uint8>(start + ((end - start) * t));
        };

        return Color(
            lerp_component(r, target.r),
            lerp_component(g, target.g),
            lerp_component(b, target.b),
            lerp_component(a, target.a));
    }

    // Sobrecarga para aceptar componentes RGBA directamente
    [[nodiscard]] constexpr auto LERP(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha, float t) const -> Color {
        return LERP(Color(red, green, blue, alpha), t);
    }

    // Convierte el color a un entero de 32 bits en formato RGBA
    [[nodiscard]] constexpr auto TO_UINT32() const -> Uint32 {
        return (static_cast<Uint32>(r) << 24) |
            (static_cast<Uint32>(g) << 16) |
            (static_cast<Uint32>(b) << 8) |
            static_cast<Uint32>(a);
    }
};

// --- Enum ColorCycleStyle: define estilos de ciclo de color ---
enum class ColorCycleStyle {
    SUBTLE_PULSE,  // Variación leve en brillo (por defecto)
    HUE_WAVE,      // Variación suave en tono (sin verde)
    VIBRANT,       // Cambios agresivos en tono y brillo
    DARKEN_GLOW,   // Oscurece hacia el centro y regresa
    LIGHT_FLASH    // Ilumina hacia el centro y regresa
};

// --- Namespace Colors: constantes y utilidades de color ---
namespace Colors {
    // --- Constantes ---
    constexpr size_t CYCLE_SIZE = 6;  // Mitad del ciclo espejado

    // --- Alias ---
    using Cycle = std::array<Color, 2 * CYCLE_SIZE>;

    // --- Colores predefinidos ---
    constexpr Color NO_COLOR_MOD = Color(0XFF, 0XFF, 0XFF);
    constexpr Color SHADOW_TEXT = Color(0X43, 0X43, 0X4F);
    constexpr Color TITLE_SHADOW_TEXT = Color(0x14, 0x87, 0xc4);
    constexpr Color ORANGE_TEXT = Color(0XFF, 0X7A, 0X00);

    constexpr Color FLASH = Color(0XFF, 0XFF, 0XFF);

    constexpr Color BLUE_SKY = Color(0X02, 0X88, 0XD1);
    constexpr Color PINK_SKY = Color(0XFF, 0X6B, 0X97);
    constexpr Color GREEN_SKY = Color(0X00, 0X79, 0X6B);

    // --- Funciones ---
    auto getColorLikeKnightRider(const std::vector<Color>& colors, int counter) -> Color;
    auto generateMirroredCycle(Color base, ColorCycleStyle style = ColorCycleStyle::SUBTLE_PULSE) -> Cycle;
}  // namespace Colors