import clsx from "clsx";
import { Pencil as EditIcon, Puzzle as PuzzleIcon } from "lucide-react";
import { useTranslation } from "react-i18next";
import {
	href,
	Link,
	useLoaderData,
	useMatches,
	useOutletContext,
} from "react-router";
import { Avatar } from "~/components/Avatar";
import { LinkButton, SendouButton } from "~/components/elements/Button";
import { SendouPopover } from "~/components/elements/Popover";
import { Flag } from "~/components/Flag";
import { Image, WeaponImage } from "~/components/Image";
import { BattlefyIcon } from "~/components/icons/Battlefy";
import { BskyIcon } from "~/components/icons/Bsky";
import { DiscordIcon } from "~/components/icons/Discord";
import { TwitchIcon } from "~/components/icons/Twitch";
import { YouTubeIcon } from "~/components/icons/YouTube";
import { useUser } from "~/features/auth/core/user";
import { BadgeDisplay } from "~/features/badges/components/BadgeDisplay";
import { UserCard } from "~/features/user-card/components/UserCard";
import { modesShort } from "~/modules/in-game-lists/modes";
import { countryCodeToTranslatedName } from "~/utils/i18n";
import invariant from "~/utils/invariant";
import type { SendouRouteHandle } from "~/utils/remix.server";
import { rawSensToString } from "~/utils/strings";
import { assertUnreachable } from "~/utils/types";
import {
	bskyUrl,
	modeImageUrl,
	navIconUrl,
	teamPage,
	topSearchPlayerPage,
} from "~/utils/urls";
import { MutualFriends } from "../components/MutualFriends";
import type { UserPageNavItem } from "../components/UserPageIconNav";
import { UserPageIconNav } from "../components/UserPageIconNav";
import { Widget } from "../components/Widget";
import { loader } from "../loaders/u.$identifier.index.server";
import type { UserPageLoaderData } from "../loaders/u.$identifier.server";
import styles from "../user-page.module.css";
import newStyles from "./u.$identifier.module.css";

export { loader };

export const handle: SendouRouteHandle = {
	i18n: [
		"badges",
		"team",
		"org",
		"vods",
		"lfg",
		"builds",
		"weapons",
		"gear",
		"game-badges",
	],
};

export default function UserInfoPage() {
	const data = useLoaderData<typeof loader>();

	if (data.type === "new") {
		return <NewUserInfoPage />;
	}
	return <OldUserInfoPage />;
}

function NewUserInfoPage() {
	const { t, i18n } = useTranslation(["user"]);
	const data = useLoaderData<typeof loader>();
	const user = useUser();
	const [, parentRoute] = useMatches();
	invariant(parentRoute);
	const layoutData = parentRoute.loaderData as UserPageLoaderData;
	const { navItems } = useOutletContext<{ navItems: UserPageNavItem[] }>();

	if (data.type !== "new") {
		throw new Error("Expected new user data");
	}

	const mainWidgets = data.widgets.filter((w) => w.slot === "main");
	const sideWidgets = data.widgets.filter((w) => w.slot === "side");

	const isOwnPage = layoutData.user.id === user?.id;

	return (
		<div className={newStyles.container}>
			<div className="stack sm">
				<div className={newStyles.header}>
					<UserCard userId={layoutData.user.id}>
						<Avatar user={layoutData.user} size="xmd" loading="eager" />
					</UserCard>
					<div className={newStyles.userInfo}>
						<div className={newStyles.nameGroup}>
							<h1 className={newStyles.username}>
								<UserCard userId={layoutData.user.id}>
									{layoutData.user.username}
								</UserCard>
							</h1>
							<ProfileSubtitle
								inGameName={layoutData.user.inGameName}
								pronouns={layoutData.user.pronouns}
								plusTier={layoutData.user.plusTier}
								country={layoutData.user.country}
								language={i18n.language}
							/>
						</div>
					</div>
					<div className={newStyles.desktopIconNav}>
						<UserPageIconNav items={navItems} />
					</div>
				</div>
				<MutualFriends mutualFriends={layoutData.mutualFriends} />
			</div>
			{isOwnPage ? (
				<div className={newStyles.editButtons}>
					<LinkButton
						to={href("/u/:identifier/edit-widgets", {
							identifier:
								layoutData.user.customUrl ?? layoutData.user.discordId,
						})}
						variant="outlined"
						size="small"
						icon={<PuzzleIcon />}
					>
						{t("user:widgets.edit")}
					</LinkButton>
					<LinkButton
						to={href("/u/:identifier/edit", {
							identifier:
								layoutData.user.customUrl ?? layoutData.user.discordId,
						})}
						variant="outlined"
						size="small"
						icon={<EditIcon />}
					>
						{t("user:widgets.editProfile")}
					</LinkButton>
				</div>
			) : null}

			<div className={newStyles.mobileIconNav}>
				<UserPageIconNav items={navItems} />
			</div>

			<div className={clsx(newStyles.sideCarousel, "scrollbar")}>
				{sideWidgets.map((widget) => (
					<Widget key={widget.id} widget={widget} user={layoutData.user} />
				))}
			</div>

			<div className={newStyles.mainStack}>
				{mainWidgets.map((widget) => (
					<Widget key={widget.id} widget={widget} user={layoutData.user} />
				))}
			</div>

			<div className={newStyles.grid}>
				<div className={newStyles.main}>
					{mainWidgets.map((widget) => (
						<Widget key={widget.id} widget={widget} user={layoutData.user} />
					))}
				</div>
				<div className={newStyles.side}>
					{sideWidgets.map((widget) => (
						<Widget key={widget.id} widget={widget} user={layoutData.user} />
					))}
				</div>
			</div>
		</div>
	);
}

export function OldUserInfoPage() {
	const data = useLoaderData<typeof loader>();
	const [, parentRoute] = useMatches();
	invariant(parentRoute);
	const layoutData = parentRoute.loaderData as UserPageLoaderData;

	if (data.type !== "old") {
		throw new Error("Expected old user data");
	}

	return (
		<div className={styles.container}>
			<div className="stack sm">
				<div className={styles.avatarContainer}>
					<UserCard userId={layoutData.user.id}>
						<Avatar
							user={layoutData.user}
							size="lg"
							className={styles.avatar}
							loading="eager"
						/>
					</UserCard>
					<div>
						<h2 className={styles.name}>
							<UserCard userId={layoutData.user.id}>
								<div>{layoutData.user.username}</div>
							</UserCard>
							<div>
								{data.user.country ? (
									<Flag countryCode={data.user.country} tiny />
								) : null}
							</div>
						</h2>
						<TeamInfo />
					</div>
					<div className={styles.socials}>
						{data.user.twitch ? (
							<SocialLink type="twitch" identifier={data.user.twitch} />
						) : null}
						{data.user.youtubeId ? (
							<SocialLink type="youtube" identifier={data.user.youtubeId} />
						) : null}
						{data.user.battlefy ? (
							<SocialLink type="battlefy" identifier={data.user.battlefy} />
						) : null}
						{data.user.bsky ? (
							<SocialLink type="bsky" identifier={data.user.bsky} />
						) : null}
					</div>
				</div>
				<div className="stack items-center">
					<MutualFriends mutualFriends={layoutData.mutualFriends} />
				</div>
			</div>
			<ExtraInfos />
			<WeaponPool />
			<TopPlacements />
			<BadgeDisplay badges={data.user.badges} key={layoutData.user.id} />
			{data.user.bio && <article>{data.user.bio}</article>}
		</div>
	);
}

function TeamInfo() {
	const { t } = useTranslation(["team"]);
	const data = useLoaderData<typeof loader>();

	if (data.type !== "old") {
		throw new Error("Expected old user data");
	}

	if (!data.user.team) return null;

	return (
		<div className="stack horizontal sm">
			<Link
				to={teamPage(data.user.team.customUrl)}
				className={styles.team}
				data-testid="main-team-link"
			>
				{data.user.team.avatarUrl ? (
					<img
						alt=""
						src={data.user.team.avatarUrl}
						width={32}
						height={32}
						className="rounded-full"
					/>
				) : null}
				<div>
					{data.user.team.name}
					{data.user.team.userTeamCustomRole ? (
						<div className="text-xxs text-lighter font-bold">
							{data.user.team.userTeamCustomRole}
						</div>
					) : data.user.team.userTeamRole ? (
						<div className="text-xxs text-lighter font-bold">
							{t(`team:roles.${data.user.team.userTeamRole}`)}
						</div>
					) : null}
				</div>
			</Link>
			<SecondaryTeamsPopover />
		</div>
	);
}

function SecondaryTeamsPopover() {
	const { t } = useTranslation(["team"]);

	const data = useLoaderData<typeof loader>();

	if (data.type !== "old") {
		throw new Error("Expected old user data");
	}

	if (data.user.secondaryTeams.length === 0) return null;

	return (
		<SendouPopover
			trigger={
				<SendouButton
					className="focus-text-decoration self-start"
					variant="minimal"
					size="small"
				>
					<span
						className="text-sm font-bold text-main-forced"
						data-testid="secondary-team-trigger"
					>
						+{data.user.secondaryTeams.length}
					</span>
				</SendouButton>
			}
		>
			<div className="stack sm">
				{data.user.secondaryTeams.map((team) => (
					<div
						key={team.customUrl}
						className="stack horizontal md items-center"
					>
						<Link
							to={teamPage(team.customUrl)}
							className={clsx(styles.team, "text-main-forced")}
						>
							{team.avatarUrl ? (
								<img
									alt=""
									src={team.avatarUrl}
									width={24}
									height={24}
									className="rounded-full"
								/>
							) : null}
							{team.name}
						</Link>
						{team.userTeamCustomRole ? (
							<div className="text-xxs text-lighter font-bold">
								{team.userTeamCustomRole}
							</div>
						) : team.userTeamRole ? (
							<div className="text-xxs text-lighter font-bold">
								{t(`team:roles.${team.userTeamRole}`)}
							</div>
						) : null}
					</div>
				))}
			</div>
		</SendouPopover>
	);
}

interface SocialLinkProps {
	type: "youtube" | "twitch" | "battlefy" | "bsky";
	identifier: string;
}

export function SocialLink({
	type,
	identifier,
}: {
	type: SocialLinkProps["type"];
	identifier: string;
}) {
	const href = () => {
		switch (type) {
			case "twitch":
				return `https://www.twitch.tv/${identifier}`;
			case "youtube":
				return `https://www.youtube.com/channel/${identifier}`;
			case "battlefy":
				return `https://battlefy.com/users/${identifier}`;
			case "bsky":
				return bskyUrl(identifier);
			default:
				assertUnreachable(type);
		}
	};

	return (
		<a
			className={clsx(styles.socialLink, {
				[styles.socialLinkYoutube]: type === "youtube",
				[styles.socialLinkTwitch]: type === "twitch",
				[styles.socialLinkBattlefy]: type === "battlefy",
				[styles.socialLinkBsky]: type === "bsky",
			})}
			href={href()}
		>
			<SocialLinkIcon type={type} />
		</a>
	);
}

function SocialLinkIcon({ type }: Pick<SocialLinkProps, "type">) {
	switch (type) {
		case "twitch":
			return <TwitchIcon />;
		case "youtube":
			return <YouTubeIcon />;
		case "battlefy":
			return <BattlefyIcon />;
		case "bsky":
			return <BskyIcon />;
		default:
			assertUnreachable(type);
	}
}

function ExtraInfos() {
	const { t } = useTranslation(["user"]);
	const data = useLoaderData<typeof loader>();

	if (data.type !== "old") {
		throw new Error("Expected old user data");
	}

	const motionSensText =
		typeof data.user.motionSens === "number"
			? `${t("user:motion")} ${rawSensToString(data.user.motionSens)}`
			: null;

	const stickSensText =
		typeof data.user.stickSens === "number"
			? `${t("user:stick")} ${rawSensToString(data.user.stickSens)}`
			: null;

	if (
		!data.user.inGameName &&
		typeof data.user.stickSens !== "number" &&
		!data.user.discordUniqueName &&
		!data.user.plusTier
	) {
		return null;
	}

	return (
		<div className={styles.extraInfos}>
			<div className={styles.extraInfo}>#{data.user.id}</div>
			{data.user.discordUniqueName && (
				<div className={styles.extraInfo}>
					<span className={styles.extraInfoHeading}>
						<DiscordIcon />
					</span>{" "}
					{data.user.discordUniqueName}
				</div>
			)}
			{data.user.pronouns && (
				<div className={styles.extraInfo}>
					<span className={styles.extraInfoHeading}>
						{t("user:usesPronouns")}
					</span>{" "}
					{data.user.pronouns.subject}/{data.user.pronouns.object}
				</div>
			)}
			{data.user.inGameName && (
				<div className={styles.extraInfo}>
					<span className={styles.extraInfoHeading}>{t("user:ign.short")}</span>{" "}
					{data.user.inGameName}
				</div>
			)}
			{typeof data.user.stickSens === "number" && (
				<div className={styles.extraInfo}>
					<span className={styles.extraInfoHeading}>{t("user:sens")}</span>{" "}
					{[motionSensText, stickSensText].filter(Boolean).join(" / ")}
				</div>
			)}
			{data.user.plusTier && (
				<div className={styles.extraInfo}>
					<Image path={navIconUrl("plus")} width={20} height={20} alt="" />{" "}
					{data.user.plusTier}
				</div>
			)}
		</div>
	);
}

function WeaponPool() {
	const data = useLoaderData<typeof loader>();

	if (data.type !== "old") {
		throw new Error("Expected old user data");
	}

	if (data.user.weapons.length === 0) return null;

	return (
		<div className="stack horizontal sm justify-center">
			{data.user.weapons.map((weapon, i) => {
				return (
					<div key={weapon.weaponSplId} className={styles.weapon}>
						<WeaponImage
							testId={`${weapon.weaponSplId}-${i + 1}`}
							weapon={weapon}
							width={38}
							height={38}
						/>
					</div>
				);
			})}
		</div>
	);
}

function ProfileSubtitle({
	inGameName,
	pronouns,
	plusTier,
	country,
	language,
}: {
	inGameName: string | null;
	pronouns: { subject: string; object: string } | null;
	plusTier: number | null;
	country: string | null;
	language: string;
}) {
	const parts: React.ReactNode[] = [];

	if (inGameName) {
		parts.push(inGameName);
	}

	if (plusTier) {
		parts.push(`+${plusTier}`);
	}

	if (pronouns) {
		parts.push(`${pronouns.subject}/${pronouns.object}`);
	}

	if (country) {
		parts.push(
			<span key="country" className="stack horizontal xs items-center">
				<Flag countryCode={country} tiny />
				{countryCodeToTranslatedName({ countryCode: country, language })}
			</span>,
		);
	}

	if (parts.length === 0) return null;

	return (
		<div className={newStyles.subtitle}>
			{parts.map((part, i) => (
				<span key={i} className="stack horizontal xs items-center">
					{i > 0 ? <span>·</span> : null}
					{part}
				</span>
			))}
		</div>
	);
}

function TopPlacements() {
	const data = useLoaderData<typeof loader>();

	if (data.type !== "old") {
		throw new Error("Expected old user data");
	}

	if (data.user.topPlacements.length === 0) return null;

	return (
		<Link
			to={topSearchPlayerPage(data.user.topPlacements[0].playerId)}
			className={styles.placements}
			data-testid="placements-box"
		>
			{modesShort.map((mode) => {
				const placement = data.user.topPlacements.find(
					(placement) => placement.mode === mode,
				);

				if (!placement) return null;

				return (
					<div key={mode} className={styles.placementsMode}>
						<Image path={modeImageUrl(mode)} alt="" width={24} height={24} />
						<div>
							{placement.rank} / {placement.power}
						</div>
					</div>
				);
			})}
		</Link>
	);
}
