use axon_core::*;
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;

type AxonEventInvoke = fn(&[u8], &mut Commands<'_, '_>);

#[derive(Component)]
pub struct AxonClient {
    pub id: u64,
}

#[derive(Resource, Default)]
pub struct AxonContextSet {
    contexts: Vec<Box<dyn IAxonContext + Send + Sync>>,
    invoke_set: HashMap<u32, AxonEventInvoke>,
    client_set: HashMap<u64, Entity>,
}

impl AxonContextSet {
    pub fn add_context<T: IAxonContext + Send + Sync + 'static>(
        &mut self,
        context: T,
    ) -> &mut Self {
        self.contexts.push(Box::new(context));
        self
    }
}

#[derive(Default)]
pub struct AxonPlugin;

impl Plugin for AxonPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<AxonContextSet>();
        app.add_systems(PreUpdate, axon_update_system);
        app.add_systems(PostUpdate, axon_flush_system.in_set(AxonSystemSet::Flush));
        app.configure_sets(
            PostUpdate,
            (
                AxonSystemSet::Spawn,
                AxonSystemSet::Change,
                AxonSystemSet::Despawn,
                AxonSystemSet::Frame,
                AxonSystemSet::Flush,
            )
                .chain(),
        );
    }
}

fn axon_update_system(
    mut ctx: ResMut<AxonContextSet>,
    delta_time: Res<Time>,
    mut commands: Commands<'_, '_>,
) {
    let dt = Duration::from_secs_f32(delta_time.delta_secs());

    // 先接收事件到临时变量
    let mut new_events = Vec::new();

    for context in &mut ctx.contexts {
        context.update(dt);
        context.receive_event(&mut new_events);
    }

    for event in new_events {
        match event {
            AxonReceiveEvent::AddClient { client_id } => {
                if !ctx.client_set.contains_key(&client_id) {
                    let entity = commands.spawn(AxonClient { id: client_id }).id();
                    ctx.client_set.insert(client_id, entity);
                }
            }
            AxonReceiveEvent::RemoveClient { client_id } => {
                if let Some(entity) = ctx.client_set.remove(&client_id) {
                    commands.entity(entity).despawn();
                }
            }
            AxonReceiveEvent::Invoke {
                client_id: _,
                id: _,
                t,
                v,
            } => {
                if let Some(invoke) = ctx.invoke_set.get(&t) {
                    invoke(&v, &mut commands);
                }
            }
            _ => {}
        }
    }
}

fn axon_flush_system(mut ctx: ResMut<AxonContextSet>) {
    for context in &mut ctx.contexts {
        context.flush();
    }
}

pub trait AxonApp {
    fn add_axon_event<T: AxonEvent + Event + for<'a> Deserialize<'a>>(&mut self)
    where
        for<'a> <T as Event>::Trigger<'a>: Default;
    fn add_axon_object<T: AxonObject + Component>(&mut self);
    fn add_axon_variant<T: AxonVariant + Component + Serialize>(&mut self);
}

fn axon_event_invoke<T: AxonEvent + Event + for<'a> Deserialize<'a>>(
    v: &[u8],
    commands: &mut Commands<'_, '_>,
) where
    for<'a> <T as Event>::Trigger<'a>: Default,
{
    let event = axon_sbin::from_bytes::<T>(v).unwrap();
    commands.trigger(event);
}

#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
enum AxonSystemSet {
    Spawn,
    Change,
    Despawn,
    Frame,
    Flush,
}

#[derive(Event)]
pub struct AxonActionEvent {
    pub act: u8,
    pub id: u64,
    pub t: u32,
    pub v: Vec<u8>,
    pub client_id: u64,
}

impl AxonApp for App {
    fn add_axon_event<T: AxonEvent + Event + for<'a> Deserialize<'a>>(&mut self)
    where
        for<'a> <T as Event>::Trigger<'a>: Default,
    {
        let type_id = T::axon_event_type();
        self.world_mut()
            .resource_mut::<AxonContextSet>()
            .invoke_set
            .insert(type_id, axon_event_invoke::<T>);
    }

    fn add_axon_object<T: AxonObject + Component>(&mut self) {
        self.add_systems(
            PostUpdate,
            (
                reg_object_add::<T>.in_set(AxonSystemSet::Spawn),
                reg_object_removed::<T>.in_set(AxonSystemSet::Despawn),
            ),
        );
    }
    fn add_axon_variant<T: AxonVariant + Component + Serialize>(&mut self) {
        self.add_systems(
            PostUpdate,
            reg_variant_change::<T>.in_set(AxonSystemSet::Change),
        );
    }
}

fn reg_object_add<E: AxonObject + Component>(
    query: Query<Entity, Added<E>>,
    mut ctx: ResMut<AxonContextSet>,
) {
    for entity in query.iter() {
        let id: u64 = entity.to_bits();
        let t: u32 = E::axon_object_type();
        ctx.contexts
            .iter_mut()
            .for_each(|context| context.spawn(id, t));
    }
}

fn reg_object_removed<E: AxonObject + Component>(
    mut removed: RemovedComponents<E>,
    mut ctx: ResMut<AxonContextSet>,
) {
    for entity in removed.read() {
        let id: u64 = entity.to_bits();
        ctx.contexts
            .iter_mut()
            .for_each(|context| context.despawn(id));
    }
}

fn reg_variant_change<V: AxonVariant + Component + Serialize>(
    changed: Query<(Entity, &V), Changed<V>>,
    mut ctx: ResMut<AxonContextSet>,
) {
    for (entity, variant) in changed.iter() {
        let id: u64 = entity.to_bits();
        let t = V::axon_variant_type();
        let j = axon_sbin::to_bytes(variant).unwrap();
        ctx.contexts
            .iter_mut()
            .for_each(|context| context.change(id, t, &j));
    }
}
