// enemic.cpp - Implementació d'enemics (ORNIs)
// © 1999 Visente i Sergi (versió Pascal)
// © 2025 Port a C++20 amb SDL3

#include "game/entities/enemic.hpp"

#include <cmath>
#include <cstdlib>
#include <iostream>

#include "core/defaults.hpp"
#include "core/graphics/shape_loader.hpp"
#include "core/rendering/shape_renderer.hpp"
#include "game/constants.hpp"

Enemic::Enemic(SDL_Renderer* renderer)
    : renderer_(renderer),
      centre_({0.0f, 0.0f}),
      angle_(0.0f),
      velocitat_(0.0f),
      drotacio_(0.0f),
      rotacio_(0.0f),
      esta_(false),
      brightness_(Defaults::Brightness::ENEMIC),
      tipus_(TipusEnemic::PENTAGON),
      tracking_timer_(0.0f),
      ship_position_(nullptr),
      tracking_strength_(0.5f),        // Default tracking strength
      timer_invulnerabilitat_(0.0f) {  // Start vulnerable
    // [NUEVO] Forma es carrega a inicialitzar() segons el tipus
    // Constructor no carrega forma per permetre tipus diferents
}

void Enemic::inicialitzar(TipusEnemic tipus, const Punt* ship_pos) {
    // Guardar tipus
    tipus_ = tipus;

    // Carregar forma segons el tipus
    const char* shape_file;
    float drotacio_min, drotacio_max;

    switch (tipus_) {
        case TipusEnemic::PENTAGON:
            shape_file = Defaults::Enemies::Pentagon::SHAPE_FILE;
            velocitat_ = Defaults::Enemies::Pentagon::VELOCITAT;
            drotacio_min = Defaults::Enemies::Pentagon::DROTACIO_MIN;
            drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX;
            break;

        case TipusEnemic::QUADRAT:
            shape_file = Defaults::Enemies::Quadrat::SHAPE_FILE;
            velocitat_ = Defaults::Enemies::Quadrat::VELOCITAT;
            drotacio_min = Defaults::Enemies::Quadrat::DROTACIO_MIN;
            drotacio_max = Defaults::Enemies::Quadrat::DROTACIO_MAX;
            tracking_timer_ = 0.0f;
            break;

        case TipusEnemic::MOLINILLO:
            shape_file = Defaults::Enemies::Molinillo::SHAPE_FILE;
            velocitat_ = Defaults::Enemies::Molinillo::VELOCITAT;
            drotacio_min = Defaults::Enemies::Molinillo::DROTACIO_MIN;
            drotacio_max = Defaults::Enemies::Molinillo::DROTACIO_MAX;
            break;
    }

    // Carregar forma
    forma_ = Graphics::ShapeLoader::load(shape_file);
    if (!forma_ || !forma_->es_valida()) {
        std::cerr << "[Enemic] Error: no s'ha pogut carregar " << shape_file << std::endl;
    }

    // [MODIFIED] Posició aleatòria amb comprovació de seguretat
    float min_x, max_x, min_y, max_y;
    Constants::obtenir_limits_zona_segurs(Defaults::Entities::ENEMY_RADIUS,
        min_x,
        max_x,
        min_y,
        max_y);

    if (ship_pos != nullptr) {
        // [NEW] Safe spawn: attempt to find position away from ship
        bool found_safe_position = false;

        for (int attempt = 0; attempt < Defaults::Enemies::Spawn::MAX_SPAWN_ATTEMPTS; attempt++) {
            float candidate_x, candidate_y;

            if (intent_spawn_safe(*ship_pos, candidate_x, candidate_y)) {
                centre_.x = candidate_x;
                centre_.y = candidate_y;
                found_safe_position = true;
                break;
            }
        }

        if (!found_safe_position) {
            // Fallback: spawn anywhere (user's preference)
            int range_x = static_cast<int>(max_x - min_x);
            int range_y = static_cast<int>(max_y - min_y);
            centre_.x = static_cast<float>((std::rand() % range_x) + static_cast<int>(min_x));
            centre_.y = static_cast<float>((std::rand() % range_y) + static_cast<int>(min_y));

            std::cout << "[Enemic] Advertència: spawn sense zona segura després de "
                      << Defaults::Enemies::Spawn::MAX_SPAWN_ATTEMPTS << " intents" << std::endl;
        }
    } else {
        // [EXISTING] No ship position: spawn anywhere (backward compatibility)
        int range_x = static_cast<int>(max_x - min_x);
        int range_y = static_cast<int>(max_y - min_y);
        centre_.x = static_cast<float>((std::rand() % range_x) + static_cast<int>(min_x));
        centre_.y = static_cast<float>((std::rand() % range_y) + static_cast<int>(min_y));
    }

    // Angle aleatori de moviment
    angle_ = (std::rand() % 360) * Constants::PI / 180.0f;

    // Rotació visual aleatòria (rad/s) dins del rang del tipus
    float drotacio_range = drotacio_max - drotacio_min;
    drotacio_ = drotacio_min + (static_cast<float>(std::rand()) / RAND_MAX) * drotacio_range;
    rotacio_ = 0.0f;

    // Inicialitzar estat d'animació
    animacio_ = AnimacioEnemic();  // Reset to defaults
    animacio_.drotacio_base = drotacio_;
    animacio_.drotacio_objetivo = drotacio_;
    animacio_.drotacio_t = 1.0f;  // Start without interpolating

    // [NEW] Inicialitzar invulnerabilitat
    timer_invulnerabilitat_ = Defaults::Enemies::Spawn::INVULNERABILITY_DURATION;  // 3.0s
    brightness_ = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START;      // 0.3f

    // Activar
    esta_ = true;
}

void Enemic::actualitzar(float delta_time) {
    if (esta_) {
        // [NEW] Update invulnerability timer and brightness
        if (timer_invulnerabilitat_ > 0.0f) {
            timer_invulnerabilitat_ -= delta_time;

            if (timer_invulnerabilitat_ < 0.0f) {
                timer_invulnerabilitat_ = 0.0f;
            }

            // [NEW] Update brightness with LERP during invulnerability
            float t_inv = timer_invulnerabilitat_ / Defaults::Enemies::Spawn::INVULNERABILITY_DURATION;
            float t = 1.0f - t_inv;                      // 0.0 → 1.0
            float smooth_t = t * t * (3.0f - 2.0f * t);  // smoothstep

            constexpr float START = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START;
            constexpr float END = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_END;
            brightness_ = START + (END - START) * smooth_t;
        }

        // Moviment autònom
        mou(delta_time);

        // Actualitzar animacions (palpitació, rotació accelerada)
        actualitzar_animacio(delta_time);

        // Rotació visual (time-based: drotacio_ està en rad/s)
        rotacio_ += drotacio_ * delta_time;
    }
}

void Enemic::dibuixar() const {
    if (esta_ && forma_) {
        // Calculate animated scale (includes invulnerability LERP)
        float escala = calcular_escala_actual();

        // brightness_ is already updated in actualitzar()
        Rendering::render_shape(renderer_, forma_, centre_, rotacio_, escala, true, 1.0f, brightness_);
    }
}

void Enemic::mou(float delta_time) {
    // Dispatcher: crida el comportament específic segons el tipus
    switch (tipus_) {
        case TipusEnemic::PENTAGON:
            comportament_pentagon(delta_time);
            break;
        case TipusEnemic::QUADRAT:
            comportament_quadrat(delta_time);
            break;
        case TipusEnemic::MOLINILLO:
            comportament_molinillo(delta_time);
            break;
    }
}

void Enemic::comportament_pentagon(float delta_time) {
    // Pentagon: zigzag esquivador (frequent direction changes)
    // Similar a comportament original però amb probabilitat més alta

    float velocitat_efectiva = velocitat_ * delta_time;

    // Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt)
    float dy = velocitat_efectiva * std::sin(angle_ - Constants::PI / 2.0f);
    float dx = velocitat_efectiva * std::cos(angle_ - Constants::PI / 2.0f);

    float new_y = centre_.y + dy;
    float new_x = centre_.x + dx;

    // Obtenir límits segurs
    float min_x, max_x, min_y, max_y;
    Constants::obtenir_limits_zona_segurs(Defaults::Entities::ENEMY_RADIUS,
        min_x,
        max_x,
        min_y,
        max_y);

    // Zigzag: canvi d'angle més freqüent en tocar límits
    if (new_y >= min_y && new_y <= max_y) {
        centre_.y = new_y;
    } else {
        // Probabilitat més alta de canvi d'angle
        if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Pentagon::CANVI_ANGLE_PROB) {
            float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) *
                Defaults::Enemies::Pentagon::CANVI_ANGLE_MAX;
            angle_ += (std::rand() % 2 == 0) ? rand_angle : -rand_angle;
        }
    }

    if (new_x >= min_x && new_x <= max_x) {
        centre_.x = new_x;
    } else {
        if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Pentagon::CANVI_ANGLE_PROB) {
            float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) *
                Defaults::Enemies::Pentagon::CANVI_ANGLE_MAX;
            angle_ += (std::rand() % 2 == 0) ? rand_angle : -rand_angle;
        }
    }
}

void Enemic::comportament_quadrat(float delta_time) {
    // Quadrat: perseguidor (tracks player position)

    // Update tracking timer
    tracking_timer_ += delta_time;

    // Periodically update angle toward ship
    if (tracking_timer_ >= Defaults::Enemies::Quadrat::TRACKING_INTERVAL) {
        tracking_timer_ = 0.0f;

        if (ship_position_) {
            // Calculate angle to ship
            float dx = ship_position_->x - centre_.x;
            float dy = ship_position_->y - centre_.y;
            float target_angle = std::atan2(dy, dx) + Constants::PI / 2.0f;

            // Interpolate toward target angle
            float angle_diff = target_angle - angle_;

            // Normalize angle difference to [-π, π]
            while (angle_diff > Constants::PI) angle_diff -= 2.0f * Constants::PI;
            while (angle_diff < -Constants::PI) angle_diff += 2.0f * Constants::PI;

            // Apply tracking strength (uses member variable, defaults to 0.5)
            angle_ += angle_diff * tracking_strength_;
        }
    }

    // Move in current direction
    float velocitat_efectiva = velocitat_ * delta_time;
    float dy = velocitat_efectiva * std::sin(angle_ - Constants::PI / 2.0f);
    float dx = velocitat_efectiva * std::cos(angle_ - Constants::PI / 2.0f);

    float new_y = centre_.y + dy;
    float new_x = centre_.x + dx;

    // Obtenir límits segurs
    float min_x, max_x, min_y, max_y;
    Constants::obtenir_limits_zona_segurs(Defaults::Entities::ENEMY_RADIUS,
        min_x,
        max_x,
        min_y,
        max_y);

    // Bounce on walls (simple reflection)
    if (new_y >= min_y && new_y <= max_y) {
        centre_.y = new_y;
    } else {
        angle_ = -angle_;  // Vertical reflection
    }

    if (new_x >= min_x && new_x <= max_x) {
        centre_.x = new_x;
    } else {
        angle_ = Constants::PI - angle_;  // Horizontal reflection
    }
}

void Enemic::comportament_molinillo(float delta_time) {
    // Molinillo: agressiu (fast, straight lines, proximity spin-up)

    // Check proximity to ship for spin-up effect
    if (ship_position_) {
        float dx = ship_position_->x - centre_.x;
        float dy = ship_position_->y - centre_.y;
        float distance = std::sqrt(dx * dx + dy * dy);

        if (distance < Defaults::Enemies::Molinillo::PROXIMITY_DISTANCE) {
            // Temporarily boost rotation speed when near ship
            float boost = Defaults::Enemies::Molinillo::DROTACIO_PROXIMITY_MULTIPLIER;
            drotacio_ = animacio_.drotacio_base * boost;
        } else {
            // Normal rotation speed
            drotacio_ = animacio_.drotacio_base;
        }
    }

    // Fast straight-line movement
    float velocitat_efectiva = velocitat_ * delta_time;
    float dy = velocitat_efectiva * std::sin(angle_ - Constants::PI / 2.0f);
    float dx = velocitat_efectiva * std::cos(angle_ - Constants::PI / 2.0f);

    float new_y = centre_.y + dy;
    float new_x = centre_.x + dx;

    // Obtenir límits segurs
    float min_x, max_x, min_y, max_y;
    Constants::obtenir_limits_zona_segurs(Defaults::Entities::ENEMY_RADIUS,
        min_x,
        max_x,
        min_y,
        max_y);

    // Rare angle changes on wall hits
    if (new_y >= min_y && new_y <= max_y) {
        centre_.y = new_y;
    } else {
        if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) {
            float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) *
                Defaults::Enemies::Molinillo::CANVI_ANGLE_MAX;
            angle_ += (std::rand() % 2 == 0) ? rand_angle : -rand_angle;
        }
    }

    if (new_x >= min_x && new_x <= max_x) {
        centre_.x = new_x;
    } else {
        if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) {
            float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) *
                Defaults::Enemies::Molinillo::CANVI_ANGLE_MAX;
            angle_ += (std::rand() % 2 == 0) ? rand_angle : -rand_angle;
        }
    }
}

void Enemic::actualitzar_animacio(float delta_time) {
    actualitzar_palpitacio(delta_time);
    actualitzar_rotacio_accelerada(delta_time);
}

void Enemic::actualitzar_palpitacio(float delta_time) {
    if (animacio_.palpitacio_activa) {
        // Advance phase (2π * frequency * dt)
        animacio_.palpitacio_fase += 2.0f * Constants::PI * animacio_.palpitacio_frequencia * delta_time;

        // Decrement timer
        animacio_.palpitacio_temps_restant -= delta_time;

        // Deactivate when timer expires
        if (animacio_.palpitacio_temps_restant <= 0.0f) {
            animacio_.palpitacio_activa = false;
        }
    } else {
        // Random trigger (probability per second)
        float rand_val = static_cast<float>(std::rand()) / RAND_MAX;
        float trigger_prob = Defaults::Enemies::Animation::PALPITACIO_TRIGGER_PROB * delta_time;

        if (rand_val < trigger_prob) {
            // Activate palpitation
            animacio_.palpitacio_activa = true;
            animacio_.palpitacio_fase = 0.0f;

            // Randomize parameters
            float freq_range = Defaults::Enemies::Animation::PALPITACIO_FREQ_MAX -
                Defaults::Enemies::Animation::PALPITACIO_FREQ_MIN;
            animacio_.palpitacio_frequencia = Defaults::Enemies::Animation::PALPITACIO_FREQ_MIN +
                (static_cast<float>(std::rand()) / RAND_MAX) * freq_range;

            float amp_range = Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MAX -
                Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MIN;
            animacio_.palpitacio_amplitud = Defaults::Enemies::Animation::PALPITACIO_AMPLITUD_MIN +
                (static_cast<float>(std::rand()) / RAND_MAX) * amp_range;

            float dur_range = Defaults::Enemies::Animation::PALPITACIO_DURACIO_MAX -
                Defaults::Enemies::Animation::PALPITACIO_DURACIO_MIN;
            animacio_.palpitacio_temps_restant = Defaults::Enemies::Animation::PALPITACIO_DURACIO_MIN +
                (static_cast<float>(std::rand()) / RAND_MAX) * dur_range;
        }
    }
}

void Enemic::actualitzar_rotacio_accelerada(float delta_time) {
    if (animacio_.drotacio_t < 1.0f) {
        // Transitioning to new target
        animacio_.drotacio_t += delta_time / animacio_.drotacio_duracio;

        if (animacio_.drotacio_t >= 1.0f) {
            animacio_.drotacio_t = 1.0f;
            animacio_.drotacio_base = animacio_.drotacio_objetivo;  // Reached target
            drotacio_ = animacio_.drotacio_base;
        } else {
            // Smoothstep interpolation: t² * (3 - 2t)
            float t = animacio_.drotacio_t;
            float smooth_t = t * t * (3.0f - 2.0f * t);

            // Interpolate between base and target
            float initial = animacio_.drotacio_base;
            float target = animacio_.drotacio_objetivo;
            drotacio_ = initial + (target - initial) * smooth_t;
        }
    } else {
        // Random trigger for new acceleration
        float rand_val = static_cast<float>(std::rand()) / RAND_MAX;
        float trigger_prob = Defaults::Enemies::Animation::ROTACIO_ACCEL_TRIGGER_PROB * delta_time;

        if (rand_val < trigger_prob) {
            // Start new transition
            animacio_.drotacio_t = 0.0f;

            // Randomize target speed (multiplier * base)
            float mult_range = Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MAX -
                Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MIN;
            float multiplier = Defaults::Enemies::Animation::ROTACIO_ACCEL_MULTIPLIER_MIN +
                (static_cast<float>(std::rand()) / RAND_MAX) * mult_range;

            animacio_.drotacio_objetivo = animacio_.drotacio_base * multiplier;

            // Randomize duration
            float dur_range = Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MAX -
                Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MIN;
            animacio_.drotacio_duracio = Defaults::Enemies::Animation::ROTACIO_ACCEL_DURACIO_MIN +
                (static_cast<float>(std::rand()) / RAND_MAX) * dur_range;
        }
    }
}

float Enemic::calcular_escala_actual() const {
    float escala = 1.0f;

    // [NEW] Invulnerability LERP prioritza sobre palpitació
    if (timer_invulnerabilitat_ > 0.0f) {
        // Calculate t: 0.0 at spawn → 1.0 at end
        float t_inv = timer_invulnerabilitat_ / Defaults::Enemies::Spawn::INVULNERABILITY_DURATION;
        float t = 1.0f - t_inv;  // 0.0 → 1.0

        // Apply smoothstep: t² * (3 - 2t)
        float smooth_t = t * t * (3.0f - 2.0f * t);

        // LERP scale from 0.0 to 1.0
        constexpr float START = Defaults::Enemies::Spawn::INVULNERABILITY_SCALE_START;
        constexpr float END = Defaults::Enemies::Spawn::INVULNERABILITY_SCALE_END;
        escala = START + (END - START) * smooth_t;
    } else if (animacio_.palpitacio_activa) {
        // [EXISTING] Palpitació només quan no invulnerable
        escala += animacio_.palpitacio_amplitud * std::sin(animacio_.palpitacio_fase);
    }

    return escala;
}

// [NEW] Stage system API implementations

float Enemic::get_base_velocity() const {
    switch (tipus_) {
        case TipusEnemic::PENTAGON:
            return Defaults::Enemies::Pentagon::VELOCITAT;
        case TipusEnemic::QUADRAT:
            return Defaults::Enemies::Quadrat::VELOCITAT;
        case TipusEnemic::MOLINILLO:
            return Defaults::Enemies::Molinillo::VELOCITAT;
    }
    return 0.0f;
}

float Enemic::get_base_rotation() const {
    // Return the base rotation speed (drotacio_base if available, otherwise current drotacio_)
    return animacio_.drotacio_base != 0.0f ? animacio_.drotacio_base : drotacio_;
}

void Enemic::set_tracking_strength(float strength) {
    // Only applies to QUADRAT type
    if (tipus_ == TipusEnemic::QUADRAT) {
        tracking_strength_ = strength;
    }
}

// [NEW] Safe spawn helper - checks if position is away from ship
bool Enemic::intent_spawn_safe(const Punt& ship_pos, float& out_x, float& out_y) {
    // Generate random position within safe bounds
    float min_x, max_x, min_y, max_y;
    Constants::obtenir_limits_zona_segurs(Defaults::Entities::ENEMY_RADIUS,
        min_x,
        max_x,
        min_y,
        max_y);

    int range_x = static_cast<int>(max_x - min_x);
    int range_y = static_cast<int>(max_y - min_y);

    out_x = static_cast<float>((std::rand() % range_x) + static_cast<int>(min_x));
    out_y = static_cast<float>((std::rand() % range_y) + static_cast<int>(min_y));

    // Check Euclidean distance to ship
    float dx = out_x - ship_pos.x;
    float dy = out_y - ship_pos.y;
    float distancia = std::sqrt(dx * dx + dy * dy);

    // Return true if position is safe (>= 36px from ship)
    return distancia >= Defaults::Enemies::Spawn::SAFETY_DISTANCE;
}
