import React, { createContext, useContext, useState, useEffect, useRef, useCallback } from 'react';
import { Platform } from 'react-native';
import { MwaWalletAdapter } from './wallet/mwa-adapter';
import { PhantomDeeplinkAdapter } from './wallet/phantom-deeplink';
import type { PhantomSession } from './wallet/phantom-deeplink';
import type { WalletAdapter } from './wallet/types';
import { ConnectWalletScreen } from './ui/ConnectWalletScreen';
import type { ConnectWalletScreenProps } from './ui/ConnectWalletScreen';
import type { TokenStorage } from './storage';
import { STORAGE_KEYS } from './storage';

const TAG = '[Dubs:ManagedWallet]';

/** Auto-detect redirect URI on iOS using the app's scheme from expo-linking. */
function getDefaultRedirectUri(): string | undefined {
  if (Platform.OS !== 'ios') return undefined;
  try {
    const expoLinking = require('expo-linking');
    if (expoLinking.createURL) {
      const uri = expoLinking.createURL('phantom-callback');
      console.log(TAG, 'Auto-detected redirect URI via expo-linking:', uri);
      return uri;
    }
  } catch {}
  try {
    const Constants = require('expo-constants').default;
    const scheme = Constants.expoConfig?.scheme;
    if (scheme) {
      const uri = `${scheme}://phantom-callback`;
      console.log(TAG, 'Auto-detected redirect URI via expo-constants:', uri);
      return uri;
    }
  } catch {}
  return undefined;
}

// ── Module-level Phantom adapter singleton ──
// Persists across React remounts (e.g. when the app backgrounds to open Phantom).
// This prevents the Linking listener from being torn down mid-flow.
let phantomSingleton: PhantomDeeplinkAdapter | null = null;

function getOrCreatePhantomAdapter(config: {
  redirectUri: string;
  appUrl?: string;
  cluster: string;
  storage: TokenStorage;
}): PhantomDeeplinkAdapter {
  if (!phantomSingleton) {
    console.log(TAG, 'Creating PhantomDeeplinkAdapter (singleton)');
    phantomSingleton = new PhantomDeeplinkAdapter({
      redirectUri: config.redirectUri,
      appUrl: config.appUrl,
      cluster: config.cluster,
      storage: config.storage,
      onSessionChange: (session) => {
        if (session) {
          console.log(TAG, 'Phantom session changed — saving to storage, wallet:', session.walletPublicKey);
          config.storage.setItem(STORAGE_KEYS.PHANTOM_SESSION, JSON.stringify(session)).catch((err) => {
            console.log(TAG, 'Failed to save Phantom session:', err);
          });
        } else {
          console.log(TAG, 'Phantom session cleared — removing from storage');
          config.storage.deleteItem(STORAGE_KEYS.PHANTOM_SESSION).catch((err) => {
            console.log(TAG, 'Failed to delete Phantom session:', err);
          });
        }
      },
    });
  }
  return phantomSingleton;
}

// ── Disconnect Context (internal) ──

type DisconnectFn = () => Promise<void>;

export const DisconnectContext = createContext<DisconnectFn | null>(null);

export function useDisconnect(): DisconnectFn | null {
  return useContext(DisconnectContext);
}

// ── Props ──

interface ManagedWalletProviderProps {
  appName: string;
  cluster: string;
  storage: TokenStorage;
  renderConnectScreen?: ((props: ConnectWalletScreenProps) => React.ReactNode) | false;
  /** Developer UI config overrides for the connect screen */
  accentColor?: string;
  appIcon?: string;
  tagline?: string;
  /** Deeplink redirect URI for Phantom (required on iOS, optional on Android) */
  redirectUri?: string;
  /** App URL shown in Phantom's connect screen */
  appUrl?: string;
  children: (adapter: WalletAdapter) => React.ReactNode;
}

// ── Component ──

export function ManagedWalletProvider({
  appName,
  cluster,
  storage,
  renderConnectScreen,
  accentColor,
  appIcon,
  tagline,
  redirectUri,
  appUrl,
  children,
}: ManagedWalletProviderProps) {
  const [connected, setConnected] = useState(false);
  const [connecting, setConnecting] = useState(false);
  const [isReady, setIsReady] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Determine which adapter to use:
  // - Android → MWA (Phantom + other wallets support Mobile Wallet Adapter natively)
  // - iOS → Phantom deeplinks (MWA not available on iOS)
  const resolvedRedirectUri = redirectUri || getDefaultRedirectUri();
  const usePhantom = Platform.OS === 'ios' && !!resolvedRedirectUri;

  console.log(TAG, `Platform: ${Platform.OS}, redirectUri: ${resolvedRedirectUri ?? 'none'} (explicit: ${!!redirectUri}), usePhantom: ${usePhantom}`);

  const adapterRef = useRef<WalletAdapter | null>(null);
  const transactRef = useRef<any>(null);

  // Lazily create adapter — Phantom uses a module-level singleton to survive remounts
  if (!adapterRef.current) {
    if (usePhantom) {
      adapterRef.current = getOrCreatePhantomAdapter({
        redirectUri: resolvedRedirectUri!,
        appUrl,
        cluster,
        storage,
      });
    } else {
      console.log(TAG, 'Creating MwaWalletAdapter');
      adapterRef.current = new MwaWalletAdapter({
        transact: (...args: any[]) => {
          if (!transactRef.current) {
            throw new Error(
              '@dubsdotapp/expo: @solana-mobile/mobile-wallet-adapter-protocol-web3js is required. ' +
              'Install it with: npm install @solana-mobile/mobile-wallet-adapter-protocol-web3js',
            );
          }
          return transactRef.current(...args);
        },
        appIdentity: { name: appName, uri: appUrl },
        cluster,
        onAuthTokenChange: (token) => {
          if (token) {
            storage.setItem(STORAGE_KEYS.MWA_AUTH_TOKEN, token).catch(() => {});
          } else {
            storage.deleteItem(STORAGE_KEYS.MWA_AUTH_TOKEN).catch(() => {});
          }
        },
      });
    }
  }
  const adapter = adapterRef.current;

  // Restore session / dynamic-import on mount
  useEffect(() => {
    let cancelled = false;

    (async () => {
      if (usePhantom) {
        const phantom = adapter as PhantomDeeplinkAdapter;

        // If the singleton is already connected (e.g. after returning from Phantom), skip restore
        if (phantom.connected) {
          console.log(TAG, 'Phantom adapter already connected, skipping restore');
          if (!cancelled) {
            setConnected(true);
            setIsReady(true);
          }
          return;
        }

        // Check for cold-start recovery (Android killed the app during connect)
        const coldStartUrl = phantom.consumeColdStartUrl();
        if (coldStartUrl) {
          try {
            console.log(TAG, 'Cold-start URL detected, attempting recovery');
            await phantom.completeConnectFromColdStart(coldStartUrl);
            if (!cancelled) {
              console.log(TAG, 'Cold-start recovery succeeded');
              setConnected(true);
              setIsReady(true);
            }
            return;
          } catch (err) {
            console.log(TAG, 'Cold-start recovery failed:', err instanceof Error ? err.message : err);
            // Fall through to normal session restore
          }
        }

        try {
          const savedSession = await storage.getItem(STORAGE_KEYS.PHANTOM_SESSION);
          if (savedSession && !cancelled) {
            const session: PhantomSession = JSON.parse(savedSession);
            console.log(TAG, 'Found saved Phantom session, restoring for wallet:', session.walletPublicKey);
            phantom.restoreSession(session);
            if (!cancelled) {
              console.log(TAG, 'Phantom reconnected from saved session');
              setConnected(true);
            }
          } else {
            console.log(TAG, 'No saved Phantom session');
          }
        } catch (err) {
          console.log(TAG, 'Phantom session restore failed:', err instanceof Error ? err.message : err);
          await storage.deleteItem(STORAGE_KEYS.PHANTOM_SESSION).catch(() => {});
        } finally {
          if (!cancelled) {
            console.log(TAG, 'Phantom init complete, marking ready');
            setIsReady(true);
          }
        }
      } else {
        console.log(TAG, 'MWA path — dynamic-importing transact...');
        try {
          const mwa = await import('@solana-mobile/mobile-wallet-adapter-protocol-web3js');
          if (cancelled) return;
          transactRef.current = mwa.transact;
          console.log(TAG, 'MWA transact loaded');
        } catch {
          console.log(TAG, 'MWA not installed — transact will throw on use');
        }

        try {
          const savedToken = await storage.getItem(STORAGE_KEYS.MWA_AUTH_TOKEN);
          const savedAddress = await storage.getItem(STORAGE_KEYS.MWA_WALLET_ADDRESS);
          if (savedToken && savedAddress && !cancelled) {
            console.log(TAG, 'Found saved MWA session, restoring silently for wallet:', savedAddress);
            (adapter as MwaWalletAdapter).restoreSession(savedToken, savedAddress);
            setConnected(true);
          } else {
            console.log(TAG, 'No saved MWA session (token or address missing)');
          }
        } catch (err) {
          console.log(TAG, 'MWA session restore failed:', err instanceof Error ? err.message : err);
          await storage.deleteItem(STORAGE_KEYS.MWA_AUTH_TOKEN).catch(() => {});
          await storage.deleteItem(STORAGE_KEYS.MWA_WALLET_ADDRESS).catch(() => {});
        } finally {
          if (!cancelled) {
            console.log(TAG, 'MWA init complete, marking ready');
            setIsReady(true);
          }
        }
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [adapter, storage, usePhantom]);

  const handleConnect = useCallback(async () => {
    console.log(TAG, 'handleConnect() — user tapped connect');
    setConnecting(true);
    setError(null);
    try {
      await adapter.connect!();
      const walletAddress = adapter.publicKey?.toBase58();
      console.log(TAG, 'handleConnect() — success, wallet:', walletAddress);
      // Save wallet address for silent session restore on next launch (MWA only)
      if (!usePhantom && walletAddress) {
        storage.setItem(STORAGE_KEYS.MWA_WALLET_ADDRESS, walletAddress).catch(() => {});
      }
      setConnected(true);
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Connection failed';
      console.log(TAG, 'handleConnect() — failed:', message);
      setError(message);
    } finally {
      setConnecting(false);
    }
  }, [adapter, storage, usePhantom]);

  const disconnect = useCallback(async () => {
    console.log(TAG, 'disconnect() — clearing all state');
    adapter.disconnect?.();
    // Destroy and reset the singleton so a fresh adapter is created on next connect
    if (usePhantom && phantomSingleton) {
      console.log(TAG, 'Destroying Phantom singleton');
      phantomSingleton.destroy();
      phantomSingleton = null;
      adapterRef.current = null;
    }
    await storage.deleteItem(STORAGE_KEYS.MWA_AUTH_TOKEN).catch(() => {});
    await storage.deleteItem(STORAGE_KEYS.MWA_WALLET_ADDRESS).catch(() => {});
    await storage.deleteItem(STORAGE_KEYS.PHANTOM_SESSION).catch(() => {});
    await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {});
    await storage.deleteItem(STORAGE_KEYS.PHANTOM_CONNECT_IN_FLIGHT).catch(() => {});
    setConnected(false);
    console.log(TAG, 'disconnect() — done');
  }, [adapter, storage, usePhantom]);

  // Show nothing until we've attempted silent reconnect
  if (!isReady) {
    console.log(TAG, 'Not ready yet — rendering null');
    return null;
  }

  // Not connected — show connect screen
  if (!connected) {
    if (renderConnectScreen === false) {
      return null;
    }
    const connectProps: ConnectWalletScreenProps = {
      onConnect: handleConnect,
      connecting,
      error,
      appName,
      accentColor,
      appIcon,
      tagline,
    };
    if (renderConnectScreen) {
      return <>{renderConnectScreen(connectProps)}</>;
    }
    return <ConnectWalletScreen {...connectProps} />;
  }

  // Connected — render children with adapter + disconnect
  console.log(TAG, 'Rendering children — connected with wallet:', adapter.publicKey?.toBase58());
  return (
    <DisconnectContext.Provider value={disconnect}>
      {children(adapter)}
    </DisconnectContext.Provider>
  );
}
