use glam::Vec3;

use crate::components::trigger::{
    TriggerComponent, TriggerEvent, TriggerEventKind, TriggerFilter, TriggerShape, TriggerState,
};
use crate::entity::EntityHandle;
use crate::utility::transform::Transform;
use crate::world::Storage;

pub fn trigger_system(
    trigger_events: &mut Vec<TriggerEvent>,
    triggers: &mut Storage<TriggerComponent>,
    transforms: &Storage<Transform>,
    player_tags: &Storage<()>,
)
{
    trigger_events.clear();

    let trigger_entities: Vec<EntityHandle> = triggers.all();
    let mut pending_events: Vec<TriggerEvent> = Vec::new();

    for trigger_entity in trigger_entities
    {
        let trigger_pos = match transforms.get(trigger_entity)
        {
            Some(t) => t.position,
            None => continue,
        };

        let candidate_entities: Vec<EntityHandle> = match triggers.get(trigger_entity)
        {
            Some(trigger) => match &trigger.filter
            {
                TriggerFilter::Player => player_tags.all(),
            },
            None => continue,
        };

        let activator_positions: Vec<(EntityHandle, Vec3)> = candidate_entities
            .into_iter()
            .filter_map(|e| transforms.get(e).map(|t| (e, t.position)))
            .collect();

        let overlapping = match triggers.get(trigger_entity)
        {
            Some(trigger) => activator_positions
                .iter()
                .any(|(_, pos)| match &trigger.shape
                {
                    TriggerShape::Sphere { radius } => (trigger_pos - *pos).length() < *radius,
                }),
            None => continue,
        };

        let first_activator = activator_positions.first().map(|(e, _)| *e);

        if let Some(trigger) = triggers.get_mut(trigger_entity)
        {
            match (&trigger.state, overlapping)
            {
                (TriggerState::Idle, true) =>
                {
                    trigger.state = TriggerState::Inside;
                    if let Some(activator_entity) = first_activator
                    {
                        pending_events.push(TriggerEvent {
                            trigger_entity,
                            activator_entity,
                            kind: TriggerEventKind::Entered,
                        });
                    }
                }
                (TriggerState::Inside, false) =>
                {
                    trigger.state = TriggerState::Idle;
                    if let Some(activator_entity) = first_activator
                    {
                        pending_events.push(TriggerEvent {
                            trigger_entity,
                            activator_entity,
                            kind: TriggerEventKind::Exited,
                        });
                    }
                }
                _ =>
                {}
            }
        }
    }

    trigger_events.extend(pending_events);
}
