// Context is used for data passing as there are different launch points (homescreen and multi-token screen),
import { Trans } from "@lingui/react/macro";
import { useEffect, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useAccount } from "wagmi";

import type { Token } from "@/types/index";
import { MIcon } from "@components/common/MIcon";
import { SlideInPanel } from "@components/common/SlideInPanel";
import { TokenChip } from "@components/common/TokenChip";
import { useMappedChainData } from "@hooks/useMappedChainData";
import { useSearch } from "@hooks/useSearch";
import { useSearchTokens } from "@hooks/useSearchTokens";
import { useTheme } from "@hooks/useTheme";
import { importToken } from "@store/globalSlice";
import {
	addOutputToken,
	setFromChainId,
	setFromTokens,
	setToChainId,
} from "@store/quoteParamSlice";
import { setTokenSelectorData } from "@store/tokenSelectorSlice";
import type { ReduxStateType } from "@store/types";
import { adjustDecimals } from "@utils/adjustDecimals";
import { getBorderRadius } from "@utils/getBorderRadius";
import { getFormattedTokenList } from "@utils/getFormattedTokenList";
import { useFilteredTokens } from "../../hooks/useFilteredTokens";
import { NetworkList } from "./NetworkList";
import { NetworkSearchItem } from "./NetworkSearchItem";
import { SearchBar } from "./SearchBar";
import { TokensSection } from "./TokensSection";
import { useTrendingTokens } from "./useTrendingTokens";

export const TokenSelector = () => {
	const dispatch = useDispatch();
	const { isConnected } = useAccount();
	const isVisible = useSelector(
		(state: ReduxStateType) => state.tokenSelector.isVisible,
	);
	const locale = useSelector((state: ReduxStateType) => state.settings.language);
	const data = useSelector((state: ReduxStateType) => state.tokenSelector.data);
	const mappedChainData = useMappedChainData();

	const chains = data?.chains;
	const isSourceTokenSelector = data?.isSourceTokenSelector;

	const [activeNetwork, setActiveNetwork] = useState<number | "all">("all");
	const [searchKeyword, setSearchKeyword] = useState<string>(""); //@fixme: temporary
	const [formattedTokenList, setFormattedTokenList] = useState<Token[]>();

	const allTokens = useMemo(() => data?.tokens, [data]);

	const trendingTokens = useTrendingTokens({
		tokens: allTokens?.[activeNetwork],
	});

	useEffect(() => {
		if (allTokens) {
			setFormattedTokenList(getFormattedTokenList(allTokens));
		}
	}, [allTokens]);

	const { tokenResult: searchTokenResult, networkResult: searchNetworkResult } =
		useSearchTokens({
			searchValue: searchKeyword?.trim(),
			tokens: formattedTokenList ?? [],
			activeNetwork: typeof activeNetwork === "string" ? undefined : activeNetwork,
		});

	const { data: apiTokenResult, isLoading: isSearchingTokens } = useSearch({
		keyword: searchKeyword,
	});

	// damn, this file needs more comments

	// Filter out tokens found in searchTokenResult from apiTokenResult
	const filteredApiTokenResult = useMemo(() => {
		return (
			apiTokenResult?.filter(
				(apiToken) =>
					!searchTokenResult.some(
						(searchToken) =>
							searchToken.address.toLowerCase() === apiToken.address.toLowerCase() &&
							searchToken.chainId === apiToken.chainId,
					),
			) || []
		);
	}, [apiTokenResult, searchTokenResult]);

	const filteredTokens = useFilteredTokens({
		allTokens: allTokens ?? {},
		formattedTokenList: formattedTokenList ?? [],
		activeNetwork: activeNetwork,
	});

	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	const tokensToDisplay = useMemo(() => {
		if (searchKeyword) return searchTokenResult;
		if (filteredTokens?.length) return filteredTokens;
		return [];
	}, [filteredTokens, searchKeyword, searchTokenResult, isVisible]);

	const multichainBalance = useMemo(() => {
		if (allTokens && isConnected) {
			const _formattedList = getFormattedTokenList(allTokens);
			return _formattedList.reduce(
				(balance, token) => balance + (token?.balanceInUsd || 0),
				0,
			);
		}
		return 0;
	}, [allTokens, isConnected]);

	useEffect(() => {
		if (data?.activeChain === "all") {
			setActiveNetwork("all");
		} else if (data?.activeChain) {
			setActiveNetwork(data.activeChain.chainId);
		}
	}, [data]);

	// to get usd summation of the network balance
	const getCurrentChainBalance = useMemo(() => {
		if (tokensToDisplay?.length && isConnected) {
			return tokensToDisplay.reduce(
				(balance, token) => balance + (token?.balanceInUsd || 0),
				0,
			);
		}
		return null;
	}, [tokensToDisplay, isConnected]);

	function closePanel() {
		dispatch(setTokenSelectorData({ isVisible: false, data: null }));
	}

	function setChain(chainId: number) {
		if (isSourceTokenSelector) {
			dispatch(setFromChainId(chainId));
		} else {
			dispatch(setToChainId(chainId));
		}
	}

	function setToken(token: Token, importedToken?: boolean) {
		if (importedToken) {
			dispatch(importToken({ token: token }));
		}

		if (isSourceTokenSelector) {
			dispatch(setFromTokens(token));
			setChain(token.chainId);

			closePanel();
		} else {
			dispatch(addOutputToken({ id: data?.id || 0, token }));
			setChain(token.chainId);

			closePanel();
		}
	}

	function getTokenTitle() {
		if (!tokensToDisplay?.length) return undefined;
		if (activeNetwork === "all") return "Shortlisted Tokens";

		// biome-ignore format:
		return `Tokens on ${mappedChainData?.[activeNetwork]?.name}`;
	}

	// To disabled tokens in the token list
	function shouldDisable(token: Token) {
		const tokensForChain = data?.tokensToDisable?.[token.chainId];

		if (tokensForChain) {
			return Boolean(
				tokensForChain.find(
					(address) => address.toLowerCase() === token.address.toLowerCase(),
				),
			);
		}
		return false;
	}

	const showChainDetails =
		activeNetwork === "all" || Boolean(searchKeyword?.length);

	return (
		<SlideInPanel
			isVisible={isVisible}
			closePanel={closePanel}
			cardClasses="bn-h-full"
		>
			<div className="bn-flex bn-flex-col bn-max-h-full bn-h-full">
				<div className="bn-flex bn-items-center bn-w-full bn-px-4 bn-pt-4">
					<SearchBar setValue={setSearchKeyword} />
					<CloseButton onClick={closePanel} />
				</div>

				{chains && (
					<NetworkList
						chains={chains}
						activeNetwork={activeNetwork}
						setActiveNetwork={setActiveNetwork}
						// hiding all networks when wallet not connected (or wallet is empty)
						showAllNetworksBtn={isSourceTokenSelector && !!multichainBalance}
					/>
				)}

				{isSourceTokenSelector && getCurrentChainBalance ? (
					<div className="bn-px-4 bn-mb-[18px]">
						<p className="bn-text-secondary bn-text-sm bn-font-normal">
							<Trans>My Wallet Balance</Trans>
						</p>
						<p className="bn-text-primary bn-text-xl bn-font-medium bn-aeonik-font bn-mt-1">
							${adjustDecimals(getCurrentChainBalance, 2, locale)}
						</p>
					</div>
				) : null}

				{!isSourceTokenSelector && (
					<div className="bn-px-4 bn-mb-3">
						<h4 className="bn-text-sm bn-font-normal bn-text-secondary bn-mb-2">
							<Trans>Trending Tokens</Trans>
						</h4>
						<div className="bn-flex bn-w-full bn-overflow-x-auto bn-items-center bn-pb-1">
							{trendingTokens?.map((token) => (
								<TokenChip
									token={token}
									onClick={() => setToken(token)}
									classNames="bn-mx-[2.5px]"
									// biome-ignore format:
									key={`${token.address}-${token.chainId}`}
									showChain
									disabled={shouldDisable(token)}
								/>
							))}
						</div>
					</div>
				)}

				<div className="bn-flex-1 bn-overflow-y-hidden bn-border-t bn-border-0 bn-border-primary bn-border-solid bn-flex bn-flex-col bn-w-full bn-overflow-auto">
					{!!searchNetworkResult?.length && searchKeyword && (
						<>
							<h4 className="bn-text-sm bn-font-normal bn-text-secondary bn-mb-2 bn-px-4 bn-mt-3">
								<Trans>Chains</Trans>
							</h4>
							{searchNetworkResult.map((item) => {
								return (
									<NetworkSearchItem
										key={item.chainId}
										chain={item}
										onClick={() => {
											setActiveNetwork(item.chainId);
											setSearchKeyword("");
										}}
										assetsLength={allTokens?.[item.chainId]?.length ?? 0}
									/>
								);
							})}
						</>
					)}
					<div className="bn-flex-1 bn-overflow-auto bn-w-full bn-overflow-x-hidden">
						<TokensSection
							tokens={tokensToDisplay}
							activeToken={data?.activeToken}
							setToken={setToken}
							showChainDetails={showChainDetails}
							shouldDisable={shouldDisable}
						/>
						<TokensSection
							tokens={filteredApiTokenResult}
							isSearching={isSearchingTokens}
							showNoTokensWarning={!tokensToDisplay?.length}
							setToken={(value) => setToken(value, true)}
							showChainDetails={showChainDetails}
							shouldDisable={shouldDisable}
						/>
					</div>
				</div>
			</div>
		</SlideInPanel>
	);
};

const CloseButton = ({ onClick }: { onClick: () => void }) => {
	const theme = useTheme();
	return (
		<button
			className="bn-w-10 bn-h-10 bn-bg-transparent bn-ml-2 bn-cursor-pointer bn-flex bn-items-center bn-justify-center bn-border bn-border-solid bn-border-primary bn-text-secondary"
			type="button"
			onClick={onClick}
			style={{ borderRadius: getBorderRadius(theme.borderRadius, 8) }}
		>
			<MIcon name="close" />
		</button>
	);
};

/**
 * @todo
 * 1. Abstract away functions
 * 2. Abstract the search logic
 * 3. Sorting for all balance
 * 4. Check multi-chain balance thing
 * 5. Introduce cards & conditionally render them
 */
