#include "game/enemy.h"
#include "game/sprites.h"
#include "game/projectile.h"
#include "engine/physics.h"
#include "engine/renderer.h"
#include <stdlib.h>
#include <math.h>

/* ════════════════════════════════════════════════════
 *  GRUNT - ground patrol enemy
 * ════════════════════════════════════════════════════ */

static EntityManager *s_grunt_em = NULL;

static void grunt_update(Entity *self, float dt, const Tilemap *map) {
    GruntData *gd = (GruntData *)self->data;
    if (!gd) return;

    Body *body = &self->body;

    /* Death sequence */
    if (self->flags & ENTITY_DEAD) {
        animation_set(&self->anim, &anim_grunt_death);
        animation_update(&self->anim, dt);
        gd->death_timer -= dt;
        body->vel.x = 0;
        if (gd->death_timer <= 0) {
            entity_destroy(s_grunt_em, self);
        }
        return;
    }

    /* Patrol: walk in one direction, reverse when hitting a wall
       or about to walk off a ledge */
    body->vel.x = gd->patrol_dir * GRUNT_SPEED;

    /* Set facing direction */
    if (gd->patrol_dir < 0) self->flags |= ENTITY_FACING_LEFT;
    else                     self->flags &= ~ENTITY_FACING_LEFT;

    /* Physics */
    physics_update(body, dt, map);

    /* Turn around on wall collision */
    if (body->on_wall_left || body->on_wall_right) {
        gd->patrol_dir = -gd->patrol_dir;
    }

    /* Turn around at ledge: check if there's ground ahead */
    if (body->on_ground) {
        float check_x = (gd->patrol_dir > 0) ?
            body->pos.x + body->size.x + 2.0f :
            body->pos.x - 2.0f;
        float check_y = body->pos.y + body->size.y + 4.0f;

        int tx = world_to_tile(check_x);
        int ty = world_to_tile(check_y);

        if (!tilemap_is_solid(map, tx, ty)) {
            gd->patrol_dir = -gd->patrol_dir;
        }
    }

    /* Animation */
    if (fabsf(body->vel.x) > 1.0f) {
        animation_set(&self->anim, &anim_grunt_walk);
    } else {
        animation_set(&self->anim, &anim_grunt_idle);
    }
    animation_update(&self->anim, dt);
}

static void grunt_render(Entity *self, const Camera *cam) {
    (void)cam;
    Body *body = &self->body;

    if (g_spritesheet && self->anim.def) {
        SDL_Rect src = animation_current_rect(&self->anim);

        Vec2 render_pos = vec2(
            body->pos.x - 2.0f,
            body->pos.y
        );

        Sprite spr = {
            .texture = g_spritesheet,
            .src     = src,
            .pos     = render_pos,
            .size    = vec2(SPRITE_CELL, SPRITE_CELL),
            .flip_x  = (self->flags & ENTITY_FACING_LEFT) != 0,
            .flip_y  = false,
            .layer   = LAYER_ENTITIES,
            .alpha   = 255,
        };
        renderer_submit(&spr);
    } else {
        SDL_Color color = {200, 60, 60, 255};
        renderer_draw_rect(body->pos, body->size, color, LAYER_ENTITIES, cam);
    }
}

static void grunt_destroy(Entity *self) {
    free(self->data);
    self->data = NULL;
}

void grunt_register(EntityManager *em) {
    entity_register(em, ENT_ENEMY_GRUNT, grunt_update, grunt_render, grunt_destroy);
    s_grunt_em = em;
}

Entity *grunt_spawn(EntityManager *em, Vec2 pos) {
    Entity *e = entity_spawn(em, ENT_ENEMY_GRUNT, pos);
    if (!e) return NULL;

    e->body.size = vec2(GRUNT_WIDTH, GRUNT_HEIGHT);
    e->body.gravity_scale = 1.0f;
    e->health = GRUNT_HEALTH;
    e->max_health = GRUNT_HEALTH;
    e->damage = 1;

    GruntData *gd = calloc(1, sizeof(GruntData));
    gd->patrol_dir = 1.0f;
    gd->death_timer = 0.3f;
    e->data = gd;

    return e;
}


/* ════════════════════════════════════════════════════
 *  FLYER - flying enemy
 * ════════════════════════════════════════════════════ */

static EntityManager *s_flyer_em = NULL;

/* Helper: find the player entity in the manager */
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) return e;
    }
    return NULL;
}

static void flyer_update(Entity *self, float dt, const Tilemap *map) {
    (void)map;  /* flyers don't collide with tiles */
    FlyerData *fd = (FlyerData *)self->data;
    if (!fd) return;

    Body *body = &self->body;

    /* Death sequence */
    if (self->flags & ENTITY_DEAD) {
        animation_set(&self->anim, &anim_flyer_death);
        animation_update(&self->anim, dt);
        /* Fall when dead */
        body->vel.y += physics_get_gravity() * dt;
        if (body->vel.y > MAX_FALL_SPEED) body->vel.y = MAX_FALL_SPEED;
        body->pos.y += body->vel.y * dt;
        fd->death_timer -= dt;
        if (fd->death_timer <= 0) {
            entity_destroy(s_flyer_em, self);
        }
        return;
    }

    /* Bob up and down */
    fd->bob_timer += dt;
    float bob_offset = sinf(fd->bob_timer * FLYER_BOB_SPD) * FLYER_BOB_AMP;
    body->pos.y = fd->base_y + bob_offset;

    /* Chase player if in range */
    Entity *player = find_player(s_flyer_em);
    if (player && player->active && !(player->flags & ENTITY_DEAD)) {
        float px = player->body.pos.x + player->body.size.x * 0.5f;
        float fx = body->pos.x + body->size.x * 0.5f;
        float dist = px - fx;

        if (fabsf(dist) < FLYER_DETECT) {
            /* Move toward player */
            if (dist < -2.0f) {
                body->pos.x -= FLYER_SPEED * dt;
                self->flags |= ENTITY_FACING_LEFT;
            } else if (dist > 2.0f) {
                body->pos.x += FLYER_SPEED * dt;
                self->flags &= ~ENTITY_FACING_LEFT;
            }

            /* Shoot at player */
            fd->shoot_timer -= dt;
            if (fd->shoot_timer <= 0 && s_flyer_em) {
                fd->shoot_timer = FLYER_SHOOT_CD;
                bool facing_left = (self->flags & ENTITY_FACING_LEFT) != 0;
                float bx = facing_left ?
                    body->pos.x - 4.0f :
                    body->pos.x + body->size.x;
                float by = body->pos.y + body->size.y * 0.4f;
                projectile_spawn(s_flyer_em, vec2(bx, by), facing_left, false);
            }
        }
    }

    /* Animation */
    animation_set(&self->anim, &anim_flyer_fly);
    animation_update(&self->anim, dt);
}

static void flyer_render(Entity *self, const Camera *cam) {
    (void)cam;
    Body *body = &self->body;

    if (g_spritesheet && self->anim.def) {
        SDL_Rect src = animation_current_rect(&self->anim);

        Vec2 render_pos = vec2(
            body->pos.x - 1.0f,
            body->pos.y - 2.0f
        );

        Sprite spr = {
            .texture = g_spritesheet,
            .src     = src,
            .pos     = render_pos,
            .size    = vec2(SPRITE_CELL, SPRITE_CELL),
            .flip_x  = (self->flags & ENTITY_FACING_LEFT) != 0,
            .flip_y  = false,
            .layer   = LAYER_ENTITIES,
            .alpha   = 255,
        };
        renderer_submit(&spr);
    } else {
        SDL_Color color = {150, 50, 180, 255};
        renderer_draw_rect(body->pos, body->size, color, LAYER_ENTITIES, cam);
    }
}

static void flyer_destroy(Entity *self) {
    free(self->data);
    self->data = NULL;
}

void flyer_register(EntityManager *em) {
    entity_register(em, ENT_ENEMY_FLYER, flyer_update, flyer_render, flyer_destroy);
    s_flyer_em = em;
}

Entity *flyer_spawn(EntityManager *em, Vec2 pos) {
    Entity *e = entity_spawn(em, ENT_ENEMY_FLYER, pos);
    if (!e) return NULL;

    e->body.size = vec2(FLYER_WIDTH, FLYER_HEIGHT);
    e->body.gravity_scale = 0.0f;  /* flyers don't fall */
    e->health = FLYER_HEALTH;
    e->max_health = FLYER_HEALTH;
    e->damage = 1;

    FlyerData *fd = calloc(1, sizeof(FlyerData));
    fd->base_y = pos.y;
    fd->death_timer = 0.5f;
    fd->shoot_timer = FLYER_SHOOT_CD;
    e->data = fd;

    return e;
}
