use crate::{GameState, HEDZ_GREEN, heads::HeadsImages, loading_assets::UIAssets};
use bevy::{ecs::spawn::SpawnIter, prelude::*};
use shared::backpack::backpack_ui::{
    BackpackCountText, BackpackMarker, BackpackUiState, HEAD_SLOTS, HeadDamage, HeadImage,
    HeadSelector,
};

pub fn plugin(app: &mut App) {
    app.add_systems(OnEnter(GameState::Playing), setup);
    app.add_systems(
        FixedUpdate,
        (update, update_visibility, update_count).run_if(in_state(GameState::Playing)),
    );
}

fn setup(mut commands: Commands, assets: Res<UIAssets>) {
    commands.spawn((
        Name::new("backpack-ui"),
        BackpackMarker,
        Visibility::Hidden,
        Node {
            position_type: PositionType::Absolute,
            top: Val::Px(20.0),
            right: Val::Px(20.0),
            height: Val::Px(74.0),
            ..default()
        },
        Children::spawn(SpawnIter((0..HEAD_SLOTS).map({
            let bg = assets.head_bg.clone();
            let regular = assets.head_regular.clone();
            let selector = assets.head_selector.clone();
            let damage = assets.head_damage.clone();

            move |i| {
                spawn_head_ui(
                    bg.clone(),
                    regular.clone(),
                    selector.clone(),
                    damage.clone(),
                    i,
                )
            }
        }))),
    ));

    commands.spawn((
        Name::new("backpack-head-count-ui"),
        Text::new("0"),
        TextShadow::default(),
        BackpackCountText,
        TextFont {
            font: assets.font.clone(),
            font_size: 34.0,
            ..default()
        },
        TextColor(HEDZ_GREEN.into()),
        TextLayout::new_with_justify(JustifyText::Center),
        Node {
            position_type: PositionType::Absolute,
            top: Val::Px(20.0),
            right: Val::Px(20.0),
            ..default()
        },
    ));
}

fn spawn_head_ui(
    bg: Handle<Image>,
    regular: Handle<Image>,
    selector: Handle<Image>,
    damage: Handle<Image>,
    head_slot: usize,
) -> impl Bundle {
    const SIZE: f32 = 90.0;
    const DAMAGE_SIZE: f32 = 74.0;

    (
        Node {
            position_type: PositionType::Relative,
            justify_content: JustifyContent::Center,
            align_items: AlignItems::Center,
            width: Val::Px(SIZE),
            ..default()
        },
        children![
            (
                Name::new("selector"),
                Node {
                    position_type: PositionType::Absolute,
                    bottom: Val::Px(-30.0),
                    ..default()
                },
                Visibility::Hidden,
                ImageNode::new(selector).with_flip_y(),
                HeadSelector(head_slot),
            ),
            (
                Name::new("bg"),
                Node {
                    position_type: PositionType::Absolute,
                    ..default()
                },
                ImageNode::new(bg),
            ),
            (
                Name::new("head"),
                Node {
                    position_type: PositionType::Absolute,
                    ..default()
                },
                ImageNode::default(),
                Visibility::Hidden,
                HeadImage(head_slot),
            ),
            (
                Name::new("rings"),
                Node {
                    position_type: PositionType::Absolute,
                    ..default()
                },
                ImageNode::new(regular),
            ),
            (
                Name::new("health"),
                Node {
                    height: Val::Px(DAMAGE_SIZE),
                    width: Val::Px(DAMAGE_SIZE),
                    ..default()
                },
                children![(
                    Name::new("damage_ring"),
                    HeadDamage(head_slot),
                    Node {
                        position_type: PositionType::Absolute,
                        display: Display::Block,
                        overflow: Overflow::clip(),
                        top: Val::Px(0.),
                        left: Val::Px(0.),
                        right: Val::Px(0.),
                        height: Val::Percent(0.),
                        ..default()
                    },
                    children![ImageNode::new(damage)]
                )]
            )
        ],
    )
}

fn update_visibility(
    state: Single<&BackpackUiState, Changed<BackpackUiState>>,
    mut backpack: Single<&mut Visibility, (With<BackpackMarker>, Without<BackpackCountText>)>,
    mut count: Single<&mut Visibility, (Without<BackpackMarker>, With<BackpackCountText>)>,
) {
    **backpack = if state.open {
        Visibility::Visible
    } else {
        Visibility::Hidden
    };

    **count = if !state.open {
        Visibility::Visible
    } else {
        Visibility::Hidden
    };
}

fn update_count(
    state: Single<&BackpackUiState, Changed<BackpackUiState>>,
    text: Option<Single<Entity, With<BackpackCountText>>>,
    mut writer: TextUiWriter,
) {
    let Some(text) = text else {
        return;
    };

    *writer.text(*text, 0) = state.count.to_string();
}

fn update(
    state: Single<&BackpackUiState, Changed<BackpackUiState>>,
    heads_images: Res<HeadsImages>,
    mut head_image: Query<(&HeadImage, &mut Visibility, &mut ImageNode), Without<HeadSelector>>,
    mut head_damage: Query<(&HeadDamage, &mut Node), Without<HeadSelector>>,
    mut head_selector: Query<(&HeadSelector, &mut Visibility), Without<HeadImage>>,
) {
    for (HeadImage(head), mut vis, mut image) in head_image.iter_mut() {
        if let Some(head) = &state.heads[*head] {
            *vis = Visibility::Inherited;
            image.image = heads_images.heads[head.head].clone();
        } else {
            *vis = Visibility::Hidden;
        }
    }
    for (HeadDamage(head), mut node) in head_damage.iter_mut() {
        if let Some(head) = &state.heads[*head] {
            node.height = Val::Percent(head.damage() * 100.0);
        }
    }

    for (HeadSelector(head), mut vis) in head_selector.iter_mut() {
        *vis = if *head == state.relative_current_slot() {
            Visibility::Inherited
        } else {
            Visibility::Hidden
        };
    }
}
