import MainControl from "@/Common/MainControl/MainControl";
import CSMessage from "@/Common/Message/CSMessage";
import { CoroutineV2 } from "@/Engine/CatanEngine/CoroutineV2/CoroutineV2";
import { INetResponse } from "@/Engine/CatanEngine/NetManagerV2/Core/INetResponse";
import { NetConnector } from "@/Engine/CatanEngine/NetManagerV2/NetConnector";
import { NetManagerSD } from "@/Engine/CatanEngine/NetManagerV2/NetManagerSD";
import CSSettingsSDV3 from "@/FormTableSD/CSSettingsSDV3";
import { SlotsetTableRow } from "@/FormTableSD/Tables/SlotsetTable";
import { gameObj, useGameItems } from "@/context/GameItemsContext";
import { SDAccountLoginRequest } from "@/define/Request/AccountRequest";
import { SlotInRequest } from "@/define/Request/SlotRequest";
import GameManager from "@/modules/GameManager";
import WheelSelect from "@/components/WheelSelect";
import { Button, InputNumber, Switch } from "antd";
import dayjs from "dayjs";
import React, { useEffect, useState } from "react";

const SDGame = (props: ISDGame) => {
	const { gameUrl, onClickSlotOut } = props;
	const { gameData } = useGameItems();
	const { nowSlotId } = gameData;
	const [isOK, setIsOK] = useState<boolean>(false);
	const [isSpin, setIsSpin] = useState<boolean>(false);
	const [betOptions, setBetOptions] = useState<number[]>([]);
	const [bet, setBet] = useState<number>(0);
	const [delay, setDelay] = useState<number>(0.1);
	const [isRatioStop, setIsRatioStop] = useState<boolean>(false);
	const [ratioStop, setRatioStop] = useState<number>(200);
	const [isCountStop, setIsCountStop] = useState<boolean>(false);
	const [countStop, setCountStop] = useState<number>(200);
	const [log, setLog] = useState<string[]>([]);
	let conn: NetConnector = undefined;

	function* onLoad(): IterableIterator<any> {
		reset();
		const url: URL = new URL(gameUrl);
		const queryParameters: URLSearchParams = new URLSearchParams(url.search);
		const token: string = queryParameters.get("token");
		const slotid: number = +queryParameters.get("slotid");
		let host: string = queryParameters.get("host");
		const port: number = 9005;

		// NetConnector 對沒帶協定的 host 會用「目前頁面」的協定判斷 ws/wss，
		// bot 頁面是 http 會誤判成 ws://；改依遊戲網址本身的協定，https 就強制 wss
		if (url.protocol === "https:" && !/^https?:\/\//.test(host)) {
			host = `https://${host}`;
		}

		GameManager.SlotData.SlotId = slotid;
		GameManager.SlotData.IsRatioStop = isRatioStop;
		GameManager.SlotData.RatioStop = ratioStop;
		GameManager.SlotData.IsCountStop = isCountStop;
		GameManager.SlotData.CountStop = countStop;
		conn = new NetConnector(host, port);
		conn.OnDataReceived.AddCallback(onNetDataReceived);
		conn.OnDisconnected.AddCallback(onNetDisconnected);
		loadJSON(slotid);
		setSlotClass();
		NetManagerSD.Initialize(conn);
		console.log("[SDsocket] connecting...");
		AddLog(`連線 SD socket: ${host}:${port}`);
		yield NetManagerSD.ConnectAsync();
		AddLog("SD socket 已連線");
		yield* login(token);
		yield* slotIn();
	}

	function onClose(): void {
		CoroutineV2.StopCoroutinesBy("Spin");
		try {
			if (conn && conn.IsConnected) {
				conn.Disconnect();
			}
		} catch (e) {
			//
		}
		reset();
	}

	function reset(): void {
		GameManager.SlotData.SlotId = undefined;
		GameManager.SlotData.SlotClass = undefined;
		GameManager.SlotData.IsSpin = undefined;
		GameManager.SlotData.NowBet = undefined;
		GameManager.SlotData.IsRatioStop = undefined;
		GameManager.SlotData.RatioStop = undefined;
		GameManager.SlotData.IsCountStop = undefined;
		GameManager.SlotData.CountStop = undefined;
	}

	function* login(token: string): IterableIterator<any> {
		const req = new SDAccountLoginRequest(token);
		yield req.SendAsync(true);
		const resp = req.Result;
		if (!resp.IsValid) {
			CSMessage.NetError(resp.Method, resp.Status, "SD Account Login Fail");
			AddLog(`SD 登入失敗 [${resp.Method}] status:${resp.Status}`);
			return;
		}
		AddLog("SD 登入成功");
	}

	function* slotIn(): IterableIterator<any> {
		let req: SlotInRequest = new SlotInRequest(GameManager.SlotData.SlotId);
		yield req.SendAsync(true);
		let resp: INetResponse<JSON> = req.Result;
		if (resp.IsValid) {
			const br: number[] = resp.Data["br"];
			const db: number = resp.Data["db"];
			setBetOptions(br);
			setBet(br[db]);
			GameManager.SlotData.NowBet = br[db];
			setIsOK(true);
			AddLog(`機台就緒 押注選項: [${br.join(", ")}]`);
		} else {
			AddLog(`SlotIn 失敗 [${resp.Method}] status:${resp.Status}`);
		}
	}

	async function loadJSON(slotid: number) {
		let slotset: SlotsetTableRow = CSSettingsSDV3.Slotset[slotid];
		let formName: string[] = slotset.FormName;
		let parallel: Promise<void>[] = [];
		for (let i: number = 0; i < formName.length; i++) {
			await MainControl.DownloadFormSetting(formName[i]);
		}
		// set Form
		await Promise.all(parallel);
	}

	async function setSlotClass() {
		let slot: any;
		const slotGroup: typeof import("../../define/Game/Base/Slot") = await import(/* @vite-ignore */`../../define/Game/Base/Slot`);
		try {
			slot = slotGroup[`Slot${GameManager.SlotData.SlotId}`];
		} catch (error) {
			//
		}
		if (!slot) {
			slot = slotGroup.SlotBase;
		}
		GameManager.SlotData.SlotClass = new slot(GameManager.SlotData.SlotId, AddLog);
	}

	async function OnClickSpin() {
		GameManager.SlotData.IsSpin = true;
		setIsSpin(true);
		CoroutineV2.Single(spin()).Start("Spin");
	}

	function OnClickStop() {
		GameManager.SlotData.IsSpin = false;
		setIsSpin(false);
	}

	function* spin(): IterableIterator<any> {
		const { player } = gameObj;
		const { m: money } = player;
		if (money < GameManager.SlotData.NowBet) {
			noMoney();
			OnClickStop();
			return;
		}
		if (isCountStop) {
			if (GameManager.SlotData.CountStop <= 0) {
				OnClickStop();
				return;
			}
			GameManager.SlotData.CountStop--;
			setCountStop((v) => v - 1);
		}
		yield* GameManager.SlotData.SlotClass.Spin(bet, OnClickStop);
		yield CoroutineV2.WaitTime(delay);
		if (GameManager.SlotData.IsSpin) {
			yield* spin();
		} else {
			GameManager.SlotData.IsSpin = false;
			setIsSpin(false);
		}
	}

	function noMoney(): void {
		const { player } = gameObj;
		const { m: money } = player;
		GameManager.SlotData.IsSpin = false;
		AddLog(`金額不足: ${money}`);
	}

	function AddLog(s: string) {
		const todayTimeStr: string = dayjs().format("YYYY/MM/DD HH:mm:ss");
		setLog((v) => {
			const next: string[] = [`${todayTimeStr}  ${s}`, ...v];
			return next.length > 100 ? next.slice(0, 100) : next;
		});
	}

	/**RPC回傳.若協定錯誤斷線.原因也會在這裡收到 */
	function onNetDataReceived(resp: INetResponse<any>) {
		// MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.ServerData, resp]);
	}

	/**只要連線中斷不管主被動都會走到這裡 */
	function onNetDisconnected() {
		console.warn("[socket] Disconnected");
		conn.OnDataReceived.RemoveAllCallbacks();
		// MainControl.DataReceivedEvent.DispatchCallback([MainControl.DataType.NetDisconnected]);
	}

	useEffect(() => {
		CoroutineV2.Single(onLoad()).Start();

		return onClose;
	}, []);

	return (
		<div style={layoutStyle} className="lp-rise">
			{/* 左側：控制面板 */}
			<div className="lp-panel lp-corners" style={controlPanelStyle}>
				<div style={{ marginBottom: 18 }}>
					<div className="lp-kicker" style={{ marginBottom: 6 }}>SD Machine</div>
					<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
						<h2 className="lp-title" style={{ fontSize: 24 }}>SLOT <span className="lp-mono" style={{ color: "var(--lp-amber)" }}>#{nowSlotId}</span></h2>
						<span className={`lp-dot ${isSpin ? "lp-dot--red" : isOK ? "" : "lp-dot--amber"}`} />
						<span className="lp-label">{isSpin ? "spinning" : isOK ? "ready" : "loading"}</span>
					</div>
				</div>

				{isOK && <>
					<div style={rowStyle}>
						<span className="lp-label" style={rowLabelStyle}>押注</span>
						<WheelSelect
							wrapperStyle={{ flex: 1 }}
							style={{ width: "100%" }}
							value={bet}
							disabled={isSpin}
							options={betOptions.map((v: number) => ({ value: v, label: v.toLocaleString() }))}
							onChange={(v: number) => { setBet(v); GameManager.SlotData.NowBet = v; }}
						/>
					</div>

					<div style={rowStyle}>
						<span className="lp-label" style={rowLabelStyle}>延遲</span>
						<InputNumber
							style={{ flex: 1 }}
							min={0}
							step={0.1}
							value={delay}
							onChange={(v: number) => setDelay(+v || 0)}
							addonAfter="秒"
						/>
					</div>

					<div style={rowStyle}>
						<span className="lp-label" style={rowLabelStyle}>倍率停</span>
						<Switch
							checked={isRatioStop}
							onChange={(checked: boolean) => { setIsRatioStop(checked); GameManager.SlotData.IsRatioStop = checked; }}
						/>
						{isRatioStop &&
							<InputNumber
								style={{ flex: 1 }}
								min={0}
								value={ratioStop}
								onChange={(v: number) => { const n = +v || 0; setRatioStop(n); GameManager.SlotData.RatioStop = n; }}
								addonAfter="倍"
							/>
						}
					</div>

					<div style={rowStyle}>
						<span className="lp-label" style={rowLabelStyle}>轉數停</span>
						<Switch
							checked={isCountStop}
							onChange={(checked: boolean) => { setIsCountStop(checked); GameManager.SlotData.IsCountStop = checked; }}
						/>
						{isCountStop &&
							<InputNumber
								style={{ flex: 1 }}
								min={0}
								value={countStop}
								onChange={(v: number) => { const n = +v || 0; setCountStop(n); GameManager.SlotData.CountStop = n; }}
								addonAfter="轉"
							/>
						}
					</div>

					<div style={{ marginTop: "auto", paddingTop: 20 }}>
						{isSpin
							? <button className="lp-spin-btn lp-spin-btn--stop" onClick={OnClickStop}>STOP</button>
							: <button className="lp-spin-btn" onClick={OnClickSpin}>SPIN</button>
						}
						<Button danger block onClick={onClickSlotOut} style={{ marginTop: 12 }}>
							離開機台
						</Button>
					</div>
				</>}

				{!isOK &&
					<div style={{ marginTop: "auto" }}>
						<Button danger block onClick={onClickSlotOut}>離開機台</Button>
					</div>
				}
			</div>

			{/* 右側：Log 終端 */}
			<div style={logPanelStyle}>
				<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }}>
					<span className="lp-kicker">Log</span>
					<span className="lp-label">{log.length} 筆</span>
				</div>
				<div className="lp-log">
					{log.length === 0 && <p className="lp-cursor" style={{ color: "var(--lp-text-dim)" }}>等待紀錄</p>}
					{log.map((s: string, index: number) => <p key={index}>{s}</p>)}
				</div>
			</div>
		</div>
	);
};

export default SDGame;

interface ISDGame {
	gameUrl: string;
	onClickSlotOut: () => void;
}

const layoutStyle: React.CSSProperties = {
	flex: 1,
	minHeight: 0,
	display: "flex",
	gap: 16,
	alignItems: "stretch",
};

const controlPanelStyle: React.CSSProperties = {
	width: 340,
	flexShrink: 0,
	padding: "24px 24px 20px",
	display: "flex",
	flexDirection: "column",
	overflowY: "auto",
};

const rowStyle: React.CSSProperties = {
	display: "flex",
	alignItems: "center",
	gap: 12,
	marginBottom: 16,
};

const rowLabelStyle: React.CSSProperties = {
	width: 52,
	flexShrink: 0,
};

const logPanelStyle: React.CSSProperties = {
	flex: 1,
	minWidth: 0,
	display: "flex",
	flexDirection: "column",
};
