import type { TWsIn, TWsOut } from "@games/server/src/table";
import { fromPromise, Stream } from "kefir";
import {
	Accessor,
	createContext,
	createEffect,
	createSignal,
	For,
	onCleanup,
	Show,
} from "solid-js";
import api, { fromWebsocket } from "~/api";
import { createObservable, createObservableWithInit, cx } from "~/fn";
import { me, mePromise } from "~/profile";
import Game from "./Game";
import Player from "./Player";

export const TableContext = createContext<{
	view: Accessor<any>;
	sendWs: (msg: TWsIn) => void;
	wsEvents: Stream<TWsOut, any>;
}>();

export default (props: { tableKey: string }) => {
	const wsPromise = new Promise<
		ReturnType<ReturnType<typeof api.ws>["subscribe"]>
	>((res) => {
		const ws = api.ws(props).subscribe();
		ws.on("open", () => res(ws));
		ws.on("error", () => res(ws));
	});

	const sendWs = (msg: TWsIn) => wsPromise.then((ws) => ws.send(msg));
	const wsEvents = fromPromise(wsPromise).flatMap((ws) =>
		fromWebsocket<TWsOut>(ws)
	);
	onCleanup(() => wsPromise.then((ws) => ws.close()));

	const presenceEvents = wsEvents.filter((evt) => evt.players != null);
	const gameEvents = wsEvents.filter((evt) => evt.view !== undefined);

	const players = createObservableWithInit<string[]>(
		presenceEvents.map((evt) => evt.players!),
		[]
	);

	const [ready, setReady] = createSignal(false);
	mePromise.then(
		(me) =>
			me &&
			wsEvents
				.filter((evt) => evt.playersReady !== undefined)
				.map((evt) => evt.playersReady?.[me] ?? false)
				.onValue(setReady)
	);

	createEffect(() => sendWs({ ready: ready() }));
	const view = createObservable(gameEvents.map((evt) => evt.view));

	return (
		<TableContext.Provider
			value={{
				sendWs,
				wsEvents,
				view,
			}}
		>
			<div class="flex justify-around p-t-14">
				<For each={players().filter((p) => p != me())}>
					{(player, i) => {
						const verticalOffset = () => {
							const N = players().length - 1;
							const x = Math.abs((2 * i() + 1) / (N * 2) - 0.5);
							const y = Math.sqrt(1 - x * x);
							return 1 - y;
						};
						return (
							<Player
								playerKey={player}
								style={{
									transform: `translate(0, ${
										verticalOffset() * 150
									}vh)`,
								}}
							/>
						);
					}}
				</For>
			</div>
			<div
				id="table"
				class={cx(
					"fixed",

					"bg-radial",
					"from-orange-950",
					"to-stone-950",

					"border-4",
					"border-neutral-950",
					"shadow-lg",

					"top-40",
					"bottom-20",
					"left-10",
					"right-10"
				)}
				style={{
					"border-radius": "50%",
				}}
			>
				<Show when={view() == null}>
					<div class="absolute center">
						<button
							onClick={() => setReady((prev) => !prev)}
							class="button p-1 "
						>
							{ready() ? "Not Ready" : "Ready"}
						</button>
					</div>
				</Show>
			</div>
			<Show when={view() != null}>
				<Game />
			</Show>
		</TableContext.Provider>
	);
};
