import React from "react";
import { useTranslation } from "react-i18next";
import type { MetaFunction } from "react-router";
import { Link, useLoaderData, useSearchParams } from "react-router";
import { Avatar } from "~/components/Avatar";
import { TierImage, WeaponImage } from "~/components/Image";
import { Main } from "~/components/Main";
import * as Seasons from "~/features/mmr/core/Seasons";
import { ordinalToSp } from "~/features/mmr/mmr-utils";
import type { SkillTierInterval } from "~/features/mmr/tiered.server";
import { rankedModesShort } from "~/modules/in-game-lists/modes";
import { weaponCategories } from "~/modules/in-game-lists/weapon-ids";
import { metaTags, type SerializeFrom } from "~/utils/remix";
import type { SendouRouteHandle } from "~/utils/remix.server";
import {
	LEADERBOARDS_PAGE,
	navIconUrl,
	teamPage,
	topSearchPlayerPage,
	userPage,
	userSeasonsPage,
} from "~/utils/urls";
import { InfoPopover } from "../../../components/InfoPopover";
import { TopTenPlayer } from "../components/TopTenPlayer";
import type { XPLeaderboardItem } from "../LeaderboardRepository.server";
import {
	LEADERBOARD_TYPES,
	SEASON_SEARCH_PARAM_KEY,
	TYPE_SEARCH_PARAM_KEY,
} from "../leaderboards-constants";
import { seasonHasTopTen } from "../leaderboards-utils";
import { loader } from "../loaders/leaderboards.server";

export { loader };

import styles from "../../top-search/top-search.module.css";

export const handle: SendouRouteHandle = {
	i18n: ["vods"],
	breadcrumb: () => ({
		imgPath: navIconUrl("leaderboards"),
		href: LEADERBOARDS_PAGE,
		type: "IMAGE",
	}),
};

export const meta: MetaFunction = (args) => {
	const data = args.loaderData as SerializeFrom<typeof loader> | null;

	if (!data) return [];

	return metaTags({
		title: "Leaderboards",
		ogTitle: "Splatoon leaderboards",
		description:
			"Leaderboards of top Splatoon players ranked by their X Battle placements as well as tournament and SendouQ results. Categories per weapon and mode.",
		location: args.location,
	});
};

export default function LeaderboardsPage() {
	const { t } = useTranslation(["common", "game-misc", "weapons"]);
	const [searchParams, setSearchParams] = useSearchParams();
	const data = useLoaderData<typeof loader>();

	const isAllUserLeaderboard =
		!searchParams.get(TYPE_SEARCH_PARAM_KEY) ||
		searchParams.get(TYPE_SEARCH_PARAM_KEY) === "USER";

	const seasonPlusTypeToKey = ({
		season,
		type,
	}: {
		season: number;
		type: string;
	}) => `${type};${season}`;

	const selectValue = () => {
		const type =
			searchParams.get(TYPE_SEARCH_PARAM_KEY) ?? LEADERBOARD_TYPES[0];

		if (
			LEADERBOARD_TYPES.includes(type as (typeof LEADERBOARD_TYPES)[number])
		) {
			return seasonPlusTypeToKey({
				season: data.season,
				type,
			});
		}

		return type;
	};

	const showTopTen = Boolean(
		seasonHasTopTen(data.season) &&
			isAllUserLeaderboard &&
			data.userLeaderboard,
	);

	const renderNoEntries =
		(data.userLeaderboard && data.userLeaderboard.length === 0) ||
		(data.teamLeaderboard && data.teamLeaderboard.length === 0);

	return (
		<Main halfWidth className="stack lg">
			<select
				className="text-sm"
				value={selectValue()}
				onChange={(e) => {
					const [type, season] = e.target.value.split(";");
					setSearchParams(
						{
							[TYPE_SEARCH_PARAM_KEY]: type,
							[SEASON_SEARCH_PARAM_KEY]: season,
						},
						{ replace: true },
					);
				}}
			>
				{Seasons.allStarted().map((season) => {
					return (
						<optgroup label={`SP - Season ${season}`} key={season}>
							{LEADERBOARD_TYPES.filter((type) => !type.includes("XP")).map(
								(type) => {
									const userOrTeam = type.includes("USER") ? "USER" : "TEAM";
									const category = weaponCategories.find((c) =>
										type.includes(c.name),
									)?.name;

									return (
										<option
											key={type}
											value={seasonPlusTypeToKey({ season, type })}
										>
											{t(`common:leaderboard.type.${userOrTeam}`)}
											{type.includes("ALL")
												? ` (${t("leaderboard.type.XP-ALL")})`
												: null}
											{category
												? ` (${t(`common:weapon.category.${category}`)})`
												: ""}
										</option>
									);
								},
							)}
						</optgroup>
					);
				})}
				<optgroup label="XP">
					<option value="XP-ALL">{t("common:leaderboard.type.XP-ALL")}</option>
					{rankedModesShort.map((mode) => {
						return (
							<option key={mode} value={`XP-MODE-${mode}`}>
								{t(`game-misc:MODE_LONG_${mode}`)}
							</option>
						);
					})}
				</optgroup>
				{weaponCategories.map((category) => {
					return (
						<optgroup
							key={category.name}
							label={`XP (${t(`common:weapon.category.${category.name}`)})`}
						>
							{category.weaponIds.map((weaponId) => {
								return (
									<option key={weaponId} value={`XP-WEAPON-${weaponId}`}>
										{t(`weapons:MAIN_${weaponId}`)}
									</option>
								);
							})}
						</optgroup>
					);
				})}
			</select>
			{showTopTen ? (
				<div className="stack lg mx-auto">
					{data
						.userLeaderboard!.filter((_, i) => i <= 9)
						.map((entry, i) => {
							return (
								<Link
									key={`${entry.id}-${data.season}`}
									to={userSeasonsPage({ user: entry, season: data.season })}
								>
									<TopTenPlayer
										placement={i + 1}
										power={entry.power}
										season={data.season}
									/>
								</Link>
							);
						})}
				</div>
			) : null}

			{data.ownEntryPeek ? (
				<OwnEntryPeek
					entry={data.ownEntryPeek.entry}
					nextTier={data.ownEntryPeek.nextTier}
				/>
			) : null}

			{data.userLeaderboard ? (
				<PlayersTable
					entries={data.userLeaderboard}
					showTiers={isAllUserLeaderboard}
					showingTopTen={showTopTen}
				/>
			) : null}
			{data.teamLeaderboard ? (
				<TeamTable
					entries={data.teamLeaderboard}
					showQualificationDividers={!selectValue().includes("ALL")}
				/>
			) : null}
			{data.xpLeaderboard ? <XPTable entries={data.xpLeaderboard} /> : null}

			{renderNoEntries ? (
				<div className="text-center text-lg text-lighter">
					{data.userLeaderboard
						? t("common:leaderboard.noPlayers")
						: t("common:leaderboard.noTeams")}
				</div>
			) : null}

			{!data.xpLeaderboard && data.season === Seasons.current()?.nth ? (
				<div className="text-xs text-lighter text-center">
					{t("common:leaderboard.updateInfo")}
				</div>
			) : null}
		</Main>
	);
}

function OwnEntryPeek({
	entry,
	nextTier,
}: {
	entry: NonNullable<SerializeFrom<typeof loader>["userLeaderboard"]>[number];
	nextTier?: SkillTierInterval;
}) {
	const data = useLoaderData<typeof loader>();

	return (
		<div>
			{entry.firstOfTier ? (
				<div className={styles.tierHeader}>
					<TierImage tier={entry.firstOfTier} width={32} />
					{entry.firstOfTier.name}
					{entry.firstOfTier.isPlus ? "+" : ""}
				</div>
			) : null}
			<div>
				<Link
					to={userSeasonsPage({ user: entry, season: data.season })}
					className={styles.tableRow}
				>
					<div className={styles.tableInnerRow}>
						<div className={styles.tableRank}>{entry.placementRank}</div>
						<div>
							<Avatar size="xxs" user={entry} />
						</div>
						{typeof entry.weaponSplId === "number" ? (
							<WeaponImage
								className={styles.tableWeapon}
								variant="build"
								weaponSplId={entry.weaponSplId}
								width={32}
								height={32}
							/>
						) : null}
						<div className={styles.tableName}>{entry.username}</div>
						<div className={styles.tablePower}>{entry.power}</div>
					</div>
				</Link>
			</div>
			{nextTier ? (
				<div className="text-xs text-lighter ml-auto stack items-end">
					{nextTier.name}
					{nextTier.isPlus ? "+" : ""} @ {ordinalToSp(nextTier.neededOrdinal!)}
					SP
				</div>
			) : null}
		</div>
	);
}

function PlayersTable({
	entries,
	showTiers,
	showingTopTen,
}: {
	entries: NonNullable<SerializeFrom<typeof loader>["userLeaderboard"]>;
	showTiers?: boolean;
	showingTopTen?: boolean;
}) {
	const data = useLoaderData<typeof loader>();

	return (
		<div className={styles.table}>
			{entries
				// hide normal rows that are showed in "fancy" top 10 format
				.filter((_, i) => !showingTopTen || i > 9)
				.map((entry) => {
					return (
						<React.Fragment key={entry.entryId}>
							{entry.firstOfTier && showTiers ? (
								<div className={styles.tierHeader}>
									<TierImage tier={entry.firstOfTier} width={32} />
									{entry.firstOfTier.name}
									{entry.firstOfTier.isPlus ? "+" : ""}
								</div>
							) : null}
							<Link
								to={userSeasonsPage({ user: entry, season: data.season })}
								className={styles.tableRow}
							>
								<div className={styles.tableInnerRow}>
									<div className={styles.tableRank}>{entry.placementRank}</div>
									<div>
										<Avatar size="xxs" user={entry} />
									</div>
									{typeof entry.weaponSplId === "number" ? (
										<WeaponImage
											className={styles.tableWeapon}
											variant="build"
											weaponSplId={entry.weaponSplId}
											width={32}
											height={32}
										/>
									) : null}
									<div className={styles.tableName}>{entry.username}</div>
									{entry.pendingPlusTier ? (
										<div className="text-xs text-theme whitespace-nowrap">
											➜ +{entry.pendingPlusTier}
										</div>
									) : null}
									<div className={styles.tablePower}>
										{entry.power.toFixed(2)}
									</div>
								</div>
							</Link>
						</React.Fragment>
					);
				})}
		</div>
	);
}

function TeamTable({
	entries,
	showQualificationDividers: _showQualificationDividers,
}: {
	entries: NonNullable<SerializeFrom<typeof loader>["teamLeaderboard"]>;
	showQualificationDividers?: boolean;
}) {
	const { t } = useTranslation(["common"]);
	const data = useLoaderData<typeof loader>();
	const isCurrentSeason = data.season === Seasons.current()?.nth;
	const showQualificationDividers =
		_showQualificationDividers && isCurrentSeason && entries.length > 20;

	return (
		<div className={styles.table}>
			{entries.map((entry, i) => {
				return (
					<React.Fragment key={entry.entryId}>
						<div className={styles.tableRow}>
							<div className={styles.tableInnerRow}>
								<div className={styles.tableRank}>{entry.placementRank}</div>
								{entry.team?.avatarUrl ? (
									<Link
										to={teamPage(entry.team.customUrl)}
										title={entry.team.name}
									>
										<Avatar
											size="xxs"
											url={entry.team.avatarUrl}
											className={styles.avatar}
										/>
									</Link>
								) : null}
								<div className="text-xs">
									{entry.members.map((member, i) => {
										return (
											<React.Fragment key={member.id}>
												<Link to={userPage(member)}>{member.username}</Link>
												{i !== entry.members.length - 1 ? ", " : null}
											</React.Fragment>
										);
									})}
								</div>
								<div className={styles.tablePower}>
									{entry.power.toFixed(2)}
								</div>
							</div>
						</div>
						{i === 11 && showQualificationDividers ? (
							<div
								className={`${styles.tableRow} ${styles.tableRowQualification}`}
							>
								{t("common:leaderboard.qualification")}
								<InfoPopover tiny>
									{t("common:leaderboard.qualification.info")}
								</InfoPopover>
							</div>
						) : null}
					</React.Fragment>
				);
			})}
		</div>
	);
}

function XPTable({ entries }: { entries: XPLeaderboardItem[] }) {
	return (
		<div className={styles.table}>
			{entries.map((entry) => {
				return (
					<Link
						to={topSearchPlayerPage(entry.playerId)}
						key={entry.entryId}
						className={styles.tableRow}
					>
						<div className={styles.tableInnerRow}>
							<div className={styles.tableRank}>{entry.placementRank}</div>
							{entry.discordId ? (
								<Avatar size="xxs" user={entry as any} />
							) : null}
							<WeaponImage
								className={styles.tableWeapon}
								variant="build"
								weaponSplId={entry.weaponSplId}
								width={32}
								height={32}
							/>
							<div>{entry.name}</div>
							<div className={styles.tablePower}>{entry.power.toFixed(1)}</div>
						</div>
					</Link>
				);
			})}
		</div>
	);
}
