import { Form, Link, useFetcher, useLoaderData } from "@remix-run/react";
import clsx from "clsx";
import Compressor from "compressorjs";
import Markdown from "markdown-to-jsx";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { useCopyToClipboard } from "react-use";
import { Alert } from "~/components/Alert";
import { Avatar } from "~/components/Avatar";
import { Divider } from "~/components/Divider";
import { LinkButton, SendouButton } from "~/components/elements/Button";
import { SendouPopover } from "~/components/elements/Popover";
import {
	SendouTab,
	SendouTabList,
	SendouTabPanel,
	SendouTabs,
} from "~/components/elements/Tabs";
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { FriendCodeInput } from "~/components/FriendCodeInput";
import { Image, ModeImage } from "~/components/Image";
import { Input } from "~/components/Input";
import { CheckmarkIcon } from "~/components/icons/Checkmark";
import { ClockIcon } from "~/components/icons/Clock";
import { CrossIcon } from "~/components/icons/Cross";
import { DiscordIcon } from "~/components/icons/Discord";
import { TrashIcon } from "~/components/icons/Trash";
import { UserIcon } from "~/components/icons/User";
import { Label } from "~/components/Label";
import { containerClassName } from "~/components/Main";
import { MapPoolStages } from "~/components/MapPoolSelector";
import { Section } from "~/components/Section";
import { SubmitButton } from "~/components/SubmitButton";
import TimePopover from "~/components/TimePopover";
import { useUser } from "~/features/auth/core/user";
import { imgTypeToDimensions } from "~/features/img-upload/upload-constants";
import { MapPool } from "~/features/map-list-generator/core/map-pool";
import { ModeMapPoolPicker } from "~/features/sendouq-settings/components/ModeMapPoolPicker";
import type { TournamentDataTeam } from "~/features/tournament-bracket/core/Tournament.server";
import { useAutoRerender } from "~/hooks/useAutoRerender";
import { useIsMounted } from "~/hooks/useIsMounted";
import { useSearchParamState } from "~/hooks/useSearchParamState";
import { modesShort, rankedModesShort } from "~/modules/in-game-lists/modes";
import invariant from "~/utils/invariant";
import { logger } from "~/utils/logger";
import {
	LOG_IN_URL,
	mapsPageWithMapPool,
	navIconUrl,
	SENDOU_INK_BASE_URL,
	SENDOU_INK_DISCORD_URL,
	tournamentJoinPage,
	tournamentOrganizationPage,
	tournamentSubsPage,
	userEditProfilePage,
	userPage,
} from "~/utils/urls";
import { AlertIcon } from "../../../components/icons/Alert";
import { action } from "../actions/to.$id.register.server";
import type { TournamentRegisterPageLoader } from "../loaders/to.$id.register.server";
import { loader } from "../loaders/to.$id.register.server";
import { TOURNAMENT } from "../tournament-constants";
import {
	type CounterPickValidationStatus,
	validateCounterPickMapPool,
} from "../tournament-utils";
import { useTournament } from "./to.$id";
export { loader, action };

export default function TournamentRegisterPage() {
	const isMounted = useIsMounted();
	const tournament = useTournament();

	const startsAtEvenHour = tournament.ctx.startTime.getMinutes() === 0;

	return (
		<div className={clsx("stack lg", containerClassName("normal"))}>
			<div className="tournament__logo-container">
				<img
					src={tournament.ctx.logoUrl}
					alt=""
					className="tournament__logo"
					width={124}
					height={124}
				/>
				<div>
					<div className="tournament__title">{tournament.ctx.name}</div>
					<div>
						{tournament.ctx.organization ? (
							<Link
								to={tournamentOrganizationPage({
									organizationSlug: tournament.ctx.organization.slug,
									tournamentName: tournament.ctx.name,
								})}
								className="stack horizontal sm items-center text-xs text-main-forced"
							>
								<Avatar
									url={tournament.ctx.organization.avatarUrl ?? undefined}
									size="xxs"
								/>
								{tournament.ctx.organization.name}
							</Link>
						) : (
							<Link
								to={userPage(tournament.ctx.author)}
								className="stack horizontal xs items-center text-lighter"
							>
								<UserIcon className="tournament__info__icon" />{" "}
								{tournament.ctx.author.username}
							</Link>
						)}
					</div>
					{!tournament.isLeagueSignup ? (
						<div className="tournament__by mt-2">
							<div className="stack horizontal xs items-center">
								<ClockIcon className="tournament__info__icon" />{" "}
								{isMounted ? (
									<TimePopover
										time={tournament.ctx.startTime}
										options={{
											minute: startsAtEvenHour ? undefined : "numeric",
											hour: "numeric",
											day: "numeric",
											month: "long",
										}}
									/>
								) : null}
							</div>
						</div>
					) : null}
					<div className="stack horizontal sm mt-1">
						{tournament.ranked ? (
							<div className="tournament__badge tournament__badge__ranked">
								Ranked
							</div>
						) : (
							<div className="tournament__badge tournament__badge__unranked">
								Unranked
							</div>
						)}
						<div className="tournament__badge tournament__badge__modes">
							{tournament.modesIncluded.map((mode) => (
								<ModeImage key={mode} mode={mode} size={16} />
							))}
						</div>
					</div>
				</div>
			</div>
			<TournamentRegisterInfoTabs />
		</div>
	);
}

const TABS = ["description", "rules", "register"] as const;
type RegisterPageTab = (typeof TABS)[number];

function TournamentRegisterInfoTabs() {
	const user = useUser();
	const tournament = useTournament();
	const { t } = useTranslation(["tournament"]);

	const teamMemberOf = tournament.teamMemberOfByUser(user);
	const teamOwned = tournament.ownedTeamByUser(user);
	const isRegularMemberOfATeam = teamMemberOf && !teamOwned;

	const defaultTab = (): RegisterPageTab => {
		if (tournament.hasStarted || !teamOwned) return "description";

		return "register";
	};
	const [tabKey, setTabKey] = useSearchParamState({
		defaultValue: defaultTab(),
		name: "tab",
		revive: (val) =>
			TABS.includes(val as RegisterPageTab) ? (val as RegisterPageTab) : null,
	});

	const showAddIGNAlert =
		tournament.ctx.settings.requireInGameNames &&
		!teamOwned &&
		user &&
		!user?.inGameName;

	return (
		<div>
			<SendouTabs
				selectedKey={tabKey}
				onSelectionChange={(key) => setTabKey(key as RegisterPageTab)}
			>
				<SendouTabList sticky>
					<SendouTab id="description">Description</SendouTab>
					{tournament.ctx.rules ? (
						<SendouTab id="rules">Rules</SendouTab>
					) : null}
					{!tournament.hasStarted ? (
						<SendouTab id="register" data-testid="register-tab">
							Register
						</SendouTab>
					) : null}
				</SendouTabList>

				<SendouTabPanel id="description">
					<div className="stack lg">
						{tournament.ctx.discordUrl ? (
							<div className="w-max">
								<LinkButton
									to={tournament.ctx.discordUrl}
									variant="outlined"
									size="small"
									isExternal
									icon={<DiscordIcon />}
								>
									Join the Discord
								</LinkButton>
							</div>
						) : null}

						<div className="tournament__info__description">
							<Markdown options={{ wrapper: React.Fragment }}>
								{tournament.ctx.description ?? ""}
							</Markdown>
						</div>
						<TOPickedMapPoolInfo />
						<TiebreakerMapPoolInfo />
					</div>
				</SendouTabPanel>

				{tournament.ctx.rules ? (
					<SendouTabPanel id="rules">
						<div className="tournament__info__description">
							<Markdown options={{ wrapper: React.Fragment }}>
								{tournament.ctx.rules ?? ""}
							</Markdown>
						</div>
					</SendouTabPanel>
				) : null}

				{!tournament.hasStarted ? (
					<SendouTabPanel id="register">
						<div className="stack lg">
							{isRegularMemberOfATeam ? (
								<div className="stack md items-center">
									<Alert>{t("tournament:pre.inATeam")}</Alert>
									{teamMemberOf && teamMemberOf.checkIns.length === 0 ? (
										<FormWithConfirm
											dialogHeading={`Leave "${tournament.teamMemberOfByUser(user)?.name}"?`}
											fields={[["_action", "LEAVE_TEAM"]]}
											submitButtonText="Leave"
										>
											<SendouButton
												className="small-text"
												variant="minimal-destructive"
												type="submit"
											>
												Leave the team
											</SendouButton>
										</FormWithConfirm>
									) : null}
								</div>
							) : showAddIGNAlert ? (
								<div>
									<Alert variation="WARNING">
										<div className="stack horizontal sm items-center flex-wrap justify-center text-center">
											This tournament requires you to have an in-game name set{" "}
											<LinkButton to={userEditProfilePage(user)} size="small">
												Edit profile
											</LinkButton>
										</div>
									</Alert>
								</div>
							) : (
								<RegistrationForms />
							)}
							{user &&
							!tournament.teamMemberOfByUser(user) &&
							tournament.canAddNewSubPost &&
							!showAddIGNAlert &&
							!tournament.hasStarted ? (
								<Link
									to={tournamentSubsPage(tournament.ctx.id)}
									className="text-xs text-center"
								>
									{t("tournament:pre.sub.prompt")}
								</Link>
							) : null}
						</div>
					</SendouTabPanel>
				) : null}
			</SendouTabs>
		</div>
	);
}

function PleaseLogIn() {
	const { t } = useTranslation(["tournament"]);

	return (
		<form className="stack items-center mt-4" action={LOG_IN_URL} method="post">
			<SendouButton size="big" type="submit">
				{t("tournament:pre.logIn")}
			</SendouButton>
		</form>
	);
}

function RegistrationForms() {
	const data = useLoaderData<TournamentRegisterPageLoader>();
	const user = useUser();
	const tournament = useTournament();

	const ownTeam = tournament.ownedTeamByUser(user);
	const ownTeamCheckedIn = Boolean(ownTeam && ownTeam.checkIns.length > 0);

	if (!user && !tournament.isInvitational) {
		return <PleaseLogIn />;
	}

	const showRegistrationProgress = () => {
		if (ownTeam) return true;

		return !tournament.isInvitational;
	};

	const showRegisterNewTeam = () => {
		if (ownTeam) return true;
		if (tournament.isInvitational) return false;
		if (!tournament.registrationOpen) return false;

		return !tournament.regularCheckInHasEnded;
	};

	return (
		<div className="stack lg">
			{showRegistrationProgress() ? (
				<RegistrationProgress
					checkedIn={ownTeamCheckedIn}
					name={ownTeam?.name}
					mapPool={data?.mapPool ?? undefined}
					members={ownTeam?.members}
				/>
			) : (
				<Alert>
					This tournament is invitational. Tournament organizer adds all teams.
				</Alert>
			)}
			{showRegisterNewTeam() ? (
				<>
					<FriendCode />
					{user?.friendCode ? (
						<TeamInfo
							ownTeam={ownTeam}
							canUnregister={Boolean(ownTeam && !ownTeamCheckedIn)}
						/>
					) : null}
				</>
			) : null}
			{tournament.isLeagueSignup ? <GoogleFormsLink /> : null}
			{ownTeam ? (
				<>
					<FillRoster ownTeam={ownTeam} ownTeamCheckedIn={ownTeamCheckedIn} />
					{tournament.teamsPrePickMaps ? <CounterPickMapPoolPicker /> : null}
				</>
			) : null}
		</div>
	);
}

function RegistrationProgress({
	checkedIn,
	name,
	members,
	mapPool,
}: {
	checkedIn?: boolean;
	name?: string;
	members?: unknown[];
	mapPool?: unknown[];
}) {
	const { i18n, t } = useTranslation(["tournament"]);
	const tournament = useTournament();
	const isMounted = useIsMounted();

	const completedIfTruthy = (condition: unknown) =>
		condition ? "completed" : "incomplete";

	const steps = [
		{
			name: t("tournament:pre.steps.name"),
			status: completedIfTruthy(name),
		},
		{
			name: t("tournament:pre.steps.roster"),
			status: completedIfTruthy(
				members && members.length >= tournament.minMembersPerTeam,
			),
		},
		tournament.teamsPrePickMaps
			? {
					name: t("tournament:pre.steps.pool"),
					status: completedIfTruthy(mapPool && mapPool.length > 0),
				}
			: null,
		!tournament.isLeagueSignup
			? {
					name: t("tournament:pre.steps.check-in"),
					status: completedIfTruthy(checkedIn),
				}
			: null,
		tournament.isLeagueSignup
			? {
					name: "Google Sheet",
					status: "notice" as const,
				}
			: null,
	].filter((step) => step !== null);

	const regClosesBeforeStart =
		tournament.registrationClosesAt.getTime() !==
		tournament.ctx.startTime.getTime();

	const registrationClosesAtString = isMounted
		? (tournament.isLeagueSignup
				? tournament.ctx.startTime
				: tournament.registrationClosesAt
			).toLocaleTimeString(i18n.language, {
				minute: "numeric",
				hour: "numeric",
				day: "2-digit",
				month: "2-digit",
			})
		: "";

	return (
		<div>
			<h3 className="tournament__section-header text-center">
				{t("tournament:pre.steps.header")}
			</h3>
			<section className="tournament__section stack md">
				<div className="stack horizontal lg justify-center text-sm font-semi-bold">
					{steps.map((step, i) => {
						return (
							<div
								key={step.name}
								className="stack sm items-center text-center"
							>
								{step.name}
								{step.status === "completed" ? (
									<CheckmarkIcon
										className="tournament__section__icon fill-success"
										testId={`checkmark-icon-num-${i + 1}`}
									/>
								) : step.status === "notice" ? (
									<AlertIcon className="tournament__section__icon fill-info p-1" />
								) : (
									<CrossIcon className="tournament__section__icon fill-error" />
								)}
							</div>
						);
					})}
				</div>
				{!tournament.isLeagueSignup ? (
					<CheckIn
						canCheckIn={
							steps.filter((step) => step.status === "incomplete").length === 1
						}
						status={
							tournament.regularCheckInIsOpen
								? "OPEN"
								: tournament.regularCheckInHasEnded
									? "OVER"
									: "UPCOMING"
						}
						startDate={tournament.regularCheckInStartsAt}
						endDate={tournament.regularCheckInEndsAt}
						checkedIn={checkedIn}
					/>
				) : null}
			</section>
			<div className="tournament__section__warning">
				{regClosesBeforeStart || tournament.isLeagueSignup ? (
					<span className="text-warning">
						Registration closes at {registrationClosesAtString}
					</span>
				) : (
					t("tournament:pre.footer")
				)}
			</div>
		</div>
	);
}

function CheckIn({
	status,
	canCheckIn,
	startDate,
	endDate,
	checkedIn,
}: {
	status: "OVER" | "OPEN" | "UPCOMING";
	canCheckIn: boolean;
	startDate: Date;
	endDate: Date;
	checkedIn?: boolean;
}) {
	const { t, i18n } = useTranslation(["tournament"]);
	const isMounted = useIsMounted();
	const fetcher = useFetcher();

	useAutoRerender();

	const checkInStartsString = isMounted
		? startDate.toLocaleTimeString(i18n.language, {
				minute: "numeric",
				hour: "numeric",
				day: "2-digit",
				month: "2-digit",
			})
		: "";

	const checkInEndsString = isMounted
		? endDate.toLocaleTimeString(i18n.language, {
				minute: "numeric",
				hour: "numeric",
				day: "2-digit",
				month: "2-digit",
			})
		: "";

	if (status === "UPCOMING") {
		return (
			<div className={clsx("text-center text-xs", { invisible: !isMounted })}>
				{t("tournament:pre.checkIn.range", {
					start: checkInStartsString,
					finish: checkInEndsString,
				})}
			</div>
		);
	}

	if (checkedIn) {
		return (
			<div className="text-center text-xs">
				{t("tournament:pre.checkIn.checkedIn")}
			</div>
		);
	}

	if (status === "OVER") {
		return (
			<div className="text-center text-xs">
				{t("tournament:pre.checkIn.over")}
			</div>
		);
	}

	if (!canCheckIn) {
		return (
			<div className="stack items-center">
				<SendouPopover
					trigger={
						<SendouButton size="small">
							{t("tournament:pre.checkIn.button")}
						</SendouButton>
					}
				>
					{t("tournament:pre.checkIn.cant")}
				</SendouPopover>
			</div>
		);
	}

	return (
		<fetcher.Form method="post" className="stack items-center">
			<SubmitButton
				size="small"
				_action="CHECK_IN"
				state={fetcher.state}
				testId="check-in-button"
			>
				{t("tournament:pre.checkIn.button")}
			</SubmitButton>
		</fetcher.Form>
	);
}

function TeamInfo({
	ownTeam,
	canUnregister,
}: {
	ownTeam?: TournamentDataTeam | null;
	canUnregister: boolean;
}) {
	const data = useLoaderData<TournamentRegisterPageLoader>();
	const { t } = useTranslation(["tournament", "common"]);
	const fetcher = useFetcher();
	const tournament = useTournament();
	const [teamName, setTeamName] = React.useState(ownTeam?.name ?? "");
	const user = useUser();
	const ref = React.useRef<HTMLFormElement>(null);
	const [signUpWithTeamId, setSignUpWithTeamId] = React.useState(
		() => tournament.ownedTeamByUser(user)?.team?.id ?? null,
	);
	const [uploadedAvatar, setUploadedAvatar] = React.useState<File | null>(null);

	const handleSignUpWithTeamChange = (teamId: number | null) => {
		if (!teamId) {
			setSignUpWithTeamId(null);
		} else {
			setSignUpWithTeamId(teamId);
			const teamName = data?.teams.find((team) => team.id === teamId)?.name;
			invariant(teamName, "team name should exist");

			setTeamName(teamName);
		}
	};

	const handleSubmit = () => {
		const formData = new FormData(ref.current!);

		if (uploadedAvatar) {
			// replace with the compressed version
			formData.delete("img");
			formData.append("img", uploadedAvatar, uploadedAvatar.name);
		}

		fetcher.submit(formData, {
			encType: uploadedAvatar ? "multipart/form-data" : undefined,
			method: "post",
		});
	};

	const submitButtonDisabled = () => {
		if (fetcher.state !== "idle") return true;

		return false;
	};

	const avatarUrl = (() => {
		if (signUpWithTeamId) {
			const teamToSignUpWith = data?.teams.find(
				(team) => team.id === signUpWithTeamId,
			);
			return teamToSignUpWith?.logoUrl;
		}
		if (uploadedAvatar) return URL.createObjectURL(uploadedAvatar);

		return ownTeam?.pickupAvatarUrl;
	})();

	const canEditAvatar =
		tournament.registrationOpen &&
		!signUpWithTeamId &&
		uploadedAvatar &&
		!ownTeam?.pickupAvatarUrl;

	const canDeleteAvatar = ownTeam?.pickupAvatarUrl;

	return (
		<div>
			<div className="stack horizontal justify-between">
				<h3 className="tournament__section-header">
					2. {t("tournament:pre.info.header")}
				</h3>
				{canUnregister &&
				tournament.isLeagueSignup &&
				!tournament.registrationOpen ? (
					<SendouPopover
						trigger={
							<SendouButton
								size="small"
								variant="minimal-destructive"
								className="small-text"
							>
								{t("tournament:pre.info.unregister")}
							</SendouButton>
						}
					>
						Unregistration from a league after the registration has ended is
						handled by the organizers
					</SendouPopover>
				) : canUnregister ? (
					<FormWithConfirm
						dialogHeading={t("tournament:pre.info.unregister.confirm")}
						submitButtonText={t("tournament:pre.info.unregister")}
						fields={[["_action", "UNREGISTER"]]}
					>
						<SendouButton
							className="small-text"
							variant="minimal-destructive"
							size="small"
						>
							{t("tournament:pre.info.unregister")}
						</SendouButton>
					</FormWithConfirm>
				) : null}
			</div>
			<section className="tournament__section">
				<Form method="post" className="stack md items-center" ref={ref}>
					<input type="hidden" name="_action" value="UPSERT_TEAM" />
					{signUpWithTeamId ? (
						<input type="hidden" name="teamId" value={signUpWithTeamId} />
					) : null}
					<div className="stack sm-plus items-center">
						{data && data.teams.length > 0 && tournament.registrationOpen ? (
							<div className="tournament__section__input-container">
								<Label htmlFor="signingUpAs">Team signing up as</Label>
								<select
									id="signingUpAs"
									onChange={(e) => {
										if (e.target.value === "") {
											handleSignUpWithTeamChange(null);
										} else {
											handleSignUpWithTeamChange(Number(e.target.value));
										}
									}}
								>
									<option value="">Sign up with pick-up</option>
									{data.teams.map((team) => {
										return (
											<option key={team.id} value={team.id}>
												{team.name}
											</option>
										);
									})}
								</select>
							</div>
						) : null}

						{!signUpWithTeamId ? (
							<div className="tournament__section__input-container">
								<Label htmlFor="teamName">
									{data && data.teams.length > 0
										? "Pick-up name"
										: t("tournament:pre.steps.name")}
								</Label>
								<Input
									name="teamName"
									id="teamName"
									required
									maxLength={TOURNAMENT.TEAM_NAME_MAX_LENGTH}
									value={teamName}
									onChange={(e) => setTeamName(e.target.value)}
									readOnly={
										!tournament.registrationOpen || Boolean(signUpWithTeamId)
									}
								/>
							</div>
						) : (
							<input type="hidden" name="teamName" value={teamName} />
						)}
						{tournament.registrationOpen || avatarUrl ? (
							<div className="tournament__section__input-container">
								<Label htmlFor="logo">Logo</Label>
								{avatarUrl ? (
									<div className="stack horizontal md items-center">
										<Avatar size="xsm" url={avatarUrl} />
										{canEditAvatar ? (
											<SendouButton
												variant="minimal"
												size="small"
												onPress={() => setUploadedAvatar(null)}
											>
												{t("common:actions.edit")}
											</SendouButton>
										) : null}
										{canDeleteAvatar ? (
											<FormWithConfirm
												dialogHeading="Delete team logo?"
												fields={[["_action", "DELETE_LOGO"]]}
											>
												<SendouButton
													variant="minimal-destructive"
													size="small"
													type="submit"
												>
													<TrashIcon className="small-icon" />
												</SendouButton>
											</FormWithConfirm>
										) : null}
									</div>
								) : (
									<TournamentLogoUpload onChange={setUploadedAvatar} />
								)}
							</div>
						) : null}
						<div className="stack sm">
							<div className="text-lighter text-sm stack horizontal sm items-center">
								<input
									id="no-host"
									type="checkbox"
									name="prefersNotToHost"
									defaultChecked={Boolean(ownTeam?.prefersNotToHost)}
								/>
								<label htmlFor="no-host" className="mb-0">
									{t("tournament:pre.info.noHost")}
								</label>
							</div>
						</div>
					</div>
					<SendouButton
						data-testid="save-team-button"
						isDisabled={submitButtonDisabled()}
						onPress={handleSubmit}
					>
						{t("common:actions.save")}
					</SendouButton>
				</Form>
			</section>
		</div>
	);
}

const logoDimensions = imgTypeToDimensions["team-pfp"];
function TournamentLogoUpload({
	onChange,
}: {
	onChange: (file: File | null) => void;
}) {
	return (
		<input
			id="img-field"
			className="plain"
			type="file"
			name="img"
			accept="image/png, image/jpeg, image/webp"
			onChange={(e) => {
				const uploadedFile = e.target.files?.[0];
				if (!uploadedFile) {
					onChange(null);
					return;
				}

				new Compressor(uploadedFile, {
					height: logoDimensions.height,
					width: logoDimensions.width,
					maxHeight: logoDimensions.height,
					maxWidth: logoDimensions.width,
					// 0.5MB
					convertSize: 500_000,
					resize: "cover",
					success(result) {
						const file = new File([result], "img.webp", {
							type: "image/webp",
						});
						onChange(file);
					},
					error(err) {
						logger.error(err.message);
					},
				});
			}}
		/>
	);
}

function FriendCode() {
	const user = useUser();

	return (
		<div>
			<h3 className="tournament__section-header">1. Friend code</h3>
			<section className="tournament__section">
				<div className="tournament__section__input-container mx-auto">
					<FriendCodeInput friendCode={user?.friendCode} />
				</div>
			</section>
			{user?.friendCode ? (
				<div className="tournament__section__warning">
					Is the friend code above wrong? Post a message on the{" "}
					<a
						href={SENDOU_INK_DISCORD_URL}
						target="_blank"
						rel="noopener noreferrer"
					>
						sendou.ink Discord helpdesk
					</a>{" "}
					to change it.
				</div>
			) : null}
		</div>
	);
}

function GoogleFormsLink() {
	return (
		<div>
			<h3 className="tournament__section-header">
				Additional Requirement: Google Form
			</h3>
			<section className="tournament__section stack lg items-center">
				<a
					href={import.meta.env.VITE_LEAGUE_GOOGLE_FORM_URL}
					className="py-4 font-bold"
					target="_blank"
					rel="noopener noreferrer"
				>
					Answer survey hosted on Google Forms
				</a>
			</section>
			<div className="tournament__section__warning">
				Answer to additional question about your team's preferred match time and
				info to help with seeding
			</div>
		</div>
	);
}

function FillRoster({
	ownTeam,
	ownTeamCheckedIn,
}: {
	ownTeam: TournamentDataTeam;
	ownTeamCheckedIn: boolean;
}) {
	const data = useLoaderData<TournamentRegisterPageLoader>();
	const user = useUser();
	const tournament = useTournament();
	const [, copyToClipboard] = useCopyToClipboard();
	const { t } = useTranslation(["common", "tournament"]);

	const inviteLink = `${SENDOU_INK_BASE_URL}${tournamentJoinPage({
		tournamentId: tournament.ctx.id,
		inviteCode: ownTeam.inviteCode!,
	})}`;

	const { members: ownTeamMembers } = tournament.ownedTeamByUser(user) ?? {};
	invariant(ownTeamMembers, "own team members should exist");

	const missingMembers = Math.max(
		tournament.minMembersPerTeam - ownTeamMembers.length,
		0,
	);

	const optionalMembers = Math.max(
		tournament.maxMembersPerTeam - ownTeamMembers.length - missingMembers,
		0,
	);

	const showDeleteMemberSection =
		(!ownTeamCheckedIn && ownTeamMembers.length > 1) ||
		(ownTeamCheckedIn && ownTeamMembers.length > tournament.minMembersPerTeam);

	const playersAvailableToDirectlyAdd = (() => {
		return (data!.trusterPlayers?.trusters ?? []).filter((user) => {
			const isNotInTeam = tournament.ctx.teams.every((team) =>
				team.members.every((member) => member.userId !== user.id),
			);

			const hasInGameNameIfNeeded =
				!tournament.ctx.settings.requireInGameNames || user.inGameName;

			return isNotInTeam && hasInGameNameIfNeeded;
		});
	})();

	const teamIsFull = ownTeamMembers.length >= tournament.maxMembersPerTeam;
	const canAddMembers = !teamIsFull && tournament.registrationOpen;

	return (
		<div>
			<h3 className="tournament__section-header">
				3. {t("tournament:pre.roster.header")}
			</h3>
			<section className="tournament__section stack lg items-center">
				{playersAvailableToDirectlyAdd.length > 0 && canAddMembers ? (
					<>
						<DirectlyAddPlayerSelect
							players={playersAvailableToDirectlyAdd}
							teams={data!.trusterPlayers?.teams ?? []}
						/>
						<Divider className="text-uppercase">{t("common:or")}</Divider>
					</>
				) : null}
				{canAddMembers ? (
					<div className="stack md items-center">
						<div className="text-center text-sm">
							{t("tournament:actions.shareLink", { inviteLink })}
						</div>
						<div>
							<SendouButton
								size="small"
								onPress={() => copyToClipboard(inviteLink)}
								variant="outlined"
							>
								{t("common:actions.copyToClipboard")}
							</SendouButton>
						</div>
					</div>
				) : null}
				<div className="tournament__roster-grid">
					{ownTeamMembers.map((member, i) => {
						return (
							<div
								key={member.userId}
								className="stack sm items-center text-sm"
								data-testid={`member-num-${i + 1}`}
							>
								<Avatar size="xsm" user={member} />
								{tournament.ctx.settings.requireInGameNames ? (
									<div className="tournament__roster-grid__member-name">
										<div className="text-center">
											{member.inGameName ?? member.username}
										</div>
										{member.inGameName ? (
											<div className="text-lighter text-xs font-bold text-center">
												{member.username}
											</div>
										) : null}
									</div>
								) : (
									<div className="tournament__roster-grid__member-name">
										{member.username}
									</div>
								)}
							</div>
						);
					})}
					{new Array(missingMembers).fill(null).map((_, i) => {
						return (
							<div key={i} className="tournament__missing-player">
								?
							</div>
						);
					})}
					{new Array(optionalMembers).fill(null).map((_, i) => {
						return (
							<div
								key={i}
								className="tournament__missing-player tournament__missing-player__optional"
							>
								?
							</div>
						);
					})}
				</div>
				{showDeleteMemberSection ? (
					<DeleteMember members={ownTeamMembers} />
				) : null}
			</section>
			{tournament.ctx.settings.requireInGameNames ? (
				<div className="tournament__section__warning text-warning-important">
					Note that you are expected to use the in-game names as listed above.
					Playing in the event with a different name or using the alias feature
					might result in disqualification.
				</div>
			) : (
				// TODO: proper English for 1v1 "At least 1 members are required to participate. Max roster size is 1"
				<div className="tournament__section__warning">
					{t("tournament:pre.roster.footer", {
						atLeastCount: tournament.minMembersPerTeam,
						maxCount: tournament.maxMembersPerTeam,
					})}
				</div>
			)}
		</div>
	);
}

function DirectlyAddPlayerSelect({
	players,
	teams,
}: {
	players: { id: number; username: string; teamId?: number }[];
	teams: { id: number; name: string }[];
}) {
	const { t } = useTranslation(["tournament", "common"]);
	const fetcher = useFetcher();
	const id = React.useId();

	const othersOptions = players
		.filter((player) => !player.teamId)
		.map((player) => {
			return (
				<option key={player.id} value={player.id}>
					{player.username}
				</option>
			);
		});

	return (
		<fetcher.Form method="post" className="stack horizontal sm items-end">
			<div>
				<Label htmlFor={id}>
					{t("tournament:pre.roster.addTrusted.header")}
				</Label>
				<select id={id} name="userId">
					{teams.map((team) => {
						return (
							<optgroup label={team.name} key={team.id}>
								{players
									.filter((player) => player.teamId === team.id)
									.map((player) => {
										return (
											<option key={player.id} value={player.id}>
												{player.username}
											</option>
										);
									})}
							</optgroup>
						);
					})}
					{teams && teams.length > 0 ? (
						<optgroup label="Others">{othersOptions}</optgroup>
					) : (
						othersOptions
					)}
				</select>
			</div>
			<SubmitButton
				_action="ADD_PLAYER"
				state={fetcher.state}
				testId="add-player-button"
			>
				{t("common:actions.add")}
			</SubmitButton>
		</fetcher.Form>
	);
}

function DeleteMember({ members }: { members: TournamentDataTeam["members"] }) {
	const { t } = useTranslation(["tournament", "common"]);
	const id = React.useId();
	const fetcher = useFetcher();
	const [expanded, setExpanded] = React.useState(false);

	if (!expanded) {
		return (
			<SendouButton
				size="small"
				variant="minimal-destructive"
				onPress={() => setExpanded(true)}
			>
				{t("tournament:pre.roster.delete.button")}
			</SendouButton>
		);
	}

	return (
		<fetcher.Form method="post">
			<Label htmlFor={id}>{t("tournament:pre.roster.delete.header")}</Label>
			<div className="stack md horizontal">
				<select name="userId" id={id}>
					{members
						.filter((member) => !member.isOwner)
						.map((member) => (
							<option key={member.userId} value={member.userId}>
								{member.username}
							</option>
						))}
				</select>
				<SubmitButton
					state={fetcher.state}
					_action="DELETE_TEAM_MEMBER"
					variant="minimal-destructive"
				>
					{t("common:actions.delete")}
				</SubmitButton>
			</div>
		</fetcher.Form>
	);
}

// TODO: useBlocker to prevent leaving page if made changes without saving
function CounterPickMapPoolPicker() {
	const { t } = useTranslation(["common", "game-misc", "tournament"]);
	const tournament = useTournament();
	const fetcher = useFetcher();
	const data = useLoaderData<TournamentRegisterPageLoader>();
	const [counterPickMaps, setCounterPickMaps] = React.useState(
		data?.mapPool ?? [],
	);

	const counterPickMapPool = new MapPool(counterPickMaps);

	const isOneModeTournamentOf =
		tournament.modesIncluded.length === 1 ? tournament.modesIncluded[0] : null;

	return (
		<div>
			<h3 className="tournament__section-header">
				4. {t("tournament:pre.pool.header")}
			</h3>
			<section className="tournament__section">
				<fetcher.Form method="post" className="stack lg">
					<input
						type="hidden"
						name="mapPool"
						value={JSON.stringify(counterPickMaps)}
					/>
					{rankedModesShort
						.filter(
							(mode) =>
								!isOneModeTournamentOf || isOneModeTournamentOf === mode,
						)
						.map((mode) => {
							return (
								<ModeMapPoolPicker
									key={mode}
									amountToPick={
										isOneModeTournamentOf
											? TOURNAMENT.COUNTERPICK_ONE_MODE_TOURNAMENT_MAPS_PER_MODE
											: TOURNAMENT.COUNTERPICK_MAPS_PER_MODE
									}
									mode={mode}
									tiebreaker={
										tournament.ctx.tieBreakerMapPool.find(
											(stage) => stage.mode === mode,
										)?.stageId
									}
									pool={
										counterPickMaps
											.filter((m) => m.mode === mode)
											.map((m) => m.stageId) ?? []
									}
									onChange={(stageIds) =>
										setCounterPickMaps([
											...counterPickMaps.filter((m) => m.mode !== mode),
											...stageIds.map((stageId) => ({ mode, stageId })),
										])
									}
								/>
							);
						})}
					{validateCounterPickMapPool(
						counterPickMapPool,
						isOneModeTournamentOf,
						tournament.ctx.tieBreakerMapPool,
					) === "VALID" ? (
						<SubmitButton
							_action="UPDATE_MAP_POOL"
							state={fetcher.state}
							className="self-center mt-4"
							testId="save-map-list-button"
						>
							{t("common:actions.save")}
						</SubmitButton>
					) : (
						<MapPoolValidationStatusMessage
							status={validateCounterPickMapPool(
								counterPickMapPool,
								isOneModeTournamentOf,
								tournament.ctx.tieBreakerMapPool,
							)}
						/>
					)}
				</fetcher.Form>
			</section>
		</div>
	);
}

function MapPoolValidationStatusMessage({
	status,
}: {
	status: CounterPickValidationStatus;
}) {
	const { t } = useTranslation(["common"]);

	if (
		status !== "TOO_MUCH_STAGE_REPEAT" &&
		status !== "STAGE_REPEAT_IN_SAME_MODE" &&
		status !== "INCLUDES_BANNED" &&
		status !== "INCLUDES_TIEBREAKER"
	)
		return null;

	return (
		<div className="mt-4">
			<Alert alertClassName="w-max" variation="WARNING" tiny>
				{t(`common:maps.validation.${status}`, {
					maxStageRepeat: TOURNAMENT.COUNTERPICK_MAX_STAGE_REPEAT,
				})}
			</Alert>
		</div>
	);
}

function TOPickedMapPoolInfo() {
	const { t } = useTranslation(["calendar"]);
	const tournament = useTournament();

	if (tournament.ctx.toSetMapPool.length === 0) return null;

	const mapPool = new MapPool(tournament.ctx.toSetMapPool);

	return (
		<Section title={t("calendar:forms.mapPool")}>
			<div className="event__map-pool-section">
				<MapPoolStages mapPool={mapPool} />
				<LinkButton
					className="event__create-map-list-link"
					to={mapsPageWithMapPool(mapPool)}
					variant="outlined"
					size="small"
				>
					<Image alt="" path={navIconUrl("maps")} width={22} height={22} />
					{t("calendar:createMapList")}
				</LinkButton>
			</div>
		</Section>
	);
}

function TiebreakerMapPoolInfo() {
	const { t } = useTranslation(["game-misc"]);
	const tournament = useTournament();

	if (tournament.ctx.tieBreakerMapPool.length === 0) return null;

	return (
		<div className="text-sm text-lighter text-semi-bold">
			Tiebreaker map pool:{" "}
			{tournament.ctx.tieBreakerMapPool
				.sort((a, b) => modesShort.indexOf(a.mode) - modesShort.indexOf(b.mode))
				.map(
					(map) =>
						`${t(`game-misc:MODE_SHORT_${map.mode}`)} ${t(`game-misc:STAGE_${map.stageId}`)}`,
				)
				.join(", ")}
		</div>
	);
}
