use crate::{
    GameState, character::CharacterAnimations, head::ActiveHead, heads_database::HeadsDatabase,
};
use bevy::{animation::RepeatAnimation, ecs::query::QueryData, prelude::*};
use serde::{Deserialize, Serialize};
use std::time::Duration;

pub fn plugin(app: &mut App) {
    app.register_type::<AnimationFlags>();

    app.add_systems(
        Update,
        update_animation.run_if(in_state(GameState::Playing)),
    );
}

#[derive(Component, Default, Reflect, PartialEq, Serialize, Deserialize)]
#[reflect(Component)]
#[require(AnimationFlagCache)]
pub struct AnimationFlags {
    pub any_direction: bool,
    pub jumping: bool,
    pub jump_count: u8,
    pub shooting: bool,
    pub restart_shooting: bool,
    pub hit: bool,
}

#[derive(Component, Default, Reflect)]
#[reflect(Component)]
pub struct AnimationFlagCache {
    pub jump_count: u8,
}

#[derive(QueryData)]
#[query_data(mutable)]
pub struct AnimationController {
    pub transitions: &'static mut AnimationTransitions,
    pub player: &'static mut AnimationPlayer,
}

impl AnimationController {
    pub fn play_inner(
        player: &mut AnimationPlayer,
        transitions: &mut AnimationTransitions,
        animation: AnimationNodeIndex,
        transition: Duration,
        repeat: RepeatAnimation,
    ) {
        transitions
            .play(player, animation, transition)
            .set_repeat(repeat);
    }
}

impl AnimationControllerItem<'_> {
    pub fn play(
        &mut self,
        animation: AnimationNodeIndex,
        transition: Duration,
        repeat: RepeatAnimation,
    ) {
        AnimationController::play_inner(
            &mut self.player,
            &mut self.transitions,
            animation,
            transition,
            repeat,
        );
    }

    pub fn is_playing(&self, index: AnimationNodeIndex) -> bool {
        self.player.is_playing_animation(index)
    }
}

const DEFAULT_TRANSITION_DURATION: Duration = Duration::ZERO;

fn update_animation(
    mut animated: Query<(AnimationController, &CharacterAnimations)>,
    mut character: Query<(&ActiveHead, &mut AnimationFlags, &mut AnimationFlagCache)>,
    headdb: Res<HeadsDatabase>,
) {
    for (mut controller, anims) in animated.iter_mut() {
        let (head, mut flags, mut cache) = character.get_mut(anims.of_character).unwrap();
        let head = headdb.head_stats(head.0);

        let is_playing_shoot = anims.shoot.is_some()
            && controller.is_playing(anims.shoot.unwrap())
            && !controller
                .player
                .animation(anims.shoot.unwrap())
                .unwrap()
                .is_finished();
        let is_playing_run_shoot = anims.run_shoot.is_some()
            && controller.is_playing(anims.run_shoot.unwrap())
            && !controller
                .player
                .animation(anims.run_shoot.unwrap())
                .unwrap()
                .is_finished();
        let wait_for_shoot = !head.interrupt_shoot && (is_playing_shoot || is_playing_run_shoot);
        if wait_for_shoot {
            return;
        } else if flags.shooting && flags.any_direction && anims.run_shoot.is_some() {
            if !controller.is_playing(anims.run_shoot.unwrap()) {
                controller.play(
                    anims.run_shoot.unwrap(),
                    DEFAULT_TRANSITION_DURATION,
                    RepeatAnimation::Never,
                );
            }
            if controller
                .player
                .animation(anims.run_shoot.unwrap())
                .unwrap()
                .is_finished()
                || flags.restart_shooting
            {
                controller
                    .player
                    .animation_mut(anims.run_shoot.unwrap())
                    .unwrap()
                    .replay();

                flags.restart_shooting = false;
            }
        } else if flags.shooting && anims.shoot.is_some() {
            if !controller.is_playing(anims.shoot.unwrap()) {
                controller.play(
                    anims.shoot.unwrap(),
                    DEFAULT_TRANSITION_DURATION,
                    RepeatAnimation::Never,
                );
            }
            if controller
                .player
                .animation(anims.shoot.unwrap())
                .unwrap()
                .is_finished()
                || flags.restart_shooting
            {
                controller
                    .player
                    .animation_mut(anims.shoot.unwrap())
                    .unwrap()
                    .replay();

                flags.restart_shooting = false;
            }
        } else if flags.hit {
            if !controller.is_playing(anims.hit) {
                controller.play(
                    anims.hit,
                    DEFAULT_TRANSITION_DURATION,
                    RepeatAnimation::Never,
                );
            }
        } else if flags.jumping {
            if !controller.is_playing(anims.jump) || flags.jump_count != cache.jump_count {
                controller.play(
                    anims.jump,
                    DEFAULT_TRANSITION_DURATION,
                    RepeatAnimation::Never,
                );
                cache.jump_count = flags.jump_count;
            }
        } else if flags.any_direction {
            if !controller.player.is_playing_animation(anims.run) {
                controller.play(
                    anims.run,
                    DEFAULT_TRANSITION_DURATION,
                    RepeatAnimation::Forever,
                );
            }
        } else if !controller.is_playing(anims.idle) {
            controller.play(
                anims.idle,
                DEFAULT_TRANSITION_DURATION,
                RepeatAnimation::Forever,
            );
        }
    }
}
