use std::fs;
use std::path::Path;
use bincode;

pub fn save_aim_config(config: &AimConfig, path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let serialized = bincode::serialize(config)?;
    fs::write(path, serialized)?;
    Ok(())
}

pub fn load_aim_config(path: &str) -> Result<AimConfig, Box<dyn std::error::Error>> {
    if !Path::new(path).exists() {
        return Ok(AimConfig {
            enabled: false,
            auto_shoot: false,
            smooth_aim: true,
            prediction: true,
            gravity_compensation: false,
            auto_wall: false,
            team_check: true,
            anti_recoil: true,
            triggerbot: false,
            rapid_fire: false,
            auto_scope: false,
            advanced_prediction: false,
            movement_compensation: true,
            smooth_factor: 2.5,
            max_distance: 500.0,
            fov_radius: 15.0,
            recoil_control: 0.8,
            trigger_delay: 10,
            rapid_fire_interval: 50,
            zoom_multiplier: 1.5,
            aim_offset_x: 0.0,
            aim_offset_y: 0.0,
            aim_curve_strength: 1.0,
            aim_jitter_suppression: 0.5,
            aim_prediction_factor: 1.2,
            bullet_velocity: 900.0,
            bullet_gravity: 9.8,
            server_latency: 50.0,
            client_latency: 20.0,
            network_smoothing: 0.3,
            interpolation_factor: 0.1,
            extrapolation_limit: 0.5,
            aim_stickiness: 0.7,
            aim_drift_compensation: 0.2,
            aim_key: 0x01,
            aim_bone: AimBone::Head,
            aim_priority_mode: AimPriorityMode::ClosestToCrosshair,
            aim_visualization: AimVisualization::Crosshair,
            aim_method: AimMethod::LinearInterpolation,
            smoothing_algorithm: SmoothingAlgorithm::Exponential,
            detection_zone: DetectionZone::CrosshairArea,
            draw_fov: true,
            draw_target_info: false,
            aim_sound_alert: false,
            aim_log: false,
            disable_conditions: DisableConditions::ON_GUI | DisableConditions::ON_FLASHBANG,
        });
    }
    let data = fs::read(path)?;
    let config = bincode::deserialize(&data)?;
    Ok(config)
}

pub fn calculate_aim_angles(
    current_pos: Vec3,
    current_angles: Vec2,
    target_pos: Vec3,
    config: &AimConfig,
) -> Vec2 {
    let delta = target_pos - current_pos;
    let distance = delta.length();

    let yaw = delta.x.atan2(delta.z);
    let pitch = (-delta.y).atan2(delta.x.hypot(delta.z));

    let mut yaw = yaw.to_degrees();
    let mut pitch = pitch.to_degrees();

    yaw = (yaw + 180.0) % 360.0 - 180.0;
    pitch = (pitch + 180.0) % 360.0 - 180.0;

    if config.smooth_aim {
        let yaw_delta = yaw - current_angles.x;
        let pitch_delta = pitch - current_angles.y;
        yaw = current_angles.x + yaw_delta / config.smooth_factor;
        pitch = current_angles.y + pitch_delta / config.smooth_factor;
    }

    if config.anti_recoil {
        pitch -= config.recoil_control * 0.5;
        yaw -= config.recoil_control * 0.2;
    }

    Vec2::new(yaw, pitch)
}

pub fn select_best_target(
    targets: &[AimTarget],
    config: &AimConfig,
    local_angles: Vec2,
) -> Option<&AimTarget> {
    targets
        .iter()
        .filter(|t| {
            t.distance <= config.max_distance
                && (t.visible || !config.prediction)
                && t.fov <= config.fov_radius
        })
        .max_by(|a, b| match config.aim_priority_mode {
            AimPriorityMode::Distance => a.distance.partial_cmp(&b.distance).unwrap_or(std::cmp::Ordering::Equal),
            AimPriorityMode::ClosestToCrosshair => a.fov.partial_cmp(&b.fov).unwrap_or(std::cmp::Ordering::Equal),
            AimPriorityMode::LowestHealth => a.priority.partial_cmp(&b.priority).unwrap_or(std::cmp::Ordering::Equal),
            _ => a.priority.partial_cmp(&b.priority).unwrap_or(std::cmp::Ordering::Equal),
        })
}

pub fn execute_aim(
    local_pos: Vec3,
    local_angles: Vec2,
    targets: &[AimTarget],
    config: &AimConfig,
    entity: &Entity,
) -> Option<Vec2> {
    if !config.enabled || config.should_disable(entity) {
        return None;
    }

    let best_target = select_best_target(targets, config, local_angles)?;

    let target_pos = match config.aim_bone {
        AimBone::Head => best_target.head_pos,
        AimBone::Chest => best_target.body_pos,
        _ => best_target.body_pos,
    };

    let aim_angles = calculate_aim_angles(local_pos, local_angles, target_pos, config);

    if config.auto_shoot {
    }

    Some(aim_angles)
}