use avian3d::{
    math::{Scalar, Vector},
    prelude::Collider,
};
use bevy::prelude::*;
use bevy_dolly::{prelude::Rig, system::Dolly};
use blenvy::{BlueprintInfo, GameWorldTag, HideUntilReady, SpawnBlueprint};

use crate::char_controller::CharacterControllerBundle;

#[derive(Component, PartialEq, Eq, Default, Reflect)]
#[reflect(Component)]
pub struct Player;

#[derive(Component, PartialEq, Eq, Default, Reflect)]
#[reflect(Component)]
pub struct PlayerHead;

#[derive(Component, PartialEq, Eq, Default, Reflect)]
#[reflect(Component)]
pub struct MainCamera;

pub struct SetupPlugin;

impl Plugin for SetupPlugin {
    fn build(&self, app: &mut App) {
        app.register_type::<Player>()
            .register_type::<PlayerHead>()
            .add_systems(Startup, setup)
            .add_systems(
                Update,
                (update_camera_rig, Dolly::<MainCamera>::update_active),
            )
            // TODO: change to component hooks
            .add_systems(Update, setup_player);
    }
}

fn setup(mut commands: Commands) {
    use bevy_dolly::prelude::*;
    commands.spawn((
        MainCamera, // The rig component tag
        Rig::builder() // The rig itself
            .with(Position::new(Vec3::ZERO)) // Start position
            // Adds a driver for rotation
            .with(Rotation::new(Quat::IDENTITY))
            // Interpolation when the translation is updated, also known as smoothing
            .with(Smooth::new_position(0.3))
            // Interpolation when the rotation is updated (updated via the YawPitch driver)
            .with(Smooth::new_rotation(0.3))
            // Moves the camera point out in the Z direction and uses the position as the pivot
            // .with(Arm::new(Vec3::Z * 4.0))
            .build(),
        Camera3dBundle::default(), // The camera which is related via the rig tag
    ));

    // commands.spawn(PointLightBundle {
    //     transform: Transform::from_translation(Vec3::Y * 30.),
    //     point_light: PointLight {
    //         intensity: 20_000_000.0,
    //         shadows_enabled: true,
    //         range: 100.,
    //         ..default()
    //     },
    //     ..default()
    // });

    // commands.spawn((
    //     DirectionalLightBundle {
    //         transform: Transform::from_translation(Vec3::Y * 10.).with_rotation(Quat::from_euler(
    //             EulerRot::XYZ,
    //             3. * PI / 2.,
    //             0.,
    //             0.,
    //         )),
    //         directional_light: DirectionalLight {
    //             shadows_enabled: true,
    //             ..default()
    //         },
    //         ..default()
    //     },
    //     Name::new("Sun"),
    // ));

    // commands.spawn(SceneBundle {
    //     scene: asset_server.load("hotel.glb#Scene0"),
    //     ..Default::default()
    // });
    commands.spawn((
        BlueprintInfo::from_path("levels/World.glb"),
        SpawnBlueprint,
        HideUntilReady,
        GameWorldTag,
    ));
}

fn setup_player(
    mut commands: Commands,
    q_player: Query<Entity, (With<Player>, Without<Collider>)>,
) {
    let Ok(player_entity) = q_player.get_single() else {
        return;
    };

    commands.entity(player_entity).insert(
        CharacterControllerBundle::new(
            Collider::capsule_endpoints(1., Vec3::Y, Vec3::Y * 3.),
            Vector::NEG_Y * 9.81 * 2.0,
        )
        .with_movement(30.0, 0.92, 7.0, (30.0 as Scalar).to_radians()),
    );
}

fn update_camera_rig(
    q_player_head: Query<&GlobalTransform, (With<PlayerHead>, Without<Rig>)>,
    mut rig_q: Query<&mut Rig, Without<PlayerHead>>,
) {
    use bevy_dolly::prelude::*;
    let Ok(player_head_transform) = q_player_head.get_single() else {
        return;
    };
    let (_, rot, pos) = player_head_transform.to_scale_rotation_translation();

    let mut rig = rig_q.single_mut();
    if let Some(d_rot) = rig.try_driver_mut::<Rotation>() {
        d_rot.rotation = rot;
    }
    if let Some(d_pos) = rig.try_driver_mut::<Position>() {
        d_pos.position = pos;
    }
}
