// enemy.hpp - Clase para enemigos (ORNIs)
// © 2026 JailDesigner

#pragma once
#include <SDL3/SDL.h>

#include <array>
#include <cstdint>
#include <vector>

#include "core/defaults/enemies.hpp"
#include "core/entities/entity.hpp"
#include "core/types.hpp"
#include "game/entities/enemy_ai.hpp"

class Ship;

// Tipo de enemy
enum class EnemyType : uint8_t {
    PENTAGON = 0,  // Pentágono esquivador (zigzag)
    SQUARE = 1,    // Square perseguidor (tracks ship)
    PINWHEEL = 2,  // Molinillo agresivo (rápido, girando)
    STAR = 3,      // Estrella de 5 puntes (clone visual de Pentagon, comportament zigzag)
    ORB = 4,       // Orb gegant tough (HP=10, chase lent — primer enemic HP>1)
};

// Forward declaration — EnemyConfig viu a enemy_config.hpp i s'inclou només a enemy.cpp.
struct EnemyConfig;

// Estado de animación (palpitación + rotación acelerada)
struct EnemyAnimation {
    // Palpitación (efecto respiración)
    bool pulse_active = false;
    float pulse_phase = 0.0F;
    float pulse_frequency = 2.0F;
    float pulse_amplitude = 0.15F;
    float pulse_time_remaining = 0.0F;

    // Aceleración de rotación visual (modulación a largo plazo)
    float rotation_delta_base = 0.0F;
    float rotation_delta_target = 0.0F;
    float rotation_delta_t = 0.0F;
    float rotation_delta_duration = 0.0F;
};

class Enemy : public Entities::Entity {
   public:
    Enemy()
        : Entity(nullptr) {}
    explicit Enemy(Rendering::Renderer* renderer);

    void init() override { init(EnemyType::PENTAGON, nullptr); }
    void init(EnemyType type, const Vec2* ship_pos = nullptr);
    void update(float delta_time) override;
    void postUpdate(float delta_time) override;
    void draw() const override;

    // Override: Interfaz de Entity
    [[nodiscard]] auto isActive() const -> bool override { return is_active_; }

    // Override: Interfaz de colisión. El radi ve del config carregat per tipus.
    [[nodiscard]] auto getCollisionRadius() const -> float override { return collision_radius_; }
    // Mentre fa spawn (invulnerable) segueix col·lisionant: les bales el
    // poden abatre i el cos físic rebota amb la nau. El damage a la nau
    // segueix filtrat per `isInvulnerable()` al detectShipEnemy.
    [[nodiscard]] auto isCollidable() const -> bool override {
        return is_active_;
    }

    // Marcar destruido (desactiva el cuerpo físicamente: radius=0)
    void destroy();

    // Getters
    [[nodiscard]] auto getRotationDelta() const -> float { return rotation_delta_; }
    [[nodiscard]] auto getVelocityVector() const -> Vec2 { return body_.velocity; }
    // Configuració activa (carregada al darrer init()). Vàlida mentre l'enemic
    // ha estat inicialitzat almenys un cop; abans és nullptr.
    [[nodiscard]] auto getConfig() const -> const EnemyConfig& { return *config_; }

    // Referències als 2 ships per a AI de tracking/proximity/chase/flee.
    // nullptr = ship inexistent al match. El sistema d'IA filtra per ship->isActive().
    void setShips(const Ship* p1, const Ship* p2) { ships_ = {p1, p2}; }
    [[nodiscard]] auto getShips() const -> const std::array<const Ship*, 2>& { return ships_; }

    // Accessors per al sistema d'IA (Systems::EnemyAi).
    [[nodiscard]] auto getAiState() -> EnemyAiState& { return ai_state_; }
    [[nodiscard]] auto getAiTickTimers() -> std::vector<float>& { return ai_tick_timers_; }
    [[nodiscard]] auto getRotationBase() const -> float { return animation_.rotation_delta_base; }
    void setRotationDelta(float rot) { rotation_delta_ = rot; }
    // Public: el sistema d'IA reorienta la velocitat des d'un angle.
    void setVelocityFromAngle(float angle_movement, float speed);

    // Stage system API (base stats)
    [[nodiscard]] auto getBaseVelocity() const -> float;
    [[nodiscard]] auto getBaseRotation() const -> float;
    [[nodiscard]] auto getType() const -> EnemyType { return type_; }

    // Setters para multiplicadores de dificultad (stage system).
    // Establecen la velocidad escalar deseada manteniendo la dirección
    // actual del body_.velocity.
    void setVelocity(float speed);
    void setRotation(float rot) {
        rotation_delta_ = rot;
        animation_.rotation_delta_base = rot;
    }
    void setTrackingStrength(float strength);

    // Invulnerabilidad
    [[nodiscard]] auto isInvulnerable() const -> bool { return invulnerability_timer_ > 0.0F; }
    [[nodiscard]] auto getInvulnerabilityTime() const -> float { return invulnerability_timer_; }

    // Estado "herido": entre primer impacto de bala y explosión diferida.
    // shooter_id: id del jugador que herí; 0xFF = sin atribución (cadena, etc.).
    void hurt(uint8_t shooter_id = 0xFF);
    [[nodiscard]] auto isWounded() const -> bool { return wounded_timer_ > 0.0F; }
    [[nodiscard]] auto getWoundedTimer() const -> float { return wounded_timer_; }
    [[nodiscard]] auto woundExpiredThisFrame() const -> bool { return wound_expired_this_frame_; }
    void consumeWoundExpired() { wound_expired_this_frame_ = false; }
    [[nodiscard]] auto getLastHitBy() const -> uint8_t { return last_hit_by_; }

    // Salut: decrementada per l'acció DECREASE_HEALTH al dispatcher d'events.
    // Quan arriba a 0 o menys, el dispatcher dispara ON_NO_HEALTH (que
    // típicament encadena SET_HURT o DESTROY al YAML). last_hit_by s'actualitza
    // al decrement perquè la mort posterior atribueixi correctament el kill.
    [[nodiscard]] auto getHealth() const -> int { return health_; }
    void decrementHealth(uint8_t shooter_id = 0xFF) {
        --health_;
        last_hit_by_ = shooter_id;
    }

    // Flash visual: brief impacto-feedback quan rep un hit no letal (HP>1).
    // Disparat per l'acció FLASH; el render alça la lluminositat mentre dura.
    void triggerFlash() { flash_timer_ = Defaults::Enemies::Visual::FLASH_DURATION; }

    // Aplica un impulso (cambio inmediato de velocidad mass-aware) al cuerpo físico.
    void applyImpulse(const Vec2& impulse);

   private:
    // Configuració carregada per tipus (apunta a una entrada de EnemyRegistry).
    // nullptr abans del primer init(); per això getConfig() només és vàlid post-init.
    const EnemyConfig* config_{nullptr};

    // Cache local del radi (per evitar dereferenciar config_ a getCollisionRadius);
    // s'actualitza a init() segons el tipus.
    float collision_radius_{0.0F};

    float rotation_delta_{0.0F};  // Velocidad angular visual (rad/s)
    float rotation_{0.0F};        // Rotación visual acumulada
    bool is_active_{false};

    EnemyType type_{EnemyType::PENTAGON};
    EnemyAnimation animation_;

    // Estat per-instància que la primitiva de moviment manté entre frames.
    EnemyAiState ai_state_;

    // Timers paral·lels a config_->ai.tick: timers_[i] és el temps restant
    // (en segons) fins a la pròxima execució de l'acció i. Re-dimensionat a
    // init() segons la mida de config_->ai.tick.
    std::vector<float> ai_tick_timers_;

    // Referències als 2 ships per a AI de tracking/proximity/chase/flee.
    std::array<const Ship*, 2> ships_{nullptr, nullptr};

    // Invulnerabilidad post-spawn
    float invulnerability_timer_{0.0F};

    // Estado "herido"
    float wounded_timer_{0.0F};
    bool wound_expired_this_frame_{false};
    uint8_t last_hit_by_{0xFF};

    // Salut per-instància. Reseteja a config_->health a init(); el dispatcher
    // d'events la decrementa via DECREASE_HEALTH i dispara ON_NO_HEALTH quan
    // creua zero. Permet enemics tough (HP>1) sense canvis al motor.
    int health_{1};

    // Flash visual temporitzat per a feedback d'impacte parcial (HP>1).
    // L'acció FLASH el reseteja a FLASH_DURATION; draw() alça la lluminositat
    // mentre dura, i update() el decrementa.
    float flash_timer_{0.0F};

    // Métodos privados
    void updateAnimation(float delta_time);
    void updatePulse(float delta_time);
    void updateRotationAcceleration(float delta_time);
    [[nodiscard]] auto computeCurrentScale() const -> float;
    // Static: passa els paràmetres com a args per no acoblar a *this.
    static auto attemptSafeSpawn(const Vec2& ship_pos, float collision_radius, float safety_distance, float& out_x, float& out_y) -> bool;
};
