use bevy::input::mouse::MouseButton;
use bevy::prelude::*;
use bevy::window::PrimaryWindow;

use crate::ui::util::cursor_over_ui;

#[derive(Component)]
pub struct MainCamera;

/// Orbit-style camera: rotates around `target` at `distance`, controlled by mouse.
/// Used for inspection scenes like GalaxyCreation where there is no player to follow.
#[derive(Component, Debug, Clone, Copy)]
pub struct OrbitCamera {
    pub target: Vec3,
    pub distance: f32,
    /// Yaw around the Y axis (radians). 0 = camera at +Z looking toward origin.
    pub yaw: f32,
    /// Pitch above the horizontal plane (radians). 0 = horizontal, π/2 = straight down.
    pub pitch: f32,
}

impl Default for OrbitCamera {
    fn default() -> Self {
        Self {
            target: Vec3::ZERO,
            distance: 420.0,
            yaw: 0.0,
            // ~36° above horizontal — roughly matches docs GalaxyScene camera position.
            pitch: 0.625,
        }
    }
}

/// Initial camera spawn. The same entity is reused across states; control systems
/// decide how it moves depending on which state is active.
pub fn spawn_camera(mut commands: Commands) {
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(0.0, 260.0, 360.0).looking_at(Vec3::ZERO, Vec3::Y),
        MainCamera,
        OrbitCamera::default(),
    ));
}

const ORBIT_ROTATE_SENSITIVITY: f32 = 0.005;
const ORBIT_ZOOM_SENSITIVITY: f32 = 0.1;
const ORBIT_MIN_PITCH: f32 = 0.15; // ~9° above horizontal — never fully edge-on
const ORBIT_MAX_PITCH: f32 = 1.4;  // ~80° — never straight down (gimbal safeguard)
const ORBIT_MIN_DISTANCE: f32 = 40.0;
const ORBIT_MAX_DISTANCE: f32 = 1500.0;

/// Left-drag rotates around the orbit target; scroll wheel zooms.
///
/// Drag is suppressed when the cursor is over any UI node — otherwise clicking
/// buttons or panels would also rotate the camera.
pub fn orbit_camera_control(
    mouse_input: Res<ButtonInput<MouseButton>>,
    primary_window: Query<&Window, With<PrimaryWindow>>,
    mut mouse_motion: EventReader<bevy::input::mouse::MouseMotion>,
    mut scroll_events: EventReader<bevy::input::mouse::MouseWheel>,
    mut query: Query<(&mut Transform, &mut OrbitCamera), With<MainCamera>>,
    ui_nodes: Query<(&bevy::ui::ComputedNode, &GlobalTransform)>,
) {
    let Ok((mut transform, mut orbit)) = query.single_mut() else {
        // Drain pending input to avoid stale buildup when there's no camera.
        mouse_motion.clear();
        scroll_events.clear();
        return;
    };

    let cursor_over_ui = primary_window
        .single()
        .ok()
        .map(|w| cursor_over_ui(w, &ui_nodes))
        .unwrap_or(false);

    if mouse_input.pressed(MouseButton::Left) && !cursor_over_ui {
        for event in mouse_motion.read() {
            orbit.yaw -= event.delta.x * ORBIT_ROTATE_SENSITIVITY;
            orbit.pitch -= event.delta.y * ORBIT_ROTATE_SENSITIVITY;
        }
    } else {
        mouse_motion.clear();
    }

    for event in scroll_events.read() {
        // Scroll up (positive y) → decrease distance (zoom in).
        orbit.distance *= 1.0 - event.y * ORBIT_ZOOM_SENSITIVITY;
    }

    orbit.pitch = orbit.pitch.clamp(ORBIT_MIN_PITCH, ORBIT_MAX_PITCH);
    orbit.distance = orbit.distance.clamp(ORBIT_MIN_DISTANCE, ORBIT_MAX_DISTANCE);

    let cos_p = orbit.pitch.cos();
    let pos = orbit.target
        + Vec3::new(
            orbit.distance * cos_p * orbit.yaw.sin(),
            orbit.distance * orbit.pitch.sin(),
            orbit.distance * cos_p * orbit.yaw.cos(),
        );
    *transform = Transform::from_translation(pos).looking_at(orbit.target, Vec3::Y);
}
