use crate::GameState;
use crate::player::toolbar::Item;
use crate::player::{Player, PlayerAction};
use crate::util::{single, single_mut};
use bevy::{prelude::*, window::PrimaryWindow};
use bevy_egui::{EguiContexts, EguiPlugin, egui};
use bevy_rapier3d::prelude::*;
use bevy_rapier3d::rapier::prelude::CollisionEventFlags;
use std::iter;

use super::{Interact, objects};

pub(super) fn plugin(app: &mut App) {
    app.add_plugins(EguiPlugin)
        .init_resource::<InteractionOpportunity>()
        .add_systems(
            Update,
            (
                update_interaction_opportunities,
                display_interaction_prompt,
                (objects::handle_pick_up, objects::handle_drop),
            )
                .chain()
                .run_if(in_state(GameState::Playing)),
        );
}

#[derive(Debug, Clone, Eq, PartialEq, Resource, Default)]
pub struct InteractionOpportunity(pub Option<Entity>);

fn update_interaction_opportunities(
    mut collision_events: EventReader<CollisionEvent>,
    player_query: Query<Entity, With<Player>>,
    parents: Query<&Parent>,
    target_query: Query<Entity, (Without<Player>, With<Interact>)>,
    mut interaction_opportunity: ResMut<InteractionOpportunity>,
) {
    let player = single!(player_query);

    for event in collision_events.read() {
        let (e1, e2, started) = match event {
            CollisionEvent::Started(e1, e2, CollisionEventFlags::SENSOR) => (*e1, *e2, true),
            CollisionEvent::Stopped(e1, e2, CollisionEventFlags::SENSOR) => (*e1, *e2, false),
            _ => {
                continue;
            }
        };

        let sensor = match player {
            p if p == e1 => e2,
            p if p == e2 => e1,
            _ => continue,
        };
        let mut ancestors = iter::once(sensor).chain(parents.iter_ancestors(sensor));

        let Some(interactable) = ancestors.find_map(|entity| target_query.get(entity).ok()) else {
            continue;
        };

        if started {
            interaction_opportunity.0.replace(interactable);
        } else {
            interaction_opportunity.0.take_if(|t| *t == interactable);
        }
    }
}

fn display_interaction_prompt(
    interaction_opportunity: Res<InteractionOpportunity>,
    mut egui_contexts: EguiContexts,
    primary_windows: Query<&Window, With<PrimaryWindow>>,
    names: Query<&Name, With<Interact>>, // only the interactables ofcourse
) {
    let Some(opportunity) = interaction_opportunity.0 else {
        return;
    };
    let window = single!(primary_windows);

    let entity_name = names
        .get(opportunity)
        .map(|name| name.as_str())
        .expect("A named Interactable object");

    // objective or item
    egui::Window::new("Interaction")
        .collapsible(false)
        .title_bar(false)
        .auto_sized()
        .fixed_pos(egui::Pos2::new(window.width() / 2., window.height() / 2.))
        .show(egui_contexts.ctx_mut(), |ui| {
            ui.label(format!("E: Pick Up {entity_name}"));
        });
}
