#include "game/laser_turret.h"
#include "game/player.h"
#include "game/sprites.h"
#include "engine/core.h"
#include "engine/physics.h"
#include "engine/renderer.h"
#include "engine/particle.h"
#include "engine/camera.h"
#include <stdlib.h>
#include <math.h>

/* ═══════════════════════════════════════════════════
 *  Laser Turret
 *
 *  Cycles through four states:
 *    IDLE (brief) -> CHARGING (1.5s, thin flickering
 *    indicator line) -> FIRING (2.5s, thick damaging
 *    beam) -> COOLDOWN (2s, beam fades) -> IDLE ...
 *
 *  The beam is a raycast from the turret center along
 *  aim_angle until a solid tile or the map edge.
 *
 *  Fixed variant: aim_angle set at spawn, never moves.
 *  Tracking variant: aim_angle rotates toward the
 *  player during IDLE and CHARGING, then locks when
 *  FIRING begins.
 * ═══════════════════════════════════════════════════ */

static EntityManager *s_laser_em = NULL;

/* ── Helpers ──────────────────────────────────────── */

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;
}

/* Raycast from (ox, oy) along (dx, dy) in pixel steps.
 * Returns the world-space endpoint where the beam hits
 * a solid tile or the map boundary. Ignores the turret's
 * own tile (skip_tx, skip_ty) so wall-mounted turrets can
 * shoot outward. Max range prevents runaway. */
static void beam_raycast(const Tilemap *map,
                         float ox, float oy, float dx, float dy,
                         int skip_tx, int skip_ty,
                         float *out_x, float *out_y) {
    float max_range = 800.0f; /* pixels */
    float step = 1.0f;
    float x = ox, y = oy;
    float dist = 0.0f;

    while (dist < max_range) {
        x += dx * step;
        y += dy * step;
        dist += step;

        int tx = (int)(x / TILE_SIZE);
        int ty = (int)(y / TILE_SIZE);
        if (tx < 0 || ty < 0 || tx >= map->width || ty >= map->height) break;
        if (tx == skip_tx && ty == skip_ty) continue;
        if (tilemap_is_solid(map, tx, ty)) break;
    }

    *out_x = x;
    *out_y = y;
}

/* ── Update ──────────────────────────────────────── */

static void laser_turret_update(Entity *self, float dt, const Tilemap *map) {
    LaserTurretData *ld = (LaserTurretData *)self->data;
    if (!ld) return;

    /* Death sequence */
    if (self->flags & ENTITY_DEAD) {
        self->timer -= dt;
        if (self->timer <= 0) {
            particle_emit_death_puff(self->body.pos, (SDL_Color){255, 60, 60, 255});
            entity_destroy(s_laser_em, self);
        }
        return;
    }

    /* Turret center for raycasting and aiming */
    float cx = self->body.pos.x + self->body.size.x * 0.5f;
    float cy = self->body.pos.y + self->body.size.y * 0.5f;

    /* Tracking: rotate toward player during IDLE and CHARGING */
    if (ld->tracking && (ld->state == LASER_IDLE || ld->state == LASER_CHARGING)) {
        Entity *player = find_player(s_laser_em);
        if (player && player->active && !(player->flags & ENTITY_DEAD)) {
            float px = player->body.pos.x + player->body.size.x * 0.5f;
            float py = player->body.pos.y + player->body.size.y * 0.5f;
            float target_angle = atan2f(py - cy, px - cx);

            /* Rotate toward target at limited speed */
            float diff = target_angle - ld->aim_angle;
            /* Normalize to [-PI, PI] */
            while (diff > 3.14159f)  diff -= 6.28318f;
            while (diff < -3.14159f) diff += 6.28318f;

            float max_rot = LASER_TRACK_SPEED * dt;
            if (diff > max_rot)       ld->aim_angle += max_rot;
            else if (diff < -max_rot) ld->aim_angle -= max_rot;
            else                      ld->aim_angle = target_angle;
        }
    }

    /* Update facing direction from aim */
    float dx = cosf(ld->aim_angle);
    if (dx < 0) self->flags |= ENTITY_FACING_LEFT;
    else         self->flags &= ~ENTITY_FACING_LEFT;

    /* Compute beam endpoint via raycast (skip turret's own tile) */
    float dir_x = cosf(ld->aim_angle);
    float dir_y = sinf(ld->aim_angle);
    int self_tx = (int)(cx / TILE_SIZE);
    int self_ty = (int)(cy / TILE_SIZE);
    beam_raycast(map, cx, cy, dir_x, dir_y, self_tx, self_ty,
                 &ld->beam_end_x, &ld->beam_end_y);

    /* Damage cooldown */
    if (ld->damage_cd > 0) ld->damage_cd -= dt;

    /* State machine */
    ld->timer -= dt;
    if (ld->timer <= 0) {
        switch (ld->state) {
            case LASER_IDLE:
                ld->state = LASER_CHARGING;
                ld->timer = LASER_CHARGE_TIME;
                break;
            case LASER_CHARGING:
                ld->state = LASER_FIRING;
                ld->timer = LASER_FIRE_TIME;
                ld->damage_cd = 0;
                break;
            case LASER_FIRING:
                ld->state = LASER_COOLDOWN;
                ld->timer = LASER_COOLDOWN_TIME;
                break;
            case LASER_COOLDOWN:
                ld->state = LASER_IDLE;
                ld->timer = 0.3f; /* brief idle before next cycle */
                break;
        }
    }

    /* During FIRING: check beam-player overlap and deal damage. */
    if (ld->state == LASER_FIRING && ld->damage_cd <= 0) {
        Entity *player = find_player(s_laser_em);
        if (player && player->active && !(player->flags & ENTITY_DEAD)) {
            /* Test player AABB against beam line (approximated as thin rect). */
            float bx0 = cx, by0 = cy;
            float bx1 = ld->beam_end_x, by1 = ld->beam_end_y;
            float beam_half_w = 3.0f; /* beam thickness for collision */

            /* Build an AABB enclosing the beam segment */
            float min_x = (bx0 < bx1 ? bx0 : bx1) - beam_half_w;
            float min_y = (by0 < by1 ? by0 : by1) - beam_half_w;
            float max_x = (bx0 > bx1 ? bx0 : bx1) + beam_half_w;
            float max_y = (by0 > by1 ? by0 : by1) + beam_half_w;

            /* Quick AABB pre-filter */
            Body *pb = &player->body;
            if (pb->pos.x + pb->size.x > min_x && pb->pos.x < max_x &&
                pb->pos.y + pb->size.y > min_y && pb->pos.y < max_y) {
                /* Finer check: point-to-line distance for player center. */
                float pcx = pb->pos.x + pb->size.x * 0.5f;
                float pcy = pb->pos.y + pb->size.y * 0.5f;
                float lx = bx1 - bx0, ly = by1 - by0;
                float len_sq = lx * lx + ly * ly;
                float dist_to_beam;
                if (len_sq < 0.01f) {
                    dist_to_beam = sqrtf((pcx - bx0) * (pcx - bx0) +
                                         (pcy - by0) * (pcy - by0));
                } else {
                    float t = ((pcx - bx0) * lx + (pcy - by0) * ly) / len_sq;
                    if (t < 0) t = 0;
                    if (t > 1) t = 1;
                    float closest_x = bx0 + t * lx;
                    float closest_y = by0 + t * ly;
                    dist_to_beam = sqrtf((pcx - closest_x) * (pcx - closest_x) +
                                         (pcy - closest_y) * (pcy - closest_y));
                }

                /* Hit if player center is within half-size of beam */
                float hit_radius = (pb->size.x + pb->size.y) * 0.25f + beam_half_w;
                if (dist_to_beam < hit_radius) {
                    player->health -= LASER_DAMAGE;
                    ld->damage_cd = LASER_DAMAGE_CD;
                    particle_emit_spark(vec2(pcx, pcy), (SDL_Color){255, 80, 40, 255});
                }
            }
        }
    }

    /* Sparks at beam endpoint during FIRING */
    if (ld->state == LASER_FIRING) {
        if (rand() % 4 == 0) {
            particle_emit_spark(vec2(ld->beam_end_x, ld->beam_end_y),
                                (SDL_Color){255, 100, 50, 255});
        }
    }

    /* Animation */
    if (ld->state == LASER_CHARGING || ld->state == LASER_FIRING) {
        animation_set(&self->anim, &anim_laser_turret_fire);
    } else {
        animation_set(&self->anim, &anim_laser_turret_idle);
    }
    animation_update(&self->anim, dt);
}

/* ── Render ──────────────────────────────────────── */

/* Draw the beam line from turret center to endpoint.
 * Uses raw SDL calls since renderer_draw_rect only handles
 * axis-aligned rectangles. */
static void draw_beam_line(const Camera *cam,
                           float x0, float y0, float x1, float y1,
                           int thickness, SDL_Color col) {
    Vec2 s0 = camera_world_to_screen(cam, vec2(x0, y0));
    Vec2 s1 = camera_world_to_screen(cam, vec2(x1, y1));

    SDL_SetRenderDrawBlendMode(g_engine.renderer, SDL_BLENDMODE_BLEND);
    SDL_SetRenderDrawColor(g_engine.renderer, col.r, col.g, col.b, col.a);

    /* Draw multiple lines offset perpendicular to simulate thickness. */
    float dx = s1.x - s0.x;
    float dy = s1.y - s0.y;
    float len = sqrtf(dx * dx + dy * dy);
    if (len < 0.5f) return;

    /* Perpendicular direction */
    float nx = -dy / len;
    float ny =  dx / len;

    int half = thickness / 2;
    for (int i = -half; i <= half; i++) {
        float ox = nx * (float)i;
        float oy = ny * (float)i;
        SDL_RenderDrawLine(g_engine.renderer,
                           (int)(s0.x + ox), (int)(s0.y + oy),
                           (int)(s1.x + ox), (int)(s1.y + oy));
    }

    SDL_SetRenderDrawBlendMode(g_engine.renderer, SDL_BLENDMODE_NONE);
}

static void laser_turret_render(Entity *self, const Camera *cam) {
    LaserTurretData *ld = (LaserTurretData *)self->data;
    if (!ld) return;

    Body *body = &self->body;
    float cx = body->pos.x + body->size.x * 0.5f;
    float cy = body->pos.y + body->size.y * 0.5f;

    /* Draw beam based on state */
    if (ld->state == LASER_CHARGING) {
        /* Thin flickering indicator line */
        float progress = 1.0f - (ld->timer / LASER_CHARGE_TIME);
        uint8_t alpha = (uint8_t)(80 + 100 * progress);
        /* Flicker effect: modulate alpha with a fast sine */
        float flicker = sinf(ld->timer * 20.0f) * 0.3f + 0.7f;
        alpha = (uint8_t)((float)alpha * flicker);

        SDL_Color beam_col = {255, 60, 40, alpha};
        draw_beam_line(cam, cx, cy, ld->beam_end_x, ld->beam_end_y,
                       1, beam_col);

    } else if (ld->state == LASER_FIRING) {
        /* Bright thick beam — core + glow */
        SDL_Color glow = {255, 80, 30, 100};
        draw_beam_line(cam, cx, cy, ld->beam_end_x, ld->beam_end_y,
                       5, glow);

        SDL_Color core = {255, 200, 150, 230};
        draw_beam_line(cam, cx, cy, ld->beam_end_x, ld->beam_end_y,
                       2, core);

        SDL_Color center = {255, 255, 220, 255};
        draw_beam_line(cam, cx, cy, ld->beam_end_x, ld->beam_end_y,
                       0, center);

    } else if (ld->state == LASER_COOLDOWN) {
        /* Fading beam */
        float fade = ld->timer / LASER_COOLDOWN_TIME;
        uint8_t alpha = (uint8_t)(180 * fade);
        SDL_Color beam_col = {255, 80, 30, alpha};
        draw_beam_line(cam, cx, cy, ld->beam_end_x, ld->beam_end_y,
                       (int)(3.0f * fade), beam_col);
    }

    /* Draw turret body */
    if (g_spritesheet && self->anim.def) {
        SDL_Rect src = animation_current_rect(&self->anim);

        /* Center the 16x16 sprite over the 14x14 hitbox */
        Vec2 render_pos = vec2(
            body->pos.x - 1.0f,
            body->pos.y - 1.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 {
        /* Colored rect fallback */
        SDL_Color base_col;
        if (ld->state == LASER_FIRING) {
            base_col = (SDL_Color){200, 80, 50, 255};
        } else if (ld->state == LASER_CHARGING) {
            base_col = (SDL_Color){180, 100, 60, 255};
        } else {
            base_col = (SDL_Color){140, 100, 80, 255};
        }
        renderer_draw_rect(body->pos, body->size, base_col, LAYER_ENTITIES, cam);

        /* Small dot indicating aim direction (only needed for fallback) */
        float dot_dist = 10.0f;
        float dot_x = cx + cosf(ld->aim_angle) * dot_dist - 1.0f;
        float dot_y = cy + sinf(ld->aim_angle) * dot_dist - 1.0f;
        SDL_Color dot_col = {255, 50, 30, 255};
        renderer_draw_rect(vec2(dot_x, dot_y), vec2(2, 2), dot_col,
                           LAYER_ENTITIES, cam);
    }
}

/* ── Destroy ─────────────────────────────────────── */

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

/* ── Register ────────────────────────────────────── */

void laser_turret_register(EntityManager *em) {
    entity_register(em, ENT_LASER_TURRET,
                    laser_turret_update, laser_turret_render,
                    laser_turret_destroy);
    s_laser_em = em;
}

/* ── Spawn ───────────────────────────────────────── */

static Entity *laser_spawn_internal(EntityManager *em, Vec2 pos, bool tracking) {
    Entity *e = entity_spawn(em, ENT_LASER_TURRET, pos);
    if (!e) return NULL;

    e->body.size = vec2(LASER_WIDTH, LASER_HEIGHT);
    e->body.gravity_scale = 0.0f;
    e->health = LASER_HEALTH;
    e->max_health = LASER_HEALTH;
    e->damage = 0; /* damage comes from the beam, not contact */
    e->timer = 0.3f;

    LaserTurretData *ld = calloc(1, sizeof(LaserTurretData));
    if (!ld) { entity_destroy(em, e); return NULL; }
    ld->state = LASER_IDLE;
    ld->timer = 0.5f; /* brief idle before first charge */
    ld->aim_angle = 3.14159f; /* default: aim left */
    ld->damage_cd = 0;
    ld->tracking = tracking;
    e->data = ld;

    return e;
}

Entity *laser_turret_spawn(EntityManager *em, Vec2 pos) {
    return laser_spawn_internal(em, pos, false);
}

Entity *laser_turret_spawn_tracking(EntityManager *em, Vec2 pos) {
    return laser_spawn_internal(em, pos, true);
}
