import { Currency, CurrencyAmount, Token } from "@uniswap/sdk-core";
import React, {
  CSSProperties,
  MutableRefObject,
  useCallback,
  useMemo,
} from "react";
import { FixedSizeList } from "react-window";
import { Text } from "rebass";
import styled from "styled-components/macro";
import { useWeb3 } from "../../web3";
import { useCombinedActiveList } from "../../state/glists/hooks";
import { WrappedTokenInfo } from "../../state/glists/wrappedTokenInfo";
import { useCurrencyBalance } from "../../hooks/Balances";
import { TYPE } from "../../theme";
import { useIsUserAddedToken } from "../../hooks/Tokens";
import Column from "../Column";
import { RowFixed, RowBetween } from "../Row";
import CurrencyLogo from "../CurrencyLogo";
import { MouseoverTooltip } from "../Tooltip";
import { MenuItem } from "./styleds";
import Loader from "../Loader";
import { isTokenOnList } from "../../utils";
import ImportRow from "./ImportRow";
import { LightGreyCard } from "../Card";
import useTheme from "../../hooks/useTheme";
import QuestionHelper from "../QuestionHelper";
import TokenListLogo from "../../assets/svg/tokenlist.svg";

function currencyKey(currency: Currency): string {
  return currency.isToken ? currency.address : "ETHER";
}

const StyledBalanceText = styled(Text)`
  white-space: nowrap;
  overflow: hidden;
  max-width: 5rem;
  text-overflow: ellipsis;
`;

const Tag = styled.div`
  background-color: ${({ theme }) => theme.bg3};
  color: ${({ theme }) => theme.text2};
  font-size: 14px;
  border-radius: 4px;
  padding: 0.25rem 0.3rem 0.25rem 0.3rem;
  max-width: 6rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  justify-self: flex-end;
  margin-right: 4px;
`;

const FixedContentRow = styled.div`
  padding: 4px 20px;
  height: 56px;
  display: grid;
  grid-gap: 16px;
  align-items: center;
`;

function Balance({ balance }: { balance: CurrencyAmount<Currency> }) {
  return (
    <StyledBalanceText title={balance.toExact()}>
      {balance.toSignificant(4)}
    </StyledBalanceText>
  );
}

const TagContainer = styled.div`
  display: flex;
  justify-content: flex-end;
`;

const TokenListLogoWrapper = styled.img`
  height: 20px;
`;

function TokenTags({ currency }: { currency: Currency }) {
  if (!(currency instanceof WrappedTokenInfo)) {
    return <span />;
  }

  const tags = currency.tags;
  if (!tags || tags.length === 0) return <span />;

  const tag = tags[0];

  return (
    <TagContainer>
      <MouseoverTooltip text={tag.description}>
        <Tag key={tag.id}>{tag.name}</Tag>
      </MouseoverTooltip>
      {tags.length > 1 ? (
        <MouseoverTooltip
          text={tags
            .slice(1)
            .map(({ name, description }) => `${name}: ${description}`)
            .join("; \n")}
        >
          <Tag>...</Tag>
        </MouseoverTooltip>
      ) : null}
    </TagContainer>
  );
}

function CurrencyRow({
  currency,
  onSelect,
  isSelected,
  otherSelected,
  style,
}: {
  currency: Currency;
  onSelect: () => void;
  isSelected: boolean;
  otherSelected: boolean;
  style: CSSProperties;
}) {
  const { account } = useWeb3();
  const key = currencyKey(currency);
  const selectedTokenList = useCombinedActiveList();
  const isOnSelectedList = isTokenOnList(
    selectedTokenList,
    currency.isToken ? currency : undefined
  );
  const customAdded = useIsUserAddedToken(currency);
  const balance = useCurrencyBalance(account ?? undefined, currency);

  // only show add or remove buttons if not on selected list
  return (
    <MenuItem
      style={style}
      className={`token-item-${key}`}
      onClick={() => (isSelected ? null : onSelect())}
      disabled={isSelected}
      selected={otherSelected}
    >
      <CurrencyLogo currency={currency} size={"24px"} />
      <Column>
        <Text title={currency.name} fontWeight={500}>
          {currency.symbol}
        </Text>
        <TYPE.darkGray ml="0px" fontSize={"12px"} fontWeight={300}>
          {currency.name}{" "}
          {!currency.isNative &&
            !isOnSelectedList &&
            customAdded &&
            "• Added by user"}
        </TYPE.darkGray>
      </Column>
      <TokenTags currency={currency} />
      <RowFixed style={{ justifySelf: "flex-end" }}>
        {balance ? <Balance balance={balance} /> : account ? <Loader /> : null}
      </RowFixed>
    </MenuItem>
  );
}

const BREAK_LINE = "BREAK";
type BreakLine = typeof BREAK_LINE;
function isBreakLine(x: unknown): x is BreakLine {
  return x === BREAK_LINE;
}

function BreakLineComponent({ style }: { style: CSSProperties }) {
  const theme = useTheme();
  return (
    <FixedContentRow style={style}>
      <LightGreyCard padding="8px 12px" borderRadius="8px">
        <RowBetween>
          <RowFixed>
            <TokenListLogoWrapper src={TokenListLogo} />
            <TYPE.main ml="6px" fontSize="12px" color={theme.text1}>
              Expanded results from inactive Token Lists
            </TYPE.main>
          </RowFixed>
          <QuestionHelper text="Tokens from inactive lists. Import specific tokens below or click 'Manage' to activate more lists." />
        </RowBetween>
      </LightGreyCard>
    </FixedContentRow>
  );
}

export default function CurrencyList({
  height,
  currencies,
  otherListTokens,
  selectedCurrency,
  onCurrencySelect,
  otherCurrency,
  fixedListRef,
  showImportView,
  setImportToken,
}: {
  height: number;
  currencies: Currency[];
  otherListTokens?: WrappedTokenInfo[];
  selectedCurrency?: Currency | null;
  onCurrencySelect: (currency: Currency) => void;
  otherCurrency?: Currency | null;
  fixedListRef?: MutableRefObject<FixedSizeList | undefined>;
  showImportView: () => void;
  setImportToken: (token: Token) => void;
}) {
  const itemData: (Currency | BreakLine)[] = useMemo(() => {
    if (otherListTokens && otherListTokens?.length > 0) {
      return [...currencies, BREAK_LINE, ...otherListTokens];
    }
    return currencies;
  }, [currencies, otherListTokens]);

  const Row = useCallback(
    function TokenRow({ data, index, style }) {
      const row: Currency | BreakLine = data[index];

      if (isBreakLine(row)) {
        return <BreakLineComponent style={style} />;
      }

      const currency = row;

      const isSelected = Boolean(
        currency && selectedCurrency && selectedCurrency.equals(currency)
      );
      const otherSelected = Boolean(
        currency && otherCurrency && otherCurrency.equals(currency)
      );
      const handleSelect = () => currency && onCurrencySelect(currency);

      // TODO: fix native multi chain
      const token = currency.isNative ? undefined : currency?.wrapped;

      const showImport = index > currencies.length;

      if (showImport && token) {
        return (
          <ImportRow
            style={style}
            token={token}
            showImportView={showImportView}
            setImportToken={setImportToken}
            dim
          />
        );
      } else if (currency) {
        return (
          <CurrencyRow
            style={style}
            currency={currency}
            isSelected={isSelected}
            onSelect={handleSelect}
            otherSelected={otherSelected}
          />
        );
      } else {
        return null;
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [currencies.length]
  );

  const itemKey = useCallback((index: number, data: typeof itemData) => {
    const currency = data[index];
    if (isBreakLine(currency)) return BREAK_LINE;
    return currencyKey(currency);
  }, []);

  return (
    <FixedSizeList
      height={height}
      ref={fixedListRef as any}
      width="100%"
      itemData={itemData}
      itemCount={itemData.length}
      itemSize={56}
      itemKey={itemKey}
    >
      {Row}
    </FixedSizeList>
  );
}
