#pragma once

#include <SDL3/SDL.h>  // Para Uint32, SDL_GetTicks, SDL_FRect

#include <cstdlib>  // Para rand
#include <memory>   // Para unique_ptr

#include "core/rendering/sprite/animated_sprite.hpp"  // Para AnimatedSprite

// --- Clase Tabe ---
class Tabe {
   public:
    // --- Enumeraciones para dirección y estado ---
    enum class Direction : int {
        TO_THE_LEFT = 0,
        TO_THE_RIGHT = 1,
    };

    enum class State : int {
        FLY = 0,
        HIT = 1,
    };

    // --- Constructores y destructor ---
    Tabe();
    ~Tabe() = default;

    // --- Métodos principales ---
    void update(float delta_time);  // Actualiza la lógica (time-based)
    void render();                  // Dibuja el objeto
    void enable();                  // Habilita el objeto
    void setState(State state);     // Establece el estado
    auto tryToGetBonus() -> bool;   // Intenta obtener el bonus
    void pauseTimer(bool value);    // Detiene/activa el timer
    void disableSpawning();         // Deshabilita el spawning permanentemente
    void enableSpawning();          // Habilita el spawning nuevamente

    // --- Getters ---
    auto getCollider() -> SDL_FRect& { return sprite_->getRect(); }    // Obtiene el área de colisión
    [[nodiscard]] auto isEnabled() const -> bool { return enabled_; }  // Indica si el objeto está activo

   private:
    // --- Constantes ---
    static constexpr int WIDTH = 32;
    static constexpr int HEIGHT = 32;

    // --- Estructura para el temporizador del Tabe ---
    struct Timer {
       private:
        static constexpr Uint32 MINUTES_TO_MILLISECONDS = 60000;  // Factor de conversión de minutos a milisegundos

       public:
        Uint32 time_until_next_spawn;  // Tiempo restante para la próxima aparición
        Uint32 min_spawn_time;         // Tiempo mínimo entre apariciones (en milisegundos)
        Uint32 max_spawn_time;         // Tiempo máximo entre apariciones (en milisegundos)
        Uint32 current_time;           // Tiempo actual
        Uint32 delta_time;             // Diferencia de tiempo desde la última actualización
        Uint32 last_time;              // Tiempo de la última actualización
        bool is_paused{false};         // Indica si el temporizador está pausado (por pausa de juego)
        bool spawn_disabled{false};    // Indica si el spawning está deshabilitado permanentemente

        // Constructor - los parámetros min_time y max_time están en mintos
        Timer(float min_time, float max_time)
            : min_spawn_time(static_cast<Uint32>(min_time * MINUTES_TO_MILLISECONDS)),
              max_spawn_time(static_cast<Uint32>(max_time * MINUTES_TO_MILLISECONDS)),
              current_time(SDL_GetTicks()) {
            reset();
        }

        // Restablece el temporizador con un nuevo tiempo hasta la próxima aparición
        void reset() {
            Uint32 range = max_spawn_time - min_spawn_time;
            time_until_next_spawn = min_spawn_time + (rand() % (range + 1));
            last_time = SDL_GetTicks();
        }

        // Actualiza el temporizador, decrementando el tiempo hasta la próxima aparición
        void update() {
            current_time = SDL_GetTicks();

            // Solo actualizar si no está pausado (ni por juego ni por spawn deshabilitado)
            if (!is_paused && !spawn_disabled) {
                delta_time = current_time - last_time;

                if (time_until_next_spawn > delta_time) {
                    time_until_next_spawn -= delta_time;
                } else {
                    time_until_next_spawn = 0;
                }
            }

            // Siempre actualizar last_time para evitar saltos de tiempo al despausar
            last_time = current_time;
        }

        // Pausa o reanuda el temporizador
        void setPaused(bool paused) {
            if (is_paused != paused) {
                is_paused = paused;
                // Al despausar, actualizar last_time para evitar saltos
                if (!paused) {
                    last_time = SDL_GetTicks();
                }
            }
        }

        // Pausa o reanuda el spawning
        void setSpawnDisabled(bool disabled) {
            if (spawn_disabled != disabled) {
                spawn_disabled = disabled;
                // Al reactivar, actualizar last_time para evitar saltos
                if (!disabled) {
                    last_time = SDL_GetTicks();
                }
            }
        }

        // Indica si el temporizador ha finalizado
        [[nodiscard]] auto shouldSpawn() const -> bool {
            return time_until_next_spawn == 0 && !is_paused && !spawn_disabled;
        }
    };

    // --- Objetos y punteros ---
    std::unique_ptr<AnimatedSprite> sprite_;  // Sprite con los gráficos y animaciones

    // --- Variables de estado ---
    float x_ = 0;                                   // Posición X
    float y_ = 0;                                   // Posición Y
    float speed_ = 0.0F;                            // Velocidad de movimiento
    float accel_ = 0.0F;                            // Aceleración
    int fly_distance_ = 0;                          // Distancia de vuelo
    float waiting_counter_ = 0;                     // Tiempo que pasa quieto
    bool enabled_ = false;                          // Indica si el objeto está activo
    Direction direction_ = Direction::TO_THE_LEFT;  // Dirección actual
    Direction destiny_ = Direction::TO_THE_LEFT;    // Destino
    State state_ = State::FLY;                      // Estado actual
    float hit_counter_ = 0;                         // Contador para el estado HIT
    int number_of_hits_ = 0;                        // Cantidad de disparos recibidos
    bool has_bonus_ = true;                         // Indica si aún tiene el bonus para soltar
    Timer timer_;                                   // Temporizador para gestionar la aparición

    // --- Métodos internos ---
    void move(float delta_time);                             // Mueve el objeto (time-based)
    void shiftSprite() { sprite_->setPos(x_, y_); }          // Actualiza la posición del sprite
    void setRandomFlyPath(Direction direction, int length);  // Establece un vuelo aleatorio
    void updateState(float delta_time);                      // Actualiza el estado (time-based)
    void updateTimer();                                      // Actualiza el temporizador
    void disable();                                          // Deshabilita el objeto
};