use log::error;
use crate::routing::Route;
use gloo_net::http::Request;
use log::info;
use protocol::UserInfo;
use uuid::Uuid;
use std::future::Future;
use wasm_bindgen_futures::spawn_local;
use yew::prelude::*;
use yew_router::prelude::*;
#[derive(Properties, Clone, PartialEq, Debug)]
pub struct ErrorInfoProperties {
    pub message: String,
}
#[derive(Clone, PartialEq)]
pub struct AppState {
    user: UseStateHandle>,
    error: UseStateHandle >,
}
#[derive(Clone, PartialEq)]
pub struct AppContext {
    state: AppState,
    history: AnyHistory,
}
impl AppContext {
    pub fn spawn_async(&self, f: F)
    where
        F: Future> + 'static,
    {
        let error = self.state.error.clone();
        spawn_local(async move {
            if let Err(err) = f.await {
		error!("Error occurred: {err:?}");
                error.set(Some(ErrorInfoProperties {
                    message: format!("Some error occured: {:?}", err),
                }));
            }
        });
    }
    pub fn user(&self) -> Option<&UserInfo> {
        self.state.user.as_ref()
    }
    pub fn error(&self) -> Option<&ErrorInfoProperties> {
        self.state.error.as_ref()
    }
    pub fn create_table(&self) {
	let user = self.state.user.clone();
	let history = self.history.clone();
	self.spawn_async(async move {
	    let response = Request::post("/api/table").send().await?;
            let table_id: Uuid = response.json().await?;
            info!("Created table {table_id}");
	    if let Some(user_info) = user.as_ref() {
		user.set(Some(UserInfo {
		    table: Some(protocol::Table { id: table_id }),
		    ..(user_info.clone())
		}));
	    }
	    history.push(Route::Home);
	    Ok(())
	});
    }
    pub fn leave_table(&self) {
	let user = self.state.user.clone();
	let history = self.history.clone();
	self.spawn_async(async move {
	    let response = Request::delete("/api/table").send().await?;
	    if !response.ok() {
		anyhow::bail!("error while leaving table");
	    }
	    if let Some(user_info) = user.as_ref() {
		user.set(Some(UserInfo {
		    table: None,
		    ..(user_info.clone())
		}));
	    }
	    history.push(Route::Home);
	    Ok(())
	});
    }
}
#[derive(Properties, Clone, PartialEq)]
pub struct Props {
    pub children: Children,
}
async fn initialize_user_info() -> Result {
    let response = Request::get("/api/user/info").send().await?;
    if response.status() == 401 {
        web_sys::window()
            .unwrap()
            .location()
            .assign("/api/login")
            .unwrap();
    };
    let user_info = response.json().await?;
    Ok(user_info)
}
pub fn use_app_context() -> AppContext {
    let state : AppState = use_context::().unwrap();
    let history = use_history().unwrap();
    AppContext { state, history }
}
#[function_component(AppContextProvider)]
pub fn app_context_provider(props: &Props) -> Html {
    let user: UseStateHandle> = use_state(|| None);
    let error: UseStateHandle > = use_state(|| None);
    {
        let user = user.clone();
        let error = error.clone();
        use_effect_with_deps(
            move |_| {
                spawn_local(async move {
                    match initialize_user_info().await {
                        Ok(user_info) => user.set(Some(user_info)),
                        Err(e) => error.set(Some(ErrorInfoProperties {
                            message: format!("Could not contact server: {:?}", e),
                        })),
                    };
                });
                || ()
            },
            (),
        );
    }
    if user.is_none() && error.is_none() {
        return html! {
                { "Loading app..." }
        };
    }
    info!("Recomputing state");
    info!("User is {:?}", *user);
    let state = AppState {
        user,
        error,
    };
    html! {
             context={state}>
                { for props.children.iter() }
             >
    }
}