import type { ReactNode } from 'react';
import { useMemo } from 'react';
import { AuthProvider } from './AuthProvider';
import type { User, DevAuthOverride } from './types';
import type { DevUserState, DevUserContextValue } from '@lilith/ui-dev-tools';

/**
 * Function to map dev user state to a mock User object.
 * Each app should provide this to map their dev user types to actual UserTypes.
 */
export type DevUserMapper = (devUser: DevUserState) => User;

interface AuthProviderWithDevBridgeProps {
  children: ReactNode;
  /** SSO service URL (required for real auth) */
  ssoUrl: string;
  /**
   * Function to map dev user state to a User object.
   * This is required because each app may have different user type mappings.
   *
   * @example
   * ```tsx
   * const mapDevUser: DevUserMapper = (devUser) => ({
   *   id: devUser.userId || 'dev-user',
   *   email: `${devUser.primaryType}@dev.local`,
   *   username: devUser.displayName,
   *   role: UserRole.USER,
   *   userTypes: devUser.userTypes.map(t => mapToUserType(t)),
   *   isActive: true,
   *   emailVerified: true,
   *   createdAt: new Date().toISOString(),
   *   updatedAt: new Date().toISOString(),
   * });
   * ```
   */
  mapDevUser: DevUserMapper;
}

/**
 * Inner component that tries to use DevUser context.
 * If context is unavailable, falls back to just AuthProvider.
 */
function AuthProviderWithDevBridgeInner({
  children,
  ssoUrl,
  mapDevUser,
}: AuthProviderWithDevBridgeProps) {
  // Try to get dev user context - this may throw if DevUserProvider is from a different module instance
  let devUser: DevUserContextValue | null = null;
  let contextError = false;

  try {
    // Dynamic require to avoid static analysis issues
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const devTools = require('@lilith/ui-dev-tools');
    devUser = devTools.useDevUser();
  } catch {
    contextError = true;
  }

  const devOverride = useMemo<DevAuthOverride | undefined>(() => {
    // Only create override when context is available, in dev mode, AND user is authenticated
    if (contextError || !devUser || !devUser.isDevMode || !devUser.isAuthenticated) {
      return undefined;
    }

    // Use the provided mapper to create the mock user
    const mockUser = mapDevUser(devUser);

    return {
      isAuthenticated: true,
      user: mockUser,
    };
  }, [devUser, mapDevUser, contextError]);

  return (
    <AuthProvider ssoUrl={ssoUrl} devOverride={devOverride}>
      {children}
    </AuthProvider>
  );
}

/**
 * AuthProvider wrapper that bridges DevUserProvider state to AuthProvider.
 *
 * When dev auth is active (import.meta.env.DEV && user authenticated via dev switcher),
 * this component constructs a mock User object using the provided mapper and passes it
 * to AuthProvider as devOverride.
 *
 * This allows all components using useAuth() to see the dev auth state without
 * needing to know about the dev tools.
 *
 * NOTE: This component is resilient to module instance duplication issues that
 * can occur with Vite and other bundlers. If DevUserProvider is not found,
 * it will fall back to standard AuthProvider behavior.
 *
 * @example
 * ```tsx
 * <DevUserProvider userTypes={DEV_USER_TYPES} storageKey="myapp_dev_user">
 *   <AuthProviderWithDevBridge ssoUrl={config.ssoUrl} mapDevUser={mapDevUser}>
 *     <App />
 *   </AuthProviderWithDevBridge>
 *   <DevUserSwitcher />
 * </DevUserProvider>
 * ```
 */
export function AuthProviderWithDevBridge(props: AuthProviderWithDevBridgeProps) {
  return <AuthProviderWithDevBridgeInner {...props} />;
}
