use crate::{
    GameState,
    cash::{Cash, CashCollectEvent},
    character::HedzCharacter,
};
use avian3d::prelude::*;
use bevy::{
    input::common_conditions::input_just_pressed,
    prelude::*,
    window::{CursorGrabMode, PrimaryWindow},
};
use serde::{Deserialize, Serialize};

#[derive(Component, Default, Serialize, Deserialize, PartialEq)]
#[require(HedzCharacter)]
pub struct Player;

#[derive(Component, Default, Serialize, Deserialize, PartialEq)]
#[require(Transform, Visibility)]
pub struct PlayerBodyMesh;

pub fn plugin(app: &mut App) {
    app.add_systems(Startup, (toggle_cursor_system, cursor_recenter));
    app.add_systems(
        Update,
        (
            collect_cash,
            setup_animations_marker_for_player,
            toggle_cursor_system.run_if(input_just_pressed(KeyCode::Escape)),
        )
            .run_if(in_state(GameState::Playing)),
    );
}

fn cursor_recenter(q_windows: Single<&mut Window, With<PrimaryWindow>>) {
    let mut primary_window = q_windows;
    let center = Vec2::new(
        primary_window.resolution.width() / 2.,
        primary_window.resolution.height() / 2.,
    );
    primary_window.set_cursor_position(Some(center));
}

fn toggle_grab_cursor(window: &mut Window) {
    match window.cursor_options.grab_mode {
        CursorGrabMode::None => {
            window.cursor_options.grab_mode = CursorGrabMode::Confined;
            window.cursor_options.visible = false;
        }
        _ => {
            window.cursor_options.grab_mode = CursorGrabMode::None;
            window.cursor_options.visible = true;
        }
    }
}

fn toggle_cursor_system(mut window: Single<&mut Window, With<PrimaryWindow>>) {
    toggle_grab_cursor(&mut window);
}

fn collect_cash(
    mut commands: Commands,
    mut collision_event_reader: EventReader<CollisionStarted>,
    query_player: Query<&Player>,
    query_cash: Query<&Cash>,
) {
    for CollisionStarted(e1, e2) in collision_event_reader.read() {
        let collect = if query_player.contains(*e1) && query_cash.contains(*e2) {
            Some(*e2)
        } else if query_player.contains(*e2) && query_cash.contains(*e1) {
            Some(*e1)
        } else {
            None
        };

        if let Some(cash) = collect {
            commands.trigger(CashCollectEvent);
            commands.entity(cash).despawn();
        }
    }
}

fn setup_animations_marker_for_player(
    mut commands: Commands,
    animation_handles: Query<Entity, Added<AnimationGraphHandle>>,
    child_of: Query<&ChildOf>,
    player_rig: Query<&ChildOf, With<PlayerBodyMesh>>,
) {
    for animation_rig in animation_handles.iter() {
        for ancestor in child_of.iter_ancestors(animation_rig) {
            if let Ok(rig_child_of) = player_rig.get(ancestor) {
                commands.entity(rig_child_of.parent());
                return;
            }
        }
    }
}
