import {
  type AdapterNotDetectedWallet,
  type AdapterWallet,
  isRedirectable,
  shouldUseFallbackWallet,
  WalletReadyState,
} from "@aptos-labs/wallet-adapter-core";
import { Slot } from "@radix-ui/react-slot";
import { createContext, forwardRef, useCallback, useContext } from "react";
import { useWallet } from "../useWallet";
import { createHeadlessComponent, type HeadlessComponentProps } from "./utils";

export interface WalletItemProps extends HeadlessComponentProps {
  /** The wallet option to be displayed. */
  wallet: AdapterWallet | AdapterNotDetectedWallet;
  /** A callback to be invoked when the wallet is connected. */
  onConnect?: () => void;
  /**
   * When `true`, wallets will be shown on mobile even if they don't have
   * a `deeplinkProvider`. By default mobile wallets without a deeplink
   * provider are hidden.
   *
   * @default false
   */
  showAllOnMobile?: boolean;
}

function useWalletItemContext(displayName: string) {
  const context = useContext(WalletItemContext);

  if (!context) {
    throw new Error(`\`${displayName}\` must be used within \`WalletItem\``);
  }

  return context;
}

const WalletItemContext = createContext<{
  wallet: AdapterWallet | AdapterNotDetectedWallet;
  connectWallet: () => void;
} | null>(null);

const Root = forwardRef<HTMLDivElement, WalletItemProps>(
  (
    {
      wallet,
      onConnect,
      showAllOnMobile = false,
      className,
      asChild,
      children,
    },
    ref,
  ) => {
    const { connect } = useWallet();

    const isWalletReady = wallet.readyState === WalletReadyState.Installed;

    const mobileSupport =
      "deeplinkProvider" in wallet && wallet.deeplinkProvider;

    const connectWallet = useCallback(() => {
      const connectionWallet = shouldUseFallbackWallet(wallet)
        ? wallet.fallbackWallet
        : wallet;
      if (!connectionWallet) return;
      connect(connectionWallet.name);
      onConnect?.();
    }, [wallet, connect, onConnect]);

    if (
      !isWalletReady &&
      isRedirectable() &&
      !mobileSupport &&
      !showAllOnMobile
    )
      return null;

    const Component = asChild ? Slot : "div";

    return (
      <WalletItemContext.Provider value={{ wallet, connectWallet }}>
        <Component ref={ref} className={className}>
          {children}
        </Component>
      </WalletItemContext.Provider>
    );
  },
);
Root.displayName = "WalletItem";

const Icon = createHeadlessComponent(
  "WalletItem.Icon",
  "img",
  (displayName) => {
    const context = useWalletItemContext(displayName);

    return {
      src: context.wallet.icon,
      alt: `${context.wallet.name} icon`,
    };
  },
);

const Name = createHeadlessComponent(
  "WalletItem.Name",
  "div",
  (displayName) => {
    const context = useWalletItemContext(displayName);

    return {
      children: context.wallet.name,
    };
  },
);

const ConnectButton = createHeadlessComponent(
  "WalletItem.ConnectButton",
  "button",
  (displayName) => {
    const context = useWalletItemContext(displayName);

    return {
      onClick: context.connectWallet,
      children: "Connect",
    };
  },
);

const InstallLink = createHeadlessComponent(
  "WalletItem.InstallLink",
  "a",
  (displayName) => {
    const context = useWalletItemContext(displayName);

    return {
      href: context.wallet.url,
      target: "_blank",
      rel: "noopener noreferrer",
      children: "Install",
    };
  },
);

/** A headless component for rendering a wallet option's name, icon, and either connect button or install link. */
export const WalletItem = Object.assign(Root, {
  Icon,
  Name,
  ConnectButton,
  InstallLink,
});
