use crate::components::game::cards::*;
use crate::components::game::header::*;
use crate::components::websocket::WebSocketContext;
use leptos::*;
use leptos_use::core::ConnectionReadyState;
use lib::*;
use serde_json::to_string;
use std::collections::HashMap;
pub mod cards;
pub mod header;
pub mod meta;
pub mod scoreboard;

#[component]
pub fn Game() -> impl IntoView {
    // Websocket stuff
    let websocket = expect_context::<WebSocketContext>();
    let tx = websocket.clone();
    let (websocket_send, set_websocket_send) = create_signal("".to_string());
    create_effect(move |_| {
        tx.send(&websocket_send());
    });

    // Incoming
    let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open;
    let game_meta = expect_context::<ReadSignal<Option<GameStateMeta>>>();
    let judge_round = expect_context::<ReadSignal<Option<JudgeRound>>>();

    // Signals //
    let (selected_cards, set_selected_cards) = create_signal::<Vec<WhiteCardMeta>>(vec![]);
    let (card_clicked, set_card_clicked) = create_signal::<String>(String::new());
    let (player_hand, set_player_hand) =
        create_signal::<HashMap<String, WhiteCardMeta>>(HashMap::new());
    let (judging, set_judging) = create_signal(false);

    // Outoging
    provide_context::<WriteSignal<String>>(set_card_clicked);

    // On Incoming Meta //
    // Put cards in a map for easier lookup
    // The server sends a vec to preserve ordering
    create_effect(move |_| {
        if game_meta().is_some() {
            for card in game_meta().unwrap().white {
                set_player_hand.update(|map| {
                    map.insert(card.uuid.clone(), card.clone());
                });
            }
        }
        set_selected_cards.update(|list| {
            list.clear();
        });
    });

    // On Incoming Judge //
    create_effect(move |_| {
        // Clear selected cards
        if judge_round().is_some() {
            set_selected_cards.update(|list| {
                list.clear();
            });
            set_judging(true);
        }
    });

    // Player Submit Handler //
    let submit_move = move |_| {
        let msg = to_string(&PlayerMoveRequest {
            game_id: game_meta().unwrap().uuid.clone(),
            card_ids: selected_cards()
                .iter()
                .map(|card| card.uuid.clone())
                .collect(),
        })
        .unwrap();

        set_websocket_send(msg);
        set_selected_cards.update(|list| {
            list.clear();
        });
    };

    // Judging Submit Handler //
    let submit_judge = move |_| {
        let msg = to_string(&JudgeDecisionRequest {
            game_id: game_meta().unwrap().uuid.clone(),
            winning_cards: selected_cards()
                .iter()
                .map(|card| card.uuid.clone())
                .collect(),
        })
        .unwrap();

        set_websocket_send(msg);
        set_judging(false);
        set_selected_cards.update(|list| {
            list.clear();
        });
    };

    // Card selection //
    // Toggle selected status of cards
    create_effect(move |_| {
        if card_clicked() != "".to_string() {
            if judging.get_untracked() {
                let identical_cards = selected_cards
                    .get_untracked()
                    .into_iter()
                    .filter(|card| card.uuid == card_clicked.get_untracked())
                    .collect::<Vec<WhiteCardMeta>>();

                if identical_cards.len() > 0 {
                    set_selected_cards.update(|list| {
                        list.clear();
                    });
                } else {
                    // Clear selected cards
                    set_selected_cards.update(|list| {
                        list.clear();
                    });

                    // Select card group
                    for group in judge_round.get_untracked().unwrap().cards_to_judge {
                        for card in &group {
                            if card.uuid == card_clicked() {
                                set_selected_cards.update(|cards| cards.extend(group));
                                break;
                            }
                        }
                    }
                }
                // Clear the signal otherwise it selects the last selected card again
                set_card_clicked.update_untracked(|value| value.clear());
            } else {
                if !selected_cards.get_untracked().contains(
                    player_hand
                        .get_untracked()
                        .get(&card_clicked.get_untracked())
                        .unwrap(),
                ) {
                    set_selected_cards.update(|cards| {
                        cards.push(player_hand().get(&card_clicked()).unwrap().clone())
                    })
                } else if selected_cards
                    .get_untracked()
                    .contains(player_hand.get_untracked().get(&card_clicked()).unwrap())
                {
                    set_selected_cards.update(|cards| {
                        cards.remove(
                            cards
                                .iter()
                                .position(|card| {
                                    card == player_hand().get(&card_clicked()).unwrap()
                                })
                                .unwrap(),
                        );
                    })
                }
                // Clear the signal otherwise it selects the last selected card again
                set_card_clicked.update_untracked(|value| value.clear());
            }
        }
    });

    view! {
        <div class="my-2">
            <h2 class="text-2xl">Game</h2>
            <Show
                when=move || game_meta.get().is_some() && connected()
                fallback=|| view! { "You are not in a game" }
            >
                <Header game_meta=game_meta().unwrap() />
            </Show>

            // Judging view //

            <Show when=move || { judging() && connected() }>
                <div class="w-full ms-16 inline-flex flex-wrap">
                    <BlackCard card_data=game_meta().unwrap().black />

                    // Selected cards
                    <For
                        each=move || selected_cards()
                        key=move |card| card.uuid.clone()
                        children=move |card| {
                            view! { <WhiteCard card_data=card.clone() /> }
                        }
                    />
                </div>

                // Submit button
                <div class="w-full inline-flex flex-wrap justify-center">
                    <button type="button" on:click=submit_judge>
                        Submit
                    </button>
                </div>

                <For
                    each=move || judge_round().unwrap().cards_to_judge
                    key=move |_| 69
                    children=move |group| {
                        view! {
                            <div class="m-2 inline-flex flex-wrap justify-center">
                                <For
                                    each=move || group.clone()
                                    key=move |card| card.uuid.clone()
                                    children=move |card| {
                                        view! {
                                            // Hide cards from hand view when they exist as selected
                                            <Show when={
                                                let waste_of_memory = card.clone();
                                                move || { !selected_cards().contains(&waste_of_memory) }
                                            }>
                                                <WhiteCard card_data=card.clone() />
                                            </Show>
                                        }
                                    }
                                />
                            </div>
                        }
                    }
                />
            </Show>

            // Playing view //

            <Show when=move || game_meta.get().is_some() && connected() && !judging()>

                // Play cards
                <div class="w-full ms-16 inline-flex flex-wrap">
                    <BlackCard card_data=game_meta().unwrap().black />

                    // Selected cards
                    <For
                        each=move || selected_cards()
                        key=move |card| card.uuid.clone()
                        children=move |card| {
                            view! { <WhiteCard card_data=card.clone() /> }
                        }
                    />
                </div>

                // Submit button
                <div class="w-full inline-flex flex-wrap justify-center">
                    <button type="button" on:click=submit_move>
                        Submit
                    </button>
                </div>

                // Player hand
                <div class="inline-flex flex-wrap justify-center">
                    <For
                        each=move || game_meta().unwrap().white
                        key=move |card| card.uuid.clone()
                        children=move |card| {
                            view! {
                                // Hide cards from hand view when they exist as selected
                                <Show when={
                                    let id = card.uuid.clone();
                                    move || {
                                        if let Some(card) = player_hand().get(&id) {
                                            !selected_cards().contains(card)
                                        } else {
                                            true
                                        }
                                    }
                                }>
                                    <WhiteCard card_data=card.clone() />
                                </Show>
                            }
                        }
                    />
                </div>
            </Show>

        </div>
    }
}