import { useCallback, useEffect, useRef, useState } from "react";
import { useAccount, useConnect, useDisconnect } from "wagmi";
import { bsc } from "wagmi/chains";
import { hasWalletConnectProjectId } from "./RainbowWalletProvider";
import {
  connectInjectedWallet,
  getInjectedWallet,
  type WalletKind,
} from "./injected";
import { useWallet } from "./WalletProvider";

export type WalletConnectLoginState = "idle" | "connecting" | "signing";
export type WalletConnectLoginMode = "deeplink" | "qr";

function isMobileDevice(): boolean {
  if (typeof navigator === "undefined") return false;
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(
    navigator.userAgent || "",
  );
}

function currentUrl(): string {
  if (typeof window === "undefined") return "https://ark-library.com";
  return window.location.href;
}

function isWalletConnectUri(uri: string): boolean {
  return uri.startsWith("wc:");
}

function metaMaskWalletConnectLink(uri: string): string {
  if (!isWalletConnectUri(uri)) return uri;
  return `https://metamask.app.link/wc?uri=${encodeURIComponent(uri)}`;
}

function walletConnectDeeplink(
  kind: WalletKind | undefined,
  uri: string,
): string | null {
  if (kind === "tokenPocket") {
    return isWalletConnectUri(uri)
      ? `tpoutside://wc?uri=${encodeURIComponent(uri)}`
      : uri;
  }
  if (kind === "metaMask") {
    return metaMaskWalletConnectLink(uri);
  }
  if (kind === "imToken") {
    return isWalletConnectUri(uri)
      ? `imtokenv2://wc?uri=${encodeURIComponent(uri)}`
      : uri;
  }
  return null;
}

function inAppBrowserFallback(kind: WalletKind | undefined): string | null {
  if (kind === "imToken") {
    return `imtokenv2://navigate/DappView?url=${encodeURIComponent(currentUrl())}`;
  }
  return null;
}

function openWalletDeeplink(
  kind: WalletKind | undefined,
  deeplink: string,
): void {
  window.location.href = deeplink;
  const fallback = inAppBrowserFallback(kind);
  if (!fallback) return;
  window.setTimeout(() => {
    if (document.visibilityState === "visible") {
      window.location.href = fallback;
    }
  }, 1500);
}

function connectorMatchesWallet(
  connector: { id: string; name?: string; type?: string },
  kind: WalletKind | undefined,
): boolean {
  if (!kind) return false;
  const id = connector.id.toLowerCase();
  const name = connector.name?.toLowerCase() ?? "";
  const type = connector.type?.toLowerCase() ?? "";
  if (kind === "metaMask") {
    return id === "metamask" || type === "metamask" || name === "metamask";
  }
  if (kind === "tokenPocket") {
    return id === "tokenpocket" || name === "tokenpocket";
  }
  if (kind === "imToken") {
    return id === "imtoken" || name === "imtoken";
  }
  return false;
}

export function useWalletConnectLogin() {
  const available = hasWalletConnectProjectId();
  const { address: localAddress, loginAddress } = useWallet();
  const { address: wagmiAddress, isConnected: wagmiConnected } = useAccount();
  const { connectAsync, connectors } = useConnect();
  const { disconnectAsync } = useDisconnect();
  const [state, setState] = useState<WalletConnectLoginState>("idle");
  const [error, setError] = useState("");
  const [qrUri, setQrUri] = useState("");
  const [connectedAddress, setConnectedAddress] = useState("");
  const pendingRef = useRef(false);
  const completedAddressRef = useRef<string | null>(null);
  const cleanupMessageRef = useRef<(() => void) | null>(null);
  const cleanupPollingRef = useRef<(() => void) | null>(null);

  const reset = useCallback(() => {
    pendingRef.current = false;
    completedAddressRef.current = null;
    cleanupMessageRef.current?.();
    cleanupMessageRef.current = null;
    cleanupPollingRef.current?.();
    cleanupPollingRef.current = null;
    setState("idle");
    setError("");
    setQrUri("");
    setConnectedAddress("");
  }, []);

  useEffect(() => {
    if (!wagmiConnected || !wagmiAddress) return;
    const alreadyCompleted =
      completedAddressRef.current?.toLowerCase() === wagmiAddress.toLowerCase();
    if (alreadyCompleted) return;

    completedAddressRef.current = wagmiAddress;
    pendingRef.current = false;
    setConnectedAddress(wagmiAddress);
    setQrUri("");
    setState("idle");

    if (localAddress?.toLowerCase() !== wagmiAddress.toLowerCase()) {
      console.info("[wallet-login] wagmi account connected", {
        address: wagmiAddress,
        chain: "BNB Chain",
        chainId: bsc.id,
      });
      void loginAddress(wagmiAddress)
        .then(() => {
          console.info("[wallet-login] wallet session completed", {
            address: wagmiAddress,
          });
        })
        .catch((err: unknown) => {
          setError(err instanceof Error ? err.message : "Wallet login failed");
        });
    }
  }, [localAddress, loginAddress, wagmiAddress, wagmiConnected]);

  const start = useCallback(
    async (
      preferredWallet?: WalletKind,
      mode: WalletConnectLoginMode = "qr",
    ) => {
      if (!available) return;
      setError("");
      setQrUri("");
      setConnectedAddress("");
      completedAddressRef.current = null;
      pendingRef.current = true;
      setState("connecting");

      if (
        mode === "deeplink" &&
        preferredWallet &&
        getInjectedWallet(preferredWallet)
      ) {
        try {
          const injectedAddress = await connectInjectedWallet(preferredWallet);
          console.info("[wallet-login] injected connected", {
            preferredWallet,
            address: injectedAddress,
            chain: "BNB Chain",
            chainId: bsc.id,
          });
          await loginAddress(injectedAddress);
          setState("idle");
          return;
        } catch (err) {
          console.info("[wallet-login] injected connect fallback to wc", {
            preferredWallet,
            message: err instanceof Error ? err.message : String(err),
          });
        }
      }

      const walletConnectConnector =
        connectors.find((item) => item.type === "walletConnect") ??
        connectors.find((item) => item.id === "walletConnect");
      const walletSpecificConnector = connectors.find((item) =>
        connectorMatchesWallet(item, preferredWallet),
      );
      const connector =
        mode === "qr"
          ? walletConnectConnector
          : (walletSpecificConnector ?? walletConnectConnector);

      if (!connector) {
        pendingRef.current = false;
        setQrUri("");
        setState("idle");
        setError("WalletConnect is not available");
        return;
      }

      console.info("[wallet-login] walletconnect connector", {
        preferredWallet,
        connectorId: connector.id,
        connectorName: connector.name,
        connectorType: connector.type,
      });

      const onMessage = (message: { type: string; data?: unknown }) => {
        if (
          message.type !== "display_uri" ||
          typeof message.data !== "string"
        ) {
          return;
        }
        console.info("[wallet-login] walletconnect display_uri", {
          preferredWallet,
          connectorId: connector.id,
        });
        if (mode === "qr") {
          setQrUri(message.data);
        }
        const deeplink = walletConnectDeeplink(preferredWallet, message.data);
        if (mode === "deeplink" && deeplink && isMobileDevice()) {
          openWalletDeeplink(preferredWallet, deeplink);
        }
      };

      cleanupMessageRef.current?.();
      connector.emitter.on("message", onMessage);
      cleanupMessageRef.current = () =>
        connector.emitter.off("message", onMessage);

      cleanupPollingRef.current?.();
      const finishFromAddress = (address: string, source: string) => {
        const alreadyCompleted =
          completedAddressRef.current?.toLowerCase() === address.toLowerCase();
        if (alreadyCompleted) return;
        pendingRef.current = false;
        completedAddressRef.current = address;
        setConnectedAddress(address);
        setQrUri("");
        setState("idle");
        cleanupMessageRef.current?.();
        cleanupMessageRef.current = null;
        cleanupPollingRef.current?.();
        cleanupPollingRef.current = null;
        console.info("[wallet-login] wallet account connected", {
          source,
          preferredWallet,
          address,
          chain: "BNB Chain",
          chainId: bsc.id,
        });
        void loginAddress(address)
          .then(() => {
            console.info("[wallet-login] wallet session completed", {
              address,
            });
          })
          .catch((err: unknown) => {
            setError(
              err instanceof Error ? err.message : "Wallet login failed",
            );
          });
      };
      const pollId = window.setInterval(() => {
        void connector
          .getAccounts()
          .then((accounts) => {
            const account = accounts[0];
            if (account) finishFromAddress(account, "connector-poll");
          })
          .catch(() => undefined);
      }, 1000);
      cleanupPollingRef.current = () => window.clearInterval(pollId);

      try {
        await disconnectAsync().catch(() => undefined);
        await connector.disconnect().catch(() => undefined);
        const result = await connectAsync({ chainId: bsc.id, connector });
        const connectedAddress = result.accounts[0];
        if (!connectedAddress)
          throw new Error("Wallet connected without an account");
        finishFromAddress(connectedAddress, "connectAsync");
      } catch (err) {
        if (completedAddressRef.current) return;
        pendingRef.current = false;
        setState("idle");
        setError(
          err instanceof Error ? err.message : "WalletConnect login failed",
        );
        cleanupMessageRef.current?.();
        cleanupMessageRef.current = null;
        cleanupPollingRef.current?.();
        cleanupPollingRef.current = null;
      }
    },
    [available, connectAsync, connectors, disconnectAsync, loginAddress],
  );

  return {
    available,
    state,
    error,
    qrUri,
    address: connectedAddress,
    isConnected: Boolean(connectedAddress),
    start,
    reset,
  };
}
