#include "game/level.h"
#include "game/player.h"
#include "game/enemy.h"
#include "game/projectile.h"
#include "game/hazards.h"
#include "game/powerup.h"
#include "game/drone.h"
#include "game/spacecraft.h"
#include "game/sprites.h"
#include "game/entity_registry.h"
#include "engine/core.h"
#include "engine/renderer.h"
#include "engine/physics.h"
#include "engine/particle.h"
#include "engine/audio.h"
#include "engine/input.h"
#include "engine/camera.h"
#include "engine/assets.h"
#include <stdio.h>
#include <string.h>
#include <math.h>

/* ── Sound effects ───────────────────────────────── */
static Sound s_sfx_hit;
static Sound s_sfx_enemy_death;
static Sound s_sfx_pickup;
static bool  s_sfx_loaded = false;

/* ── Shared level setup (after tilemap is ready) ─── */
static bool level_setup(Level *level) {
    /* Initialize subsystems */
    entity_manager_init(&level->entities);
    camera_init(&level->camera, SCREEN_WIDTH, SCREEN_HEIGHT);
    particle_init();

    /* Load combat sound effects */
    if (!s_sfx_loaded) {
        s_sfx_hit         = audio_load_sound("assets/sounds/hitHurt.wav");
        s_sfx_enemy_death = audio_load_sound("assets/sounds/teleport.wav");
        s_sfx_pickup      = audio_load_sound("assets/sounds/powerUp.wav");
        s_sfx_loaded = true;
    }

    /* Generate spritesheet */
    sprites_init_anims();
    if (!sprites_generate(g_engine.renderer)) {
        fprintf(stderr, "Warning: failed to generate spritesheet\n");
    }

    /* Register all entity types via the central registry */
    entity_registry_init(&level->entities);

    /* Apply level gravity (0 = use default) */
    if (level->map.gravity > 0) {
        physics_set_gravity(level->map.gravity);
    } else {
        physics_set_gravity(DEFAULT_GRAVITY);
    }

    /* Apply level wind (0 = no wind) */
    physics_set_wind(level->map.wind);

    /* Apply level background color */
    if (level->map.has_bg_color) {
        renderer_set_clear_color(level->map.bg_color);
    }

    /* Initialize parallax backgrounds */
    parallax_init(&level->parallax);
    if (level->map.parallax_far_path[0]) {
        SDL_Texture *far_tex = assets_get_texture(level->map.parallax_far_path);
        if (far_tex) parallax_set_far(&level->parallax, far_tex, 0.05f, 0.05f);
    }
    if (level->map.parallax_near_path[0]) {
        SDL_Texture *near_tex = assets_get_texture(level->map.parallax_near_path);
        if (near_tex) parallax_set_near(&level->parallax, near_tex, 0.15f, 0.10f);
    }
    /* Generate procedural backgrounds for any layers not loaded from file */
    if (!level->parallax.far_layer.active && !level->parallax.near_layer.active
        && level->map.parallax_style != 0) {
        /* Use themed parallax when a style is specified */
        parallax_generate_themed(&level->parallax, g_engine.renderer,
                                 (ParallaxStyle)level->map.parallax_style);
    } else {
        /* Default: generic stars + nebula */
        if (!level->parallax.far_layer.active) {
            parallax_generate_stars(&level->parallax, g_engine.renderer);
        }
        if (!level->parallax.near_layer.active) {
            parallax_generate_nebula(&level->parallax, g_engine.renderer);
        }
    }

    /* Set camera bounds to level size */
    camera_set_bounds(&level->camera,
        (float)(level->map.width * TILE_SIZE),
        (float)(level->map.height * TILE_SIZE));

    /* Spawn entities from level data (via registry) */
    level->has_intro_ship = false;
    for (int i = 0; i < level->map.entity_spawn_count; i++) {
        EntitySpawn *es = &level->map.entity_spawns[i];
        Vec2 pos = vec2(es->x, es->y);
        entity_registry_spawn(&level->entities, es->type_name, pos);
        if (strcmp(es->type_name, "spacecraft") == 0) {
            level->has_intro_ship = true;
        }
    }

    /* Spawn player — deferred if the level has an intro spacecraft */
    if (level->has_intro_ship) {
        level->player_spawned = false;
    } else {
        Entity *player = player_spawn(&level->entities, level->map.player_spawn);
        if (!player) {
            fprintf(stderr, "Failed to spawn player!\n");
            return false;
        }
        if (level->map.player_unarmed) {
            PlayerData *ppd = (PlayerData *)player->data;
            if (ppd) ppd->has_gun = false;
        }
        level->player_spawned = true;
    }

    /* Load level music (playback deferred to first update —
     * browsers require user interaction before playing audio) */
    if (level->map.music_path[0]) {
        level->music = audio_load_music(level->map.music_path);
    }
    level->music_started = false;

    printf("Level loaded successfully.\n");
    return true;
}

bool level_load(Level *level, const char *path) {
    memset(level, 0, sizeof(Level));

    /* Load tilemap from file */
    if (!tilemap_load(&level->map, path, g_engine.renderer)) {
        return false;
    }

    return level_setup(level);
}

bool level_load_generated(Level *level, Tilemap *gen_map) {
    memset(level, 0, sizeof(Level));

    /* Take ownership of the generated tilemap */
    level->map = *gen_map;
    memset(gen_map, 0, sizeof(Tilemap)); /* prevent double-free */

    /* Load tileset texture. Use the generator's tileset_path if set,
     * otherwise fall back to the default tileset. */
    if (!level->map.tileset_path[0]) {
        snprintf(level->map.tileset_path, sizeof(level->map.tileset_path),
                 "%s", "assets/tiles/tileset.png");
    }
    level->map.tileset = assets_get_texture(level->map.tileset_path);
    if (level->map.tileset) {
        int tex_w;
        SDL_QueryTexture(level->map.tileset, NULL, NULL, &tex_w, NULL);
        level->map.tileset_cols = tex_w / TILE_SIZE;
    }

    return level_setup(level);
}

/* ── Collision handling ──────────────────────────── */

/* Forward declaration for shake access */
static Camera *s_active_camera = NULL;

static void damage_entity(Entity *target, int damage) {
    target->health -= damage;
    if (target->health <= 0) {
        target->flags |= ENTITY_DEAD;

        /* Death particles — centered on entity */
        Vec2 center = vec2(
            target->body.pos.x + target->body.size.x * 0.5f,
            target->body.pos.y + target->body.size.y * 0.5f
        );
        SDL_Color death_color;
        if (target->type == ENT_ENEMY_GRUNT) {
            death_color = (SDL_Color){200, 60, 60, 255};   /* red debris  */
        } else if (target->type == ENT_ENEMY_FLYER) {
            death_color = (SDL_Color){140, 80, 200, 255};   /* purple puff */
        } else if (target->type == ENT_TURRET || target->type == ENT_LASER_TURRET) {
            death_color = (SDL_Color){160, 160, 160, 255};  /* metal scraps */
        } else if (target->type == ENT_ENEMY_CHARGER) {
            death_color = (SDL_Color){220, 140, 40, 255};   /* orange spark */
        } else if (target->type == ENT_SPAWNER) {
            death_color = (SDL_Color){180, 60, 180, 255};   /* purple burst */
        } else {
            death_color = (SDL_Color){200, 200, 200, 255};  /* grey        */
        }
        particle_emit_death_puff(center, death_color);

        /* Screen shake on kill */
        if (s_active_camera) {
            camera_shake(s_active_camera, 2.0f, 0.15f);
        }

        audio_play_sound_at(s_sfx_enemy_death, 80, center, 0);
    }
}

static void damage_player(Entity *player, int damage, Entity *source) {
    PlayerData *ppd = (PlayerData *)player->data;
    damage_entity(player, damage);

    /* Screen shake on player hit (stronger) */
    if (s_active_camera) {
        camera_shake(s_active_camera, 4.0f, 0.2f);
    }

    audio_play_sound(s_sfx_hit, 100);

    if (player->health > 0 && ppd) {
        ppd->inv_timer = PLAYER_INV_TIME;
        player->flags |= ENTITY_INVINCIBLE;

        /* Knockback away from source */
        if (source) {
            float knock_dir = (player->body.pos.x < source->body.pos.x) ?
                -1.0f : 1.0f;
            player->body.vel.x = knock_dir * 150.0f;
            player->body.vel.y = -150.0f;
        }
    }
}

static void handle_collisions(EntityManager *em) {
    /* Find the player */
    Entity *player = NULL;
    for (int i = 0; i < em->count; i++) {
        Entity *e = &em->entities[i];
        if (e->active && e->type == ENT_PLAYER && !(e->flags & ENTITY_DEAD)) {
            player = e;
            break;
        }
    }

    for (int i = 0; i < em->count; i++) {
        Entity *a = &em->entities[i];
        if (!a->active) continue;

        /* ── Projectile vs entities ──────────── */
        if (a->type == ENT_PROJECTILE) {
            if (projectile_is_impacting(a)) continue;
            bool from_player = projectile_is_from_player(a);

            for (int j = 0; j < em->count; j++) {
                Entity *b = &em->entities[j];
                if (!b->active || b == a) continue;
                if (b->flags & ENTITY_DEAD) continue;

                bool hit = false;

                /* Player bullet hits enemies */
                if (from_player && entity_is_enemy(b)) {
                    if (physics_overlap(&a->body, &b->body)) {
                        damage_entity(b, a->damage);
                        hit = true;
                    }
                }

                /* Enemy bullet hits player */
                if (!from_player && b->type == ENT_PLAYER &&
                    !(b->flags & ENTITY_INVINCIBLE)) {
                    if (physics_overlap(&a->body, &b->body)) {
                        damage_player(b, a->damage, NULL);
                        hit = true;
                    }
                }

                if (hit) {
                    projectile_hit(a);
                    /* If projectile was destroyed or is impacting, stop checking */
                    if (!a->active || projectile_is_impacting(a)) break;
                }
            }
        }

        /* ── Enemy contact damage to player ──── */
        if (player && !(player->flags & ENTITY_INVINCIBLE) &&
            entity_is_enemy(a) && !(a->flags & ENTITY_DEAD)) {
            if (physics_overlap(&a->body, &player->body)) {
                /* Check if player is stomping (falling onto enemy from above) */
                bool stomping = (player->body.vel.y > 0) &&
                    (player->body.pos.y + player->body.size.y <
                     a->body.pos.y + a->body.size.y * 0.5f);

                if (stomping) {
                    damage_entity(a, 2);
                    player->body.vel.y = -PLAYER_JUMP_FORCE * 0.7f;
                } else {
                    damage_player(player, a->damage, a);
                }
            }
        }

        /* ── Powerup pickup ────────────────────── */
        if (player && a->type == ENT_POWERUP && a->active &&
            !(a->flags & ENTITY_DEAD)) {
            if (physics_overlap(&a->body, &player->body)) {
                PowerupData *pd = (PowerupData *)a->data;
                bool picked_up = false;

                if (pd) {
                    switch (pd->kind) {
                    case POWERUP_HEALTH:
                        if (player->health < player->max_health) {
                            player->health++;
                            picked_up = true;
                        }
                        break;

                    case POWERUP_JETPACK: {
                        PlayerData *ppd = (PlayerData *)player->data;
                        if (ppd) {
                            ppd->dash_charges = ppd->dash_max_charges;
                            ppd->dash_recharge_timer = 0.0f;
                            ppd->jetpack_boost_timer = PLAYER_JETPACK_BOOST_DURATION;
                            picked_up = true;
                        }
                        break;
                    }

                    case POWERUP_FUEL: {
                        PlayerData *ppd = (PlayerData *)player->data;
                        if (ppd && ppd->dash_charges < ppd->dash_max_charges) {
                            ppd->dash_charges++;
                            /* Keep current recharge progress toward the next
                             * charge. Only zero the timer when fully charged
                             * so the HUD stops showing a partial bar. */
                            if (ppd->dash_charges >= ppd->dash_max_charges) {
                                ppd->dash_recharge_timer = 0.0f;
                            }
                            picked_up = true;
                        }
                        break;
                    }

                    case POWERUP_DRONE:
                        drone_spawn(em, vec2(
                            player->body.pos.x + player->body.size.x * 0.5f,
                            player->body.pos.y
                        ));
                        picked_up = true;
                        break;

                    case POWERUP_GUN:
                        if (!player_has_gun(player)) {
                            player_give_gun(player);
                            picked_up = true;
                        }
                        break;

                    default:
                        break;
                    }
                }

                if (picked_up) {
                    /* Pickup particles */
                    Vec2 center = vec2(
                        a->body.pos.x + a->body.size.x * 0.5f,
                        a->body.pos.y + a->body.size.y * 0.5f
                    );
                    particle_emit_spark(center, (SDL_Color){255, 255, 100, 255});
                    audio_play_sound_at(s_sfx_pickup, 80, center, 0);

                    /* Destroy the powerup */
                    a->flags |= ENTITY_DEAD;
                }
            }
        }
    }
}

/* ── Exit zone / exit ship ───────────────────────── */

/* Landing offset: where the ship lands relative to the exit zone center.
 * The ship lands a bit to the left of the exit zone so the player walks
 * rightward into it. */
#define EXIT_SHIP_LAND_OFFSET_X (-SPACECRAFT_WIDTH * 0.5f)

/* Find the active, alive player entity (or NULL). */
static Entity *find_player(EntityManager *em) {
    for (int i = 0; i < em->count; i++) {
        Entity *e = &em->entities[i];
        if (e->active && e->type == ENT_PLAYER && !(e->flags & ENTITY_DEAD))
            return e;
    }
    return NULL;
}

/* Find the exit spacecraft entity (or NULL). */
static Entity *find_exit_ship(EntityManager *em) {
    for (int i = 0; i < em->count; i++) {
        Entity *e = &em->entities[i];
        if (e->active && e->type == ENT_SPACECRAFT && spacecraft_is_exit_ship(e))
            return e;
    }
    return NULL;
}

/* Spawn the exit ship when the player touches an exit zone. */
static void check_exit_ship_trigger(Level *level) {
    if (level->exit_triggered) return;
    if (level->exit_ship_spawned) return;
    if (level->map.exit_zone_count == 0) return;

    Entity *player = find_player(&level->entities);
    if (!player) return;

    for (int i = 0; i < level->map.exit_zone_count; i++) {
        const ExitZone *ez = &level->map.exit_zones[i];
        if (physics_aabb_overlap(
                player->body.pos, player->body.size,
                vec2(ez->x, ez->y), vec2(ez->w, ez->h))) {
            /* Land the ship so its bottom aligns with the exit zone bottom.
             * land_pos is the top-left of the ship's resting position. */
            float land_x = ez->x + ez->w * 0.5f + EXIT_SHIP_LAND_OFFSET_X;
            float land_y = ez->y + ez->h - (float)SPACECRAFT_HEIGHT;

            Entity *ship = spacecraft_spawn_exit(&level->entities,
                                                  vec2(land_x, land_y));
            if (ship) {
                level->exit_ship_spawned = true;
                level->exit_zone_idx = i;
                printf("Exit ship triggered (zone %d)\n", i);
            }
            return;
        }
    }
}

/* Handle the exit ship boarding and departure sequence. */
static void update_exit_ship(Level *level) {
    if (level->exit_triggered) return;
    if (!level->exit_ship_spawned) return;

    Entity *ship = find_exit_ship(&level->entities);

    /* Ship may have been destroyed (SC_DONE) */
    if (!ship) {
        if (level->exit_ship_boarded) {
            /* Ship finished its departure — trigger the level exit */
            const ExitZone *ez = &level->map.exit_zones[level->exit_zone_idx];
            level->exit_triggered = true;
            snprintf(level->exit_target, sizeof(level->exit_target),
                     "%s", ez->target);
            printf("Exit ship departed -> %s\n",
                   ez->target[0] ? ez->target : "(victory)");
        }
        return;
    }

    /* Wait for the ship to land, then check for player boarding */
    if (!level->exit_ship_boarded && spacecraft_is_landed(ship)) {
        Entity *player = find_player(&level->entities);
        if (player) {
            /* Check overlap between player and the landed ship */
            if (physics_overlap(&player->body, &ship->body)) {
                /* Board the ship: deactivate the player, start takeoff */
                level->exit_ship_boarded = true;
                player->active = false;

                spacecraft_takeoff(ship);
                printf("Player boarded exit ship\n");
            }
        }
    }
}

/* Direct exit zone overlap — fallback for levels without spacecraft exit. */
static void check_exit_zones(Level *level) {
    if (level->exit_triggered) return;
    if (level->exit_ship_spawned) return; /* ship handles the exit instead */
    if (level->map.exit_zone_count == 0) return;

    Entity *player = find_player(&level->entities);
    if (!player) return;

    for (int i = 0; i < level->map.exit_zone_count; i++) {
        const ExitZone *ez = &level->map.exit_zones[i];
        if (physics_aabb_overlap(
                player->body.pos, player->body.size,
                vec2(ez->x, ez->y), vec2(ez->w, ez->h))) {
            level->exit_triggered = true;
            snprintf(level->exit_target, sizeof(level->exit_target),
                     "%s", ez->target);
            printf("Exit zone triggered -> %s\n",
                   ez->target[0] ? ez->target : "(victory)");
            return;
        }
    }
}

bool level_exit_triggered(const Level *level) {
    return level->exit_triggered;
}

void level_update(Level *level, float dt) {
    /* Don't update if exit already triggered (transition pending) */
    if (level->exit_triggered) return;

    /* Start music on first update (deferred so browser audio context
     * is unlocked by the first user interaction / keypress) */
    if (!level->music_started && level->music.music) {
        audio_set_music_volume(64);
        audio_play_music(level->music, true);
        level->music_started = true;
    }

    /* Set camera pointer for collision shake triggers */
    s_active_camera = &level->camera;

    /* ── Deferred player spawn: wait for spacecraft to land ── */
    if (level->has_intro_ship && !level->player_spawned) {
        /* Find the spacecraft and check if it has landed */
        for (int i = 0; i < level->entities.count; i++) {
            Entity *e = &level->entities.entities[i];
            if (e->active && e->type == ENT_SPACECRAFT &&
                !spacecraft_is_exit_ship(e) &&
                spacecraft_is_landed(e)) {
                /* Intro ship has landed — spawn the player at spawn point */
                Entity *player = player_spawn(&level->entities,
                                              level->map.player_spawn);
                if (player) {
                    if (level->map.player_unarmed) {
                        PlayerData *ppd = (PlayerData *)player->data;
                        if (ppd) ppd->has_gun = false;
                    }
                    level->player_spawned = true;

                    /* Trigger takeoff now that the player has exited */
                    spacecraft_takeoff(e);
                }
                break;
            }
        }
    }

    /* Update all entities */
    entity_update_all(&level->entities, dt, &level->map, &level->camera);

    /* Handle collisions (only meaningful once player exists) */
    if (level->player_spawned) {
        handle_collisions(&level->entities);

        /* Exit ship: proximity trigger + boarding/departure */
        check_exit_ship_trigger(level);
        update_exit_ship(level);

        /* Fallback direct exit zone check (for levels without ship exit) */
        check_exit_zones(level);

        /* Check for player respawn (skip if player boarded exit ship) */
        if (!level->exit_ship_boarded) {
            for (int i = 0; i < level->entities.count; i++) {
                Entity *e = &level->entities.entities[i];
                if (e->active && e->type == ENT_PLAYER && player_wants_respawn(e)) {
                    player_respawn(e, level->map.player_spawn);
                    Vec2 center = vec2(
                        e->body.pos.x + e->body.size.x * 0.5f,
                        e->body.pos.y + e->body.size.y * 0.5f
                    );
                    camera_follow(&level->camera, center, vec2_zero(), dt);
                    break;
                }
            }
        }
    }

    /* Update particles */
    particle_update(dt);

    /* Camera tracking:
     *   1. Exit ship boarded → hold camera still (ship flies out of frame)
     *   2. Player spawned → follow the player
     *   3. Intro ship flying in → follow the spacecraft */
    bool cam_tracked = false;

    if (level->exit_ship_boarded) {
        /* Camera stays put — the ship flies out of frame on its own */
        cam_tracked = true;
    }

    if (!cam_tracked && level->player_spawned) {
        for (int i = 0; i < level->entities.count; i++) {
            Entity *e = &level->entities.entities[i];
            if (e->active && e->type == ENT_PLAYER) {
                float look_offset = player_get_look_up_offset(e);
                Vec2 center = vec2(
                    e->body.pos.x + e->body.size.x * 0.5f,
                    e->body.pos.y + e->body.size.y * 0.5f + look_offset
                );
                camera_follow(&level->camera, center, e->body.vel, dt);
                cam_tracked = true;
                break;
            }
        }
    }

    if (!cam_tracked) {
        /* Follow the spacecraft during intro */
        for (int i = 0; i < level->entities.count; i++) {
            Entity *e = &level->entities.entities[i];
            if (e->active && e->type == ENT_SPACECRAFT &&
                !spacecraft_is_exit_ship(e)) {
                Vec2 center = vec2(
                    e->body.pos.x + e->body.size.x * 0.5f,
                    e->body.pos.y + e->body.size.y * 0.5f
                );
                camera_follow(&level->camera, center, vec2_zero(), dt);
                cam_tracked = true;
                break;
            }
        }
    }

    /* Set audio listener to camera center */
    if (cam_tracked) {
        Vec2 listener = vec2(
            level->camera.pos.x + level->camera.viewport.x * 0.5f,
            level->camera.pos.y + level->camera.viewport.y * 0.5f
        );
        audio_set_listener(listener);
    }

    /* Update screen shake */
    camera_update_shake(&level->camera, dt);
}

void level_render(Level *level, float interpolation) {
    (void)interpolation; /* TODO: use for render interpolation */

    Camera *cam = &level->camera;

    /* Render parallax backgrounds (behind everything) */
    parallax_render(&level->parallax, cam, g_engine.renderer);

    /* Render tile layers */
    tilemap_render_layer(&level->map, level->map.bg_layer,
                         cam, g_engine.renderer);
    tilemap_render_layer(&level->map, level->map.collision_layer,
                         cam, g_engine.renderer);

    /* Render exit zones (pulsing glow on ground layer) */
    if (level->map.exit_zone_count > 0) {
        /* Pulse alpha between 40 and 100 using a sine wave */
        static float s_exit_pulse = 0.0f;
        s_exit_pulse += 3.0f * DT;  /* ~3 Hz pulse */
        float pulse = 0.5f + 0.5f * sinf(s_exit_pulse);
        uint8_t alpha = (uint8_t)(40.0f + pulse * 60.0f);

        SDL_Renderer *r = g_engine.renderer;
        SDL_SetRenderDrawBlendMode(r, SDL_BLENDMODE_BLEND);

        for (int i = 0; i < level->map.exit_zone_count; i++) {
            const ExitZone *ez = &level->map.exit_zones[i];
            Vec2 screen_pos = camera_world_to_screen(cam, vec2(ez->x, ez->y));
            float zoom = cam->zoom > 0.0f ? cam->zoom : 1.0f;

            SDL_Rect rect = {
                (int)screen_pos.x,
                (int)screen_pos.y,
                (int)(ez->w * zoom + 0.5f),
                (int)(ez->h * zoom + 0.5f)
            };

            /* Green/cyan fill */
            SDL_SetRenderDrawColor(r, 50, 230, 180, alpha);
            SDL_RenderFillRect(r, &rect);

            /* Brighter border */
            SDL_SetRenderDrawColor(r, 80, 255, 200, (uint8_t)(alpha + 40));
            SDL_RenderDrawRect(r, &rect);
        }
    }

    /* Render entities */
    entity_render_all(&level->entities, cam);

    /* Render particles (between entities and foreground) */
    particle_render(cam);

    /* Render foreground tiles */
    tilemap_render_layer(&level->map, level->map.fg_layer,
                         cam, g_engine.renderer);

    /* Render HUD - health display */
    Entity *player = NULL;
    for (int i = 0; i < level->entities.count; i++) {
        Entity *e = &level->entities.entities[i];
        if (e->active && e->type == ENT_PLAYER) {
            player = e;
            break;
        }
    }

    if (player) {
        /* Draw health hearts */
        for (int i = 0; i < player->max_health; i++) {
            SDL_Color heart_color;
            if (i < player->health) {
                heart_color = (SDL_Color){220, 50, 50, 255};   /* red = full */
            } else {
                heart_color = (SDL_Color){80, 80, 80, 255};    /* grey = empty */
            }
            Vec2 pos = vec2(8.0f + i * 14.0f, 8.0f);
            Vec2 size = vec2(10.0f, 10.0f);
            renderer_draw_rect(pos, size, heart_color, LAYER_HUD, cam);
        }

        /* Draw jetpack charge indicators */
        int charges, max_charges;
        float recharge_pct;
        bool boosted = false;
        if (player_get_dash_charges(player, &charges, &max_charges,
                                    &recharge_pct, &boosted)) {
            /* Blue when boosted, orange normally */
            SDL_Color full_color    = boosted
                ? (SDL_Color){50, 150, 255, 255}
                : (SDL_Color){255, 180, 50, 255};
            SDL_Color partial_color = boosted
                ? (SDL_Color){40, 120, 200, 180}
                : (SDL_Color){200, 140, 40, 180};

            for (int i = 0; i < max_charges; i++) {
                float bx = 8.0f + i * 10.0f;
                float by = 22.0f;
                float bw = 7.0f;
                float bh = 5.0f;

                /* Background (empty slot) */
                renderer_draw_rect(vec2(bx, by), vec2(bw, bh),
                    (SDL_Color){50, 50, 60, 255}, LAYER_HUD, cam);

                if (i < charges) {
                    /* Full charge */
                    renderer_draw_rect(vec2(bx, by), vec2(bw, bh),
                        full_color, LAYER_HUD, cam);
                } else if (i == charges) {
                    /* Currently recharging — partial fill */
                    float fill = recharge_pct * bw;
                    if (fill > 0.5f) {
                        renderer_draw_rect(vec2(bx, by), vec2(fill, bh),
                            partial_color, LAYER_HUD, cam);
                    }
                }
            }
        }
    }

    /* Flush the renderer */
    renderer_flush(cam);
}

void level_free(Level *level) {
    audio_stop_music();

    /* Free music handle (prevent leak on reload) */
    audio_free_music(&level->music);
    level->music_started = false;

    entity_manager_clear(&level->entities);
    particle_clear();
    parallax_free(&level->parallax);
    tilemap_free(&level->map);

    /* Free spritesheet */
    if (g_spritesheet) {
        SDL_DestroyTexture(g_spritesheet);
        g_spritesheet = NULL;
    }
}
