pub use bevy_animation_macros::AnimationEvent;

use bevy_ecs::{
    entity::Entity,
    event::{trigger_entity_internal, Event, Trigger},
    observer::{CachedObservers, TriggerContext},
    world::DeferredWorld,
};

/// An [`Event`] that an [`AnimationPlayer`](crate::AnimationPlayer) or an [`AnimationTargetId`](crate::AnimationTargetId) can trigger when playing an [`AnimationClip`](crate::AnimationClip).
///
/// - If you used [`AnimationClip::add_event`](crate::AnimationClip::add_event), this will be triggered by the [`AnimationPlayer`](crate::AnimationPlayer).
/// - If you used [`AnimationClip::add_event_to_target`](crate::AnimationClip::add_event_to_target), this will be triggered by the [`AnimationTargetId`](crate::AnimationTargetId).
///
/// This trait can be derived.
pub trait AnimationEvent: Clone + for<'a> Event<Trigger<'a> = AnimationEventTrigger> {}

/// The [`Trigger`] implementation for [`AnimationEvent`]. This passes in either the [`AnimationPlayer`](crate::AnimationPlayer) or the [`AnimationTargetId`](crate::AnimationTargetId)
/// context, and uses that to run any observers that target that entity. See [`AnimationEvent`] for when which entity is used.
#[derive(Debug)]
pub struct AnimationEventTrigger {
    /// The [`AnimationPlayer`](crate::AnimationPlayer) or the [`AnimationTargetId`](crate::AnimationTargetId) where this [`AnimationEvent`] occurred.
    /// See [`AnimationEvent`] for when which entity is used.
    pub target: Entity,
}

#[expect(
    unsafe_code,
    reason = "We must implement this trait to define a custom Trigger, which is required to be unsafe due to safety considerations within bevy_ecs."
)]
// SAFETY:
// - `E`'s [`Event::Trigger`] is constrained to [`AnimationEventTrigger`]
// - The implementation abides by the other safety constraints defined in [`Trigger`]
unsafe impl<E: AnimationEvent + for<'a> Event<Trigger<'a> = AnimationEventTrigger>> Trigger<E>
    for AnimationEventTrigger
{
    unsafe fn trigger(
        &mut self,
        world: DeferredWorld,
        observers: &CachedObservers,
        trigger_context: &TriggerContext,
        event: &mut E,
    ) {
        let target = self.target;
        // SAFETY:
        // - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
        // - the passed in event pointer comes from `event`, which is an `Event`
        // - `trigger` is a matching trigger type, as it comes from `self`, which is the Trigger for `E`
        // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger`
        // - this abides by the nuances defined in the `Trigger` safety docs
        unsafe {
            trigger_entity_internal(
                world,
                observers,
                event.into(),
                self.into(),
                target,
                trigger_context,
            );
        }
    }
}
