use bevy::prelude::*;
use bevy_rapier3d::prelude::ColliderDisabled;

use crate::{
    player::{Player, PlayerAction, toolbar::Item},
    util::single_mut,
};

use super::{Interact, ui::InteractionOpportunity};
#[derive(Component, Clone, Copy)]
pub struct PickUpAble;

#[derive(Component, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct KeyCardId(pub usize);

#[derive(Component, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct OpenedByKey(pub KeyCardId);

#[derive(Resource, Default, Clone, Copy)]
pub struct WrongKey(pub bool);

impl OpenedByKey {
    pub fn can_be_opened_by(&self, card: KeyCardId) -> bool {
        self.0 == card
    }
}

pub fn plugin(app: &mut App) {
    app.init_resource::<WrongKey>();
}

pub fn handle_pick_up(
    mut commands: Commands,
    // current action
    mut player_query: Query<(&PlayerAction, &Transform, &mut Item), With<Player>>,
    mut vis_query: Query<&mut Visibility, With<PickUpAble>>,
    children: Query<&mut Children>,

    mut item_transform: Query<&mut Transform, (With<Interact>, Without<Player>, With<PickUpAble>)>,
    // current interactable
    mut interaction_opportunity: ResMut<InteractionOpportunity>,
) {
    let (PlayerAction::PickUp, transform, mut item) = single_mut!(player_query) else {
        return;
    };
    // take out the interaction, because we are picking it up
    let Some(target) = interaction_opportunity.0.take() else {
        return;
    };
    // check if it is pickable and set visibility
    let Ok(mut vis) = vis_query.get_mut(target) else {
        return;
    };
    *vis = Visibility::Hidden;

    // change colliders
    if let Ok(colliders) = children.get(target) {
        for &collider in colliders {
            commands.entity(collider).insert(ColliderDisabled);
        }
    }
    let replaced = item.set_item(target);
    if let Some(dropped) = replaced {
        if let Ok(mut vis) = vis_query.get_mut(dropped) {
            *vis = Visibility::Visible;
        }
        if let Ok(colliders) = children.get(dropped) {
            for &collider in colliders {
                commands.entity(collider).remove::<ColliderDisabled>();
            }
        }
        // for simplicities sake, set the transform of the item that is dropped to that of the player
        let mut item_transform = item_transform.get_mut(dropped).unwrap();
        *item_transform = *transform;
    }
}

pub fn handle_drop(
    mut commands: Commands,
    // current action
    mut player_query: Query<(&PlayerAction, &Transform, &mut Item), With<Player>>,
    mut vis_query: Query<&mut Visibility>,
    mut item_transform: Query<&mut Transform, (With<Interact>, Without<Player>)>,
    children: Query<&mut Children>,
) {
    let (PlayerAction::Drop, transform, mut item) = single_mut!(player_query) else {
        return;
    };
    let Some(item) = item.take() else {
        return;
    };
    if let Ok(mut vis) = vis_query.get_mut(item) {
        *vis = Visibility::Visible;
    }
    if let Ok(colliders) = children.get(item) {
        for &collider in colliders {
            commands.entity(collider).remove::<ColliderDisabled>();
        }
    }

    // for simplicities sake, set the transform of the item that is dropped to that of the player
    let mut item_transform = item_transform.get_mut(item).unwrap();
    *item_transform = *transform;
}

pub fn handle_door_interaction(
    mut commands: Commands,
    mut player_query: Query<(&PlayerAction, &mut Item), With<Player>>,
    card_query: Query<&KeyCardId>,
    mut wrong_key: ResMut<WrongKey>,
    // current interactable
    interaction_opportunity: ResMut<InteractionOpportunity>,
    door_query: Query<(Entity, &OpenedByKey), With<Interact>>,
) {
    // the card
    let Ok((PlayerAction::OpenDoor, mut item)) = player_query.get_single_mut() else {
        return;
    };
    let Some(current_card) = item.inner().and_then(|e| card_query.get(e).ok()) else {
        return;
    };

    // the door
    let Some((door, door_keyhole)) = interaction_opportunity
        .0
        .and_then(|e| door_query.get(e).ok())
    else {
        return;
    };

    if door_keyhole.can_be_opened_by(*current_card) {
        commands.entity(door).despawn_recursive();
        // unwrap is safe because of the item.inner().and_then
        commands.entity(item.inner().unwrap()).despawn_recursive();
        item.take();
    } else {
        // wrong key :)
        wrong_key.0 = true;
    }
}
