import React, { createContext, useContext, useMemo, useCallback, useState, useEffect } from 'react';
import { ThemeOverrideProvider } from './ui/theme';
import type { DubsTheme } from './ui/theme';
import { Connection } from '@solana/web3.js';
import { DubsClient } from './client';
import { NETWORK_CONFIG } from './constants';
import type { DubsNetwork } from './constants';
import type { WalletAdapter } from './wallet/types';
import type { TokenStorage } from './storage';
import { createSecureStoreStorage, STORAGE_KEYS } from './storage';
import { ManagedWalletProvider, useDisconnect } from './managed-wallet';
import { AuthGate } from './ui/AuthGate';
import type { RegistrationScreenProps } from './ui/AuthGate';
import type { ConnectWalletScreenProps } from './ui/ConnectWalletScreen';
import type { AuthStatus, UiConfig } from './types';

// ── Context ──

export interface DubsContextValue {
  client: DubsClient;
  wallet: WalletAdapter;
  connection: Connection;
  appName: string;
  network: DubsNetwork;
  disconnect: () => Promise<void>;
  uiConfig: UiConfig;
  pushEnabled: boolean;
}

const DubsContext = createContext<DubsContextValue | null>(null);

// ── Props ──

export interface DubsProviderProps {
  apiKey: string;
  children: React.ReactNode;
  appName?: string;
  network?: DubsNetwork;
  /** Escape hatch: bring your own wallet adapter. Disables managed MWA. */
  wallet?: WalletAdapter;
  /** Escape hatch: custom token persistence. Defaults to expo-secure-store. */
  tokenStorage?: TokenStorage;
  /** Escape hatch: override base URL (takes precedence over network). */
  baseUrl?: string;
  /** Escape hatch: override RPC URL (takes precedence over network). */
  rpcUrl?: string;
  /** Custom connect screen, or false to hide it entirely (headless mode). */
  renderConnectScreen?: ((props: ConnectWalletScreenProps) => React.ReactNode) | false;
  /** Custom loading screen shown during auth. */
  renderLoading?: (status: AuthStatus) => React.ReactNode;
  /** Custom error screen shown on auth failure. */
  renderError?: (error: Error, retry: () => void) => React.ReactNode;
  /** Custom registration screen. */
  renderRegistration?: (props: RegistrationScreenProps) => React.ReactNode;
  /** Set to false to skip AuthGate and connect screen (headless/BYOA mode). Default: true. */
  managed?: boolean;
  /** Deeplink redirect URI for Phantom wallet (required for iOS support). */
  redirectUri?: string;
  /** App URL shown in Phantom's connect screen. */
  appUrl?: string;
  /** Enable Dubs push notifications. Default: true. Set false if your app manages its own Firebase/push. */
  pushEnabled?: boolean;
}

// ── Provider ──

export function DubsProvider({
  apiKey,
  children,
  appName = 'Dubs',
  network = 'mainnet-beta',
  wallet: externalWallet,
  tokenStorage,
  baseUrl: baseUrlOverride,
  rpcUrl: rpcUrlOverride,
  renderConnectScreen,
  renderLoading,
  renderError,
  renderRegistration,
  managed = true,
  redirectUri,
  appUrl,
  pushEnabled = true,
}: DubsProviderProps) {
  // Resolve network config — explicit props override network defaults
  const config = NETWORK_CONFIG[network];
  const baseUrl = baseUrlOverride || config.baseUrl;
  const rpcUrl = rpcUrlOverride || config.rpcUrl;

  // Create stable instances
  const client = useMemo(() => new DubsClient({ apiKey, baseUrl }), [apiKey, baseUrl]);
  const storage = useMemo(() => tokenStorage || createSecureStoreStorage(), [tokenStorage]);

  // Fetch developer UI config on mount (silent fail = default theme)
  // Also auto-detects sandbox/production environment from the API key
  const [uiConfig, setUiConfig] = useState<UiConfig | null>(null);
  const [resolvedNetwork, setResolvedNetwork] = useState<DubsNetwork>(network);
  useEffect(() => {
    client.getAppConfig()
      .then((cfg) => {
        console.log('[DubsProvider] UI config loaded:', JSON.stringify(cfg));
        setUiConfig(cfg);
        // Auto-detect network from API key environment (sandbox → devnet)
        // Only override if the user didn't explicitly set network or rpcUrl
        if (cfg.environment === 'sandbox' && network === 'mainnet-beta' && !rpcUrlOverride) {
          console.log('[DubsProvider] Sandbox API key detected — auto-switching to devnet');
          setResolvedNetwork('devnet');
        }
      })
      .catch((err) => {
        console.log('[DubsProvider] UI config fetch failed, using defaults:', err?.message);
        setUiConfig({});
      });
  }, [client]);

  // Use resolved network (may differ from prop if sandbox was auto-detected)
  const resolvedConfig = NETWORK_CONFIG[resolvedNetwork];
  const resolvedRpcUrl = rpcUrlOverride || resolvedConfig.rpcUrl;
  const cluster = resolvedConfig.cluster;

  // Connection uses the resolved RPC (auto-switched to devnet for sandbox keys)
  const connection = useMemo(() => new Connection(resolvedRpcUrl, { commitment: 'confirmed' }), [resolvedRpcUrl]);

  // Wait for config before rendering so accent color is applied on first paint
  if (uiConfig === null) return null;

  // Build theme overrides from server config so all SDK components respect the tint
  const themeOverrides: Partial<DubsTheme> = {};
  if (uiConfig.accentColor) {
    themeOverrides.accent = uiConfig.accentColor;
  }

  // ── Path A: External wallet provided (BYOA) ──
  if (externalWallet) {
    return (
      <ThemeOverrideProvider value={themeOverrides}>
        <ExternalWalletProvider
          client={client}
          connection={connection}
          wallet={externalWallet}
          appName={uiConfig.appName || appName}
          network={resolvedNetwork}
          storage={storage}
          managed={managed}
          renderLoading={renderLoading}
          renderError={renderError}
          renderRegistration={renderRegistration}
          accentColor={uiConfig.accentColor}
          uiConfig={uiConfig}
          pushEnabled={pushEnabled}
        >
          {children}
        </ExternalWalletProvider>
      </ThemeOverrideProvider>
    );
  }

  // ── Path B: Managed MWA wallet ──
  return (
    <ThemeOverrideProvider value={themeOverrides}>
      <ManagedWalletProvider
        appName={uiConfig.appName || appName}
        cluster={cluster}
        storage={storage}
        renderConnectScreen={renderConnectScreen}
        accentColor={uiConfig.accentColor}
        appIcon={uiConfig.appIcon}
        tagline={uiConfig.tagline}
        redirectUri={redirectUri}
        appUrl={appUrl || uiConfig.appUrl}
      >
        {(adapter) => (
          <ManagedInner
            client={client}
            connection={connection}
            wallet={adapter}
            appName={uiConfig.appName || appName}
            network={resolvedNetwork}
            storage={storage}
            renderLoading={renderLoading}
            renderError={renderError}
            renderRegistration={renderRegistration}
            accentColor={uiConfig.accentColor}
            uiConfig={uiConfig}
            pushEnabled={pushEnabled}
          >
            {children}
          </ManagedInner>
        )}
      </ManagedWalletProvider>
    </ThemeOverrideProvider>
  );
}

// ── ManagedInner: context + AuthGate for managed wallet path ──

function ManagedInner({
  client,
  connection,
  wallet,
  appName,
  network,
  storage,
  renderLoading,
  renderError,
  renderRegistration,
  accentColor,
  uiConfig,
  pushEnabled,
  children,
}: {
  client: DubsClient;
  connection: Connection;
  wallet: WalletAdapter;
  appName: string;
  network: DubsNetwork;
  storage: TokenStorage;
  renderLoading?: (status: AuthStatus) => React.ReactNode;
  renderError?: (error: Error, retry: () => void) => React.ReactNode;
  renderRegistration?: (props: RegistrationScreenProps) => React.ReactNode;
  accentColor?: string;
  uiConfig: UiConfig;
  pushEnabled: boolean;
  children: React.ReactNode;
}) {
  const managedDisconnect = useDisconnect();

  const disconnect = useCallback(async () => {
    // Clear client JWT
    client.setToken(null);
    // Delegate to managed wallet (clears adapter + storage)
    await managedDisconnect?.();
  }, [client, managedDisconnect]);

  const value = useMemo<DubsContextValue>(
    () => ({ client, wallet, connection, appName, network, disconnect, uiConfig, pushEnabled }),
    [client, wallet, connection, appName, network, disconnect, uiConfig, pushEnabled],
  );

  return (
    <DubsContext.Provider value={value}>
      <AuthGate
        onSaveToken={(token) => {
          if (token) return storage.setItem(STORAGE_KEYS.JWT_TOKEN, token);
          return storage.deleteItem(STORAGE_KEYS.JWT_TOKEN);
        }}
        onLoadToken={() => storage.getItem(STORAGE_KEYS.JWT_TOKEN)}
        renderLoading={renderLoading}
        renderError={renderError}
        renderRegistration={renderRegistration}
        appName={appName}
        accentColor={accentColor}
      >
        {children}
      </AuthGate>
    </DubsContext.Provider>
  );
}

// ── ExternalWalletProvider: for BYOA path ──

function ExternalWalletProvider({
  client,
  connection,
  wallet,
  appName,
  network,
  storage,
  managed,
  renderLoading,
  renderError,
  renderRegistration,
  accentColor,
  uiConfig,
  pushEnabled,
  children,
}: {
  client: DubsClient;
  connection: Connection;
  wallet: WalletAdapter;
  appName: string;
  network: DubsNetwork;
  storage: TokenStorage;
  managed: boolean;
  renderLoading?: (status: AuthStatus) => React.ReactNode;
  renderError?: (error: Error, retry: () => void) => React.ReactNode;
  renderRegistration?: (props: RegistrationScreenProps) => React.ReactNode;
  accentColor?: string;
  uiConfig: UiConfig;
  pushEnabled: boolean;
  children: React.ReactNode;
}) {
  const disconnect = useCallback(async () => {
    client.setToken(null);
    await storage.deleteItem(STORAGE_KEYS.JWT_TOKEN).catch(() => {});
    await wallet.disconnect?.();
  }, [client, storage, wallet]);

  const value = useMemo<DubsContextValue>(
    () => ({ client, wallet, connection, appName, network, disconnect, uiConfig, pushEnabled }),
    [client, wallet, connection, appName, network, disconnect, uiConfig, pushEnabled],
  );

  if (!managed) {
    // Headless mode — just context, no AuthGate
    return <DubsContext.Provider value={value}>{children}</DubsContext.Provider>;
  }

  return (
    <DubsContext.Provider value={value}>
      <AuthGate
        onSaveToken={(token) => {
          if (token) return storage.setItem(STORAGE_KEYS.JWT_TOKEN, token);
          return storage.deleteItem(STORAGE_KEYS.JWT_TOKEN);
        }}
        onLoadToken={() => storage.getItem(STORAGE_KEYS.JWT_TOKEN)}
        renderLoading={renderLoading}
        renderError={renderError}
        renderRegistration={renderRegistration}
        appName={appName}
        accentColor={accentColor}
      >
        {children}
      </AuthGate>
    </DubsContext.Provider>
  );
}

// ── Hooks ──

export function useDubs(): DubsContextValue {
  const ctx = useContext(DubsContext);
  if (!ctx) {
    throw new Error('useDubs must be used within a <DubsProvider>');
  }
  return ctx;
}

export function useAppConfig(): UiConfig {
  const ctx = useContext(DubsContext);
  return ctx?.uiConfig || {};
}
