use bevy::utils::HashSet;

use crate::prelude::*;

pub(crate) struct TutorialPlugin;

impl Plugin for TutorialPlugin {
    fn build(&self, app: &mut App) {
        app.add_state::<TutorialState>()
            .init_resource::<SeenStates>()
            .add_systems(
                OnExit(GameState::Loading),
                initialize_tutorial
                    .run_if(resource_exists::<tweak::GameTweaks>())
                    .run_if(run_once()),
            )
            .add_systems(
                Update,
                menu::handle_menu_button::<TutorialState>.run_if(in_state(GameState::Play)),
            )
            .add_systems(
                Update,
                (
                    // Evaluate if a piece is selected
                    step.run_if(state_exists::<TutorialState>())
                        .run_if(not(in_state(TutorialState::None)))
                        .run_if(
                            // A piece changes sides
                            any_component_changed::<game::Side>
                                // When a piece is selected, we
                                .or_else(any_component_added::<game::Selected>)
                                // A piece is de-selected
                                .or_else(any_component_removed::<game::Selected>())
                                // TEMP: The user hits 'enter'
                                .or_else(|keys: Res<Input<KeyCode>>| -> bool {
                                    keys.just_pressed(KeyCode::Return)
                                }),
                        ),
                ),
            )
            .add_systems(OnExit(GameState::Play), deactivate::<TutorialState>)
            .add_systems(OnEnter(GameState::Play), activate::<Tutorial>)
            .add_systems(
                Update,
                activate_tutorial_step.run_if(state_changed::<TutorialState>()),
            );
    }
}

#[derive(Debug, States, Hash, Default, PartialEq, Eq, Clone, Component)]
pub(crate) struct Tutorial;

#[derive(Debug, States, Hash, Default, PartialEq, Eq, Clone, Component)]
pub(crate) enum TutorialState {
    #[default]
    None,
    Empty,
    Intro,
    Objective,
    PieceIntro,
    PieceQueen,
    PieceDrone,
    PiecePawn,
    PieceJump,
    PieceEnd,
    Ownership,
    _Promotions,
    Outro,
}

// Assertion: "Tutorial" just starts a regular game with TutorialState set to Intro
// This plays out a usual game, but with tutorial text overlayed
// Once tutorial is done, the game can continue as usual

// Init animations, initialize all text windows but make invisible
// This should be run when TutorialState::Intro
fn initialize_tutorial(
    ui_font: Res<ui::UiFont>,
    tweaks_file: Res<tweak::GameTweaks>,
    tweaks: Res<Assets<tweak::Tweaks>>,
    mut commands: Commands,
) {
    let tweak = tweaks.get(tweaks_file.handle.clone()).expect("Load tweaks");

    info!("Initializing tutorial entities");

    // Tutorial button used for toggling on/off during gameplay
    {
        commands
            .spawn((
                TutorialState::Intro, // Marks the button to start the tutorial
                Tutorial,             // Marks the button for activate::<Tutorial>
                GameState::Play,      // Marks the button to be ignored during tutorial step cleanup
                ButtonBundle {
                    style: Style {
                        padding: UiRect::all(Val::Px(5.0)),
                        margin: UiRect::all(Val::Px(5.0)),
                        position_type: PositionType::Absolute,
                        top: Val::Px(0.0),
                        right: Val::Px(0.0),
                        ..default()
                    },
                    background_color: Color::ORANGE.with_a(0.5).into(),
                    visibility: Visibility::Hidden,
                    ..default()
                },
            ))
            .with_children(|parent| {
                parent.spawn((TextBundle::from_section(
                    "Tutorial",
                    TextStyle {
                        color: Color::BLACK,
                        font_size: 16.0,
                        ..default()
                    },
                ),));
            });
    }

    let background_hex = tweak.get::<String>("tutorial_rgba_background").unwrap();
    let text_visible_hex = tweak.get::<String>("tutorial_rgba_visible").unwrap();

    // List of (state, lines) for tutorial steps
    [
        (
            TutorialState::Intro,
            tweak
                .get::<Vec<String>>("tutorial_intro")
                .expect("Tutorial intro"),
        ),
        (
            TutorialState::Objective,
            tweak
                .get::<Vec<String>>("tutorial_objective")
                .expect("Tutorial objective"),
        ),
        (
            TutorialState::Ownership,
            tweak
                .get::<Vec<String>>("tutorial_ownership")
                .expect("Tutorial ownership"),
        ),
        (
            TutorialState::_Promotions,
            tweak
                .get::<Vec<String>>("tutorial_promotions")
                .expect("Tutorial promotions"),
        ),
        (
            TutorialState::Outro,
            tweak
                .get::<Vec<String>>("tutorial_outro")
                .expect("Tutorial pieces end"),
        ),
        (
            TutorialState::PieceIntro,
            tweak
                .get::<Vec<String>>("tutorial_pieces_prompt")
                .expect("Tutorial pieces prompt"),
        ),
        (
            TutorialState::PieceJump,
            tweak
                .get::<Vec<String>>("tutorial_pieces_jumping")
                .expect("Tutorial pieces jumping"),
        ),
        (
            TutorialState::PieceEnd,
            tweak
                .get::<Vec<String>>("tutorial_pieces_end")
                .expect("Tutorial pieces end"),
        ),
        (
            TutorialState::PieceQueen,
            tweak
                .get::<Vec<String>>("tutorial_pieces_queen")
                .expect("Tutorial pieces queen"),
        ),
        (
            TutorialState::PieceDrone,
            tweak
                .get::<Vec<String>>("tutorial_pieces_drone")
                .expect("Tutorial pieces drone"),
        ),
        (
            TutorialState::PiecePawn,
            tweak
                .get::<Vec<String>>("tutorial_pieces_pawn")
                .expect("Tutorial pieces pawn"),
        ),
    ]
    .iter()
    .for_each(|(step, lines)| {
        commands
            .spawn((
                step.clone(),
                NodeBundle {
                    style: Style {
                        width: Val::Percent(100.0),
                        height: Val::Percent(100.0),
                        justify_content: JustifyContent::Center,
                        align_items: AlignItems::FlexEnd,
                        flex_direction: FlexDirection::Column,
                        position_type: PositionType::Absolute,
                        padding: UiRect::all(Val::Px(25.0)),
                        ..default()
                    },
                    background_color: Color::NONE.into(),
                    visibility: Visibility::Hidden,
                    ..default()
                },
            ))
            .with_children(|parent| {
                parent
                    .spawn((
                        step.clone(),
                        NodeBundle {
                            style: Style {
                                width: Val::Percent(33.0),
                                max_height: Val::Percent(100.0),
                                padding: UiRect::all(Val::Px(25.0)),
                                ..default()
                            },
                            background_color: Color::hex(&background_hex).unwrap().into(),
                            ..default()
                        },
                    ))
                    .with_children(|parent| {
                        parent.spawn((
                            step.clone(),
                            TextBundle {
                                text: Text {
                                    sections: lines
                                        .iter()
                                        .map(|line| TextSection {
                                            value: line.clone(),
                                            style: TextStyle {
                                                font_size: 16.0,
                                                color: Color::hex(&text_visible_hex).unwrap(),
                                                font: ui_font.handle.clone(),
                                            },
                                        })
                                        .collect(),
                                    alignment: TextAlignment::Left,
                                    ..default()
                                },
                                ..default()
                            },
                        ));
                    });
            });
    })
}

#[derive(Debug, Resource, Default)]
struct SeenStates(HashSet<TutorialState>);

fn step(
    pieces: Query<&game::Piece, Added<game::Selected>>,
    transitions: Query<Entity, Changed<game::Side>>,
    curr_state: Res<State<TutorialState>>,
    mut next_state: ResMut<NextState<TutorialState>>,
    mut seen: ResMut<SeenStates>,
) {
    info!("Evalute tutorial state");

    // Store the current state
    // Used to avoid doing a prevoius state again
    seen.0.insert(curr_state.get().clone());

    info!("Curr: {:?}, Seen: {:?}", curr_state.get(), seen);

    next_state.set(match curr_state.get() {
        // This transition is implicit. Menu transitions from None to Intro
        TutorialState::None => TutorialState::Intro,
        // From Intro we always go to Objective
        TutorialState::Intro => TutorialState::Objective,
        // From objective we go to the Pieces flow intro
        TutorialState::Objective => TutorialState::PieceIntro,
        // From PiecesIntro we can go to any other Pieces if a piece is selected
        // Each pieces tutorial can transition to any other, as well as Ownership
        TutorialState::PieceIntro
        | TutorialState::PieceQueen
        | TutorialState::PieceDrone
        | TutorialState::PiecePawn
        | TutorialState::PieceEnd
        | TutorialState::PieceJump
        | TutorialState::Empty
        | TutorialState::Ownership => {
            // PERF: Doing all of this work up front results in unnecessary work
            let piece_selected = !pieces.is_empty();
            let all_moves_done = seen.0.contains(&TutorialState::PieceIntro)
                && seen.0.contains(&TutorialState::PieceQueen)
                && seen.0.contains(&TutorialState::PieceDrone)
                && seen.0.contains(&TutorialState::PiecePawn);
            let ownership_done = seen.0.contains(&TutorialState::Ownership);
            let ownership_check = !ownership_done && transitions.iter().count() > 0;
            let jump_done = seen.0.contains(&TutorialState::PieceJump);
            let queen_selected = pieces.iter().filter(|&p| *p == game::Piece::Queen).count() > 0;
            let queen_seen = seen.0.contains(&TutorialState::PieceQueen);
            let drone_selected = pieces.iter().filter(|&p| *p == game::Piece::Drone).count() > 0;
            let drone_seen = seen.0.contains(&TutorialState::PieceDrone);
            let pawn_selected = pieces.iter().filter(|&p| *p == game::Piece::Pawn).count() > 0;
            let pawn_seen = seen.0.contains(&TutorialState::PiecePawn);

            // A piece is selected, so talk about it
            if piece_selected {
                // When a queen is selected for the first time...
                if queen_selected && !queen_seen {
                    TutorialState::PieceQueen
                // When a drone is selected for the first time...
                } else if drone_selected && !drone_seen {
                    TutorialState::PieceDrone
                // When a pawn is selected for the first time...
                } else if pawn_selected && !pawn_seen {
                    TutorialState::PiecePawn
                } else {
                    TutorialState::Empty
                }
            // There are no pieces selected
            } else {
                // All move, jump, and ownership tutorials done, say goodbye
                if all_moves_done && jump_done && ownership_done {
                    TutorialState::Outro
                }
                // A piece moves sides, so talk about ownership
                else if ownership_check {
                    TutorialState::Ownership
                }
                // We have not touched on jumping yet
                else if all_moves_done && !jump_done {
                    TutorialState::PieceJump
                // All pieces tutorialized, so prompt user to move pieces for more tutorial
                } else if all_moves_done {
                    TutorialState::PieceEnd
                // Default, empty (tutorial doesn't always need to show something)
                } else {
                    TutorialState::Empty
                }
            }
        }
        // After the outro, we exit the tutorial
        TutorialState::Outro => TutorialState::None,
        TutorialState::_Promotions => todo!("Not implemented yet!"),
    });
}

fn activate_tutorial_step(
    state: Res<State<TutorialState>>,
    mut query: Query<(&mut Visibility, &TutorialState), Without<GameState>>,
) {
    info!("Activating step {:?}", state.get());

    // Iterate over all entities with TutorialState components
    query.iter_mut().for_each(|(mut v, s)| {
        // If their component matches the current state, make visible
        if s == state.get() {
            *v = Visibility::Inherited;
        // Otherwise hide it (again)
        } else {
            *v = Visibility::Hidden;
        }
    });
}

// Start by loading the same game as display3d does

// Show the intro text when entering Intro TutorialState
// Continue

// Show objective when entering Objective TutorialState
// Continue

// Prompt user to click some pieces
// When each piece type is clicked, show relevant text
// Once all pieces are described say jumping text
// Finally end with final text
// Continue

// When a piece crosses the canal, show ownership text
// Continue

// A few moves in, show field promotions text
// Continue

// End with outro text
// Continue

// Play game out as usual

// If player early exits, set TutorialState to None
