use crate::entity::Entity;
use crate::math::{Vec2, Vec3, Matrix4x4, Ray, RaycastResult};
use crate::aim::{AimConfig, AimTarget, AimBone};
use crate::bullet_tracer::BulletTracer;
use std::f32::consts::PI;
use serde::{Serialize, Deserialize};

#[derive(Clone, Debug, Default)]
pub struct AutoAim {
    pub config: AimConfig,
    pub bullet_tracer: BulletTracer,
    pub current_target: Option<u32>,
    pub last_aim_time: f32,
}

impl AutoAim {
    pub fn new(config: AimConfig) -> Self {
        Self {
            config: config.clone(),
            bullet_tracer: BulletTracer::from_config(&config),
            current_target: None,
            last_aim_time: 0.0,
        }
    }

    fn get_aim_bone_pos(&self, entity: &Entity) -> Vec3 {
        match self.config.aim_bone {
            AimBone::Head => entity.head_pos,
            AimBone::Neck => entity.neck_pos,
            AimBone::Chest => entity.chest_pos,
            AimBone::Pelvis => entity.pelvis_pos,
            AimBone::ShoulderLeft => entity.shoulder_left,
            AimBone::ShoulderRight => entity.shoulder_right,
            AimBone::ElbowLeft => entity.elbow_left,
            AimBone::ElbowRight => entity.elbow_right,
            AimBone::WristLeft => entity.wrist_left,
            AimBone::WristRight => entity.wrist_right,
            AimBone::HipLeft => entity.hip_left,
            AimBone::HipRight => entity.hip_right,
            AimBone::KneeLeft => entity.knee_left,
            AimBone::KneeRight => entity.knee_right,
            AimBone::AnkleLeft => entity.ankle_left,
            AimBone::AnkleRight => entity.ankle_right,
            AimBone::FootLeft => entity.foot_left,
            AimBone::FootRight => entity.foot_right,
        }
    }

    fn calculate_fov(&self, local_angles: Vec2, target_angles: Vec2) -> f32 {
        let yaw_delta = (target_angles.x - local_angles.x).abs();
        let pitch_delta = (target_angles.y - local_angles.y).abs();
        (yaw_delta.powi(2) + pitch_delta.powi(2)).sqrt()
    }

    fn world_to_screen(&self, pos: Vec3, view_matrix: &Matrix4x4, screen_width: f32, screen_height: f32) -> Option<Vec2> {
        let clip_pos = view_matrix.multiply_point(pos);
        if clip_pos.w < 0.1 {
            return None;
        }
        let ndc_pos = Vec3::new(
            clip_pos.x / clip_pos.w,
            clip_pos.y / clip_pos.w,
            clip_pos.z / clip_pos.w,
        );
        Some(Vec2::new(
            (ndc_pos.x + 1.0) * 0.5 * screen_width,
            (1.0 - ndc_pos.y) * 0.5 * screen_height,
        ))
    }

    fn is_target_valid(&self, local_entity: &Entity, target: &Entity) -> bool {
        if !target.is_alive() || target.team == local_entity.team && self.config.team_check {
            return false;
        }
        let distance = (target.pos - local_entity.pos).length();
        distance <= self.config.max_distance
    }

    fn generate_aim_targets(&self, local_entity: &Entity, entities: &[Entity], view_matrix: &Matrix4x4, screen_width: f32, screen_height: f32) -> Vec<AimTarget> {
        let mut targets = Vec::new();
        for entity in entities {
            if !self.is_target_valid(local_entity, entity) {
                continue;
            }
            let bone_pos = self.get_aim_bone_pos(entity);
            let screen_pos = match self.world_to_screen(bone_pos, view_matrix, screen_width, screen_height) {
                Some(pos) => pos,
                None => continue,
            };
            let delta = Vec2::new(screen_width / 2.0, screen_height / 2.0) - screen_pos;
            let fov = delta.length() / (screen_width / 2.0) * 90.0;
            if fov > self.config.fov_radius {
                continue;
            }
            let distance = (bone_pos - local_entity.pos).length();
            let visible = self.bullet_tracer.raycast(local_entity.pos, bone_pos).map_or(false, |r| r.entity_id == entity.id);
            targets.push(AimTarget {
                entity_id: entity.id,
                head_pos: entity.head_pos,
                body_pos: bone_pos,
                distance,
                fov,
                priority: self.calculate_priority(entity),
                visible,
                last_seen_time: self.last_aim_time,
                prediction_ticks: 0,
            });
        }
        targets
    }

    fn calculate_priority(&self, entity: &Entity) -> f32 {
        match self.config.aim_priority_mode {
            AimPriorityMode::Distance => 1.0 / entity.pos.length(),
            AimPriorityMode::ThreatLevel => entity.weapon_damage as f32,
            AimPriorityMode::VisibleOnly => if entity.visible { 1.0 } else { 0.0 },
            AimPriorityMode::ClosestToCrosshair => 1.0,
            AimPriorityMode::HighestScore => entity.score as f32,
            AimPriorityMode::LowestHealth => 1.0 / (entity.health + 1.0),
            AimPriorityMode::MostRecentSpawn => entity.spawn_time,
            AimPriorityMode::Random => rand::random(),
        }
    }

    fn select_best_target(&mut self, targets: &[AimTarget]) -> Option<&AimTarget> {
        targets.iter().max_by(|a, b| {
            a.priority.partial_cmp(&b.priority).unwrap_or(std::cmp::Ordering::Equal)
        })
    }

    fn calculate_aim_angles(&self, local_pos: Vec3, target_pos: Vec3) -> Vec2 {
        let delta = target_pos - local_pos;
        let yaw = delta.x.atan2(delta.z).to_degrees();
        let pitch = (-delta.y).atan2(delta.x.hypot(delta.z)).to_degrees();
        Vec2::new(
            (yaw + 180.0) % 360.0 - 180.0,
            (pitch + 180.0) % 360.0 - 180.0,
        )
    }

    fn smooth_angles(&self, current_angles: Vec2, target_angles: Vec2) -> Vec2 {
        if !self.config.smooth_aim {
            return target_angles;
        }
        let yaw_delta = target_angles.x - current_angles.x;
        let pitch_delta = target_angles.y - current_angles.y;
        Vec2::new(
            current_angles.x + yaw_delta / self.config.smooth_factor,
            current_angles.y + pitch_delta / self.config.smooth_factor,
        )
    }

    fn apply_recoil_compensation(&self, angles: Vec2) -> Vec2 {
        if !self.config.anti_recoil {
            return angles;
        }
        Vec2::new(
            angles.x - self.config.recoil_control * 0.2,
            angles.y - self.config.recoil_control * 0.5,
        )
    }

    pub fn update(&mut self, local_entity: &Entity, entities: &[Entity], view_matrix: &Matrix4x4, screen_width: f32, screen_height: f32, delta_time: f32) -> Option<Vec2> {
        self.last_aim_time += delta_time;
        if !self.config.enabled {
            self.current_target = None;
            return None;
        }
        if self.config.should_disable(local_entity) {
            self.current_target = None;
            return None;
        }
        let targets = self.generate_aim_targets(local_entity, entities, view_matrix, screen_width, screen_height);
        let best_target = self.select_best_target(&targets)?;
        self.current_target = Some(best_target.entity_id);
        let predicted_target = self.bullet_tracer.trace_bullet(local_entity.pos, &entities.iter().find(|e| e.id == best_target.entity_id).unwrap(), &self.config)?;
        let target_pos = predicted_target.head_pos;
        let mut aim_angles = self.calculate_aim_angles(local_entity.pos, target_pos);
        aim_angles = self.smooth_angles(local_entity.angles, aim_angles);
        aim_angles = self.apply_recoil_compensation(aim_angles);
        if self.config.auto_shoot && best_target.visible {
            self.auto_shoot();
        }
        Some(aim_angles)
    }

    fn auto_shoot(&self) {
    }

    pub fn draw_fov(&self, screen_width: f32, screen_height: f32, color: [f32; 4]) {
        if self.config.draw_fov {
            let center = Vec2::new(screen_width / 2.0, screen_height / 2.0);
            let radius = (self.config.fov_radius / 90.0) * (screen_width / 2.0);
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum AimPriorityMode {
    Distance,
    ThreatLevel,
    VisibleOnly,
    ClosestToCrosshair,
    HighestScore,
    LowestHealth,
    MostRecentSpawn,
    Random,
}

impl Entity {
    pub fn is_alive(&self) -> bool {
        self.health > 0.0 && self.state == EntityState::Alive
    }
}

impl Matrix4x4 {
    pub fn multiply_point(&self, pos: Vec3) -> Vec4 {
        Vec4::new(
            pos.x * self.m00 + pos.y * self.m10 + pos.z * self.m20 + self.m30,
            pos.x * self.m01 + pos.y * self.m11 + pos.z * self.m21 + self.m31,
            pos.x * self.m02 + pos.y * self.m12 + pos.z * self.m22 + self.m32,
            pos.x * self.m03 + pos.y * self.m13 + pos.z * self.m23 + self.m33,
        )
    }
}

#[derive(Clone, Debug)]
pub struct Vec4 {
    pub x: f32,
    pub y: f32,
    pub z: f32,
    pub w: f32,
}

impl Vec4 {
    pub fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
        Self { x, y, z, w }
    }
}