use std::rc::Rc;

use heck::ToTitleCase;
use leptos::*;
use leptos_router::Router;
use magister::{ColorBalance, PlayerInitDescriptor, Game, GameCreationError};
use phosphor_leptos::Horse;
use crate::display::Player as PlayerDisplay;

#[component]
fn SubtypePlayer() -> impl IntoView {
    use magister::card::Subtype;
    let (subtype_str, set_subtype_str) = create_signal("".to_string());
    let subtype = move || Subtype::new(subtype_str.get());
    let (member, set_member) = create_signal(String::new());
    let is_member = {
        move || {
            let member = member.get();
            let subtype = subtype();
            !member.is_empty()
                && member
                    .split_whitespace()
                    .all(|mem_sub| subtype.is_member(mem_sub))
        }
    };
    let is_quasimember = {
        move || {
            let member = member.get();
            let subtype = subtype();
            !member.is_empty()
                && member
                    .split_whitespace()
                    .all(|mem_sub| subtype.is_quasimember(mem_sub))
        }
    };
    view! {
        <div class="w-1/2">
            <label class="input input-bordered flex items-center gap-2">
                <Horse size="24px"/>
                <input
                    type="text"
                    class="grow"
                    placeholder="Subtype"
                    prop:value=move || subtype_str.get()
                    on:input=move |ev| set_subtype_str.set(event_target_value(&ev))
                />
            </label>

            <ul class="list-disc list-inside">
                {move || {
                    subtype()
                        .subtypes()
                        .map(|st| view! { <li>{st.to_title_case()}</li> })
                        .collect_view()
                }}

            </ul>

            <div class="join w-full flex">
                <label class="input input-bordered flex items-center gap-2 join-item grow">

                    <input
                        type="text"
                        class="grow"
                        placeholder="Member?"
                        prop:value=move || member.get()
                        on:input=move |ev| set_member.set(event_target_value(&ev))
                    />
                </label>
                <button class="btn join-item" on:click=move |_| set_member.update(|qm| qm.clear())>
                    "Clear"
                </button>
            </div>

            <ul class="list-inside list-disc">
                <li>
                    "Is Member? (“Search for a X card.”) "
                    <span
                        class=("text-error", move || !member.get().is_empty() && !is_member())
                        class=("text-primary", is_member)
                    >
                        {is_member}
                    </span>
                </li>

                <li>
                    "Is Quasimember? (“Search for a X or similar card.”) "
                    <span
                        class=("text-error", move || !member.get().is_empty() && !is_quasimember())
                        class=("text-primary", is_quasimember)
                    >
                        {is_quasimember}
                    </span>
                </li>

                <li>"Canonical Subtype: " {move || format!("`{}`", subtype())}</li>
                <li>"Exact Subtype: " {move || format!("`{:?}`", subtype())}</li>
            </ul>
        </div>
    }
}

#[tracing::instrument]
#[component]
pub fn App() -> impl IntoView {
    let player_1_balance = ColorBalance::default();
    let player_1_deck = vec![];
    let player_1_guardians = vec![];
    let player_2_balance = ColorBalance {
        suffering: 3,
        divine: 3,
        ..Default::default()
    };
    let player_2_deck = vec![];
    let player_2_guardians = vec![];

    let player_decks: Vec<PlayerInitDescriptor> = vec![
        (player_1_balance, player_1_deck, player_1_guardians).into(),
        (player_2_balance, player_2_deck, player_2_guardians).into(),
    ];
    let player_decks_len = player_decks.len();
    let dem_res = create_local_resource(
        || (),
        |_| async {
            let response = gloo_net::http::Request::get("/api/v0/clicked")
                .send()
                .await
                .map_err(Rc::new)?;
            let json = response.json::<Vec<Box<str>>>().await.map_err(Rc::new)?;
            Ok::<_, Rc<gloo_net::Error>>(
                json.into_iter()
                    .map(|s| Rc::from(s) as Rc<str>)
                    .collect::<Vec<_>>(),
            )
        },
    );
    let is_dem_res_error = move || dem_res.with(|res| res.as_ref().is_some_and(|res| res.is_err()));
    let dem_res_data = move || {
        if dem_res.loading().get() {
            None
        } else {
            dem_res.with(|res| res.clone().and_then(|d| d.ok()))
        }
    };
    let dem_res_error = move || {
        if dem_res.loading().get() {
            None
        } else {
            dem_res.with(|res| res.clone().and_then(|d| d.err()))
        }
    };

    view! {
        <GameProvider player_decks=player_decks>
            <Router>
                <h1 class="font-bold text-4xl">Hello</h1>
                {(0..player_decks_len)
                    .map(|idx| view! { <PlayerDisplay index=idx/> })
                    .collect_view()}
                <SubtypePlayer/>
                <div class="flex w-1/2 items-center">
                    <Show
                        when=move || !is_dem_res_error() && !dem_res.loading().get()
                        fallback=move || {
                            view! {
                                <Show when=move || dem_res_error().is_some()>
                                    <div class="text-error grow">
                                        {move || format!("ERROR: {}", dem_res_error().unwrap())}
                                    </div>
                                </Show>
                            }
                        }
                    >

                        <div class="grow">{move || dem_res_data().unwrap().join(", ")}</div>
                    </Show>
                    <button class="btn join-item" on:click=move |_| dem_res.refetch()>
                        "Reload"
                    </button>
                </div>
            </Router>
        </GameProvider>
    }
}

#[derive(Debug, Clone, Copy)]
pub struct GameReader(pub(crate) ReadSignal<Result<Game, GameCreationError>>);

#[derive(Debug, Clone, Copy)]
pub struct GameWriter(pub(crate) WriteSignal<Result<Game, GameCreationError>>);

#[tracing::instrument(skip(children, player_decks))]
#[component]
fn GameProvider(
    #[prop(into)] player_decks: MaybeSignal<Vec<PlayerInitDescriptor>>,
    children: Children,
) -> impl IntoView {
    let (game, set_game) = create_signal(Game::new(player_decks.get()));

    provide_context(GameReader(game));
    provide_context(GameWriter(set_game));
    tracing::info!("logging something for game use...");

    children()
}