#include "hit_detection.h"

#include <algorithm>
#include <cmath>
#include <limits>

namespace tactical_shooter {

HitDetection::HitDetection() {}

void HitDetection::set_entities(const std::vector<Entity *> &entities) {
    entities_ = &entities;
}

HitResult HitDetection::raycast_entity(
    const godot::Vector3 &origin,
    const godot::Vector3 &direction,
    float max_distance,
    uint16_t exclude_id
) const {
    if (!entities_) return HitResult{};

    HitResult best;
    best.distance = max_distance;

    for (Entity *entity : *entities_) {
        if (!entity || !entity->is_alive()) continue;
        if (entity->get_entity_id() == exclude_id) continue;

        // Simple sphere intersection test against entity bounding sphere
        godot::Vector3 entity_pos = entity->get_position();
        // Offset center upward for body collision (not at feet)
        godot::Vector3 body_center = entity_pos;
        body_center.y += kEntityHeight * 0.5f;

        godot::Vector3 oc = origin - body_center;
        float a = direction.dot(direction);
        float b = 2.0f * oc.dot(direction);
        float c = oc.dot(oc) - (kEntityRadius * kEntityRadius);
        float discriminant = b * b - 4.0f * a * c;

        if (discriminant < 0.0f) continue;

        float t1 = (-b - std::sqrt(discriminant)) / (2.0f * a);
        float t2 = (-b + std::sqrt(discriminant)) / (2.0f * a);

        // Use the closest positive intersection
        float t = t1;
        if (t < 0.0f) t = t2;
        if (t < 0.0f || t > best.distance) continue;

        best.hit = true;
        best.entity_id = entity->get_entity_id();
        best.distance = t;
        best.point = origin + direction * t;
        best.normal = (best.point - body_center).normalized();
        best.damage = 0.0f; // filled by process_shot
        best.hitbox_id = classify_hitbox(*entity, best.point);
    }

    return best;
}

HitResult HitDetection::process_shot(
    const godot::Vector3 &origin,
    const godot::Vector3 &direction,
    uint16_t shooter_id,
    const WeaponDamage &weapon
) {
    HitResult hit = raycast_entity(origin, direction, weapon.max_range, shooter_id);

    if (hit.hit) {
        float mult = get_hitbox_multiplier(hit.hitbox_id, weapon);
        hit.damage = weapon.base_damage * mult;
    }

    return hit;
}

float HitDetection::apply_damage(Entity &entity, float damage, float mult) {
    float raw = damage * mult;

    // Armor absorbs a portion
    float armor = entity.get_armor();
    float armor_absorb = std::min(raw * 0.5f, armor);
    armor -= armor_absorb;
    entity.set_armor(armor);

    float health_damage = raw - armor_absorb;
    float new_health = entity.get_health() - health_damage;
    entity.set_health(new_health);

    if (new_health <= 0.0f) {
        entity.kill();
    }

    return health_damage + armor_absorb;
}

std::vector<HitResult> HitDetection::sphere_overlap(
    const godot::Vector3 &center,
    float radius,
    uint16_t exclude_id
) const {
    std::vector<HitResult> results;
    if (!entities_) return results;

    float radius_sq = radius * radius;

    for (Entity *entity : *entities_) {
        if (!entity || !entity->is_alive()) continue;
        if (entity->get_entity_id() == exclude_id) continue;

        godot::Vector3 body_center = entity->get_position();
        body_center.y += kEntityHeight * 0.5f;

        godot::Vector3 diff = center - body_center;
        float dist_sq = diff.dot(diff);

        if (dist_sq <= radius_sq) {
            HitResult hit;
            hit.hit = true;
            hit.entity_id = entity->get_entity_id();
            hit.distance = std::sqrt(dist_sq);
            hit.point = body_center;
            hit.normal = diff.normalized();
            results.push_back(hit);
        }
    }

    return results;
}

float HitDetection::get_hitbox_multiplier(uint8_t hitbox_id, const WeaponDamage &weapon) const {
    switch (hitbox_id) {
        case 1: return weapon.head_multiplier;
        case 2: return weapon.arm_multiplier;
        case 3: return weapon.leg_multiplier;
        default: return weapon.body_multiplier;
    }
}

uint8_t HitDetection::classify_hitbox(
    const Entity &entity,
    const godot::Vector3 &hit_point
) const {
    godot::Vector3 entity_pos = entity.get_position();
    float relative_y = hit_point.y - entity_pos.y;
    float height_ratio = relative_y / kEntityHeight;

    if (height_ratio > 0.85f) return 1; // head
    if (height_ratio > 0.65f) return 2; // arms/upper body
    if (height_ratio > 0.25f) return 0; // body
    return 3; // legs
}

} // namespace tactical_shooter
