use crate::utils::global_observer;
use bevy::{ecs::system::SystemParam, prelude::*};
use lightyear::prelude::{AppTriggerExt, Channel, NetworkDirection, RemoteTrigger, TriggerSender};
use serde::{Deserialize, Serialize};

#[derive(SystemParam)]
pub struct ServerMultiTriggerSender<'w, 's, M: Event + Clone> {
    senders: Query<'w, 's, &'static mut TriggerSender<M>>,
}

impl<'w, 's, M: Event + Clone> ServerMultiTriggerSender<'w, 's, M> {
    pub fn server_trigger_targets<C: Channel>(&mut self, trigger: M, target: &[Entity]) {
        if cfg!(not(feature = "server")) {
            return;
        }

        for mut sender in self.senders.iter_mut() {
            sender.trigger_targets::<C>(trigger.clone(), target.iter().copied());
        }
    }
}

pub trait TriggerAppExt {
    fn replicate_trigger<M: Event + Clone + Serialize + for<'de> Deserialize<'de>, C: Channel>(
        &mut self,
    );
}

impl TriggerAppExt for App {
    fn replicate_trigger<M: Event + Clone + Serialize + for<'de> Deserialize<'de>, C: Channel>(
        &mut self,
    ) {
        self.add_trigger::<M>()
            .add_direction(NetworkDirection::ServerToClient);
        global_observer!(self, replicate_trigger_to_clients::<M, C>);
        global_observer!(self, remote_to_local_trigger::<M>);
    }
}

fn replicate_trigger_to_clients<M: Event + Clone, C: Channel>(
    trigger: Trigger<M>,
    mut sender: ServerMultiTriggerSender<M>,
) {
    let targets: &[Entity] = if trigger.target() == Entity::PLACEHOLDER {
        &[]
    } else {
        &[trigger.target()]
    };
    sender.server_trigger_targets::<C>(trigger.event().clone(), targets);
}

fn remote_to_local_trigger<M: Event + Clone>(trigger: Trigger<RemoteTrigger<M>>, mut c: Commands) {
    c.trigger_targets(trigger.event().trigger.clone(), trigger.target());
}
