import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { OrgConfigFeatureGate } from 'src/application/models/api/orgConfigResults';
import { EnviveConfigService, EnviveServiceConfig } from 'src/services/enviveConfigService';
import { AmplitudeService } from 'src/services/amplitudeService/amplitudeService';
import Logger from 'src/application/logging/logger';
import { UserIdentityService } from 'src/services/userIdentityService';
import { AmplitudeProvider } from '../amplitudeContext';
import { EnviveConfigProvider } from '../enviveConfigContext';
import { EnviveCssProvider } from '../enviveCssContext';
import { FeatureFlagService, FeatureFlagServiceProvider } from '../featureFlagServiceContext';
import { GraphQLProvider, GraphQlConfigValues } from '../graphqlContext';
import { LocalStorageProvider } from '../localStorageContext';
import { NewOrgConfigProvider } from '../newOrgConfigContext';
import { UserIdentityProvider } from '../userIdentityContext';
import { EnviveAgent } from './types';
import { SearchProvider } from '../searchContext';
import { SalesAgentProvider } from '../salesAgentContext/salesAgentContext';
import { SystemSettingsContextProvider } from '../systemSettingsContext';
import { HardcopyProvider, HardcopyResponse } from '../hardcopyContext';
import { WidgetTypeV3 } from '../typesV3';
import { WidgetConfigProvider } from '../widgetConfigContext';
import { PageProvider } from '../pageContext';
import { WindowChatToggleBinder } from './WindowChatToggleBinder';

const logger = new Logger('enviveContext');

interface AgentWrapperProps {
  children: ReactNode;
  enabledAgents: EnviveAgent[];
}

export interface ExtensionStatusCallbacks {
  onInjectionLoading?: () => Promise<unknown> | void;
  onInjectionSuccess?: () => Promise<unknown> | void;
  onInjectionError?: (error: Error) => Promise<unknown> | void;
}

interface EnviveProviderProps extends AgentWrapperProps {
  amplitudeApiKey?: string;
  dataResidency?: string;
  env?: string;
  baseUrl?: string;
  reactAppName?: string;
  cdnUrl?: string;
  contextSource?: string;
  orgLevelApiKey?: string;
  orgShortName?: string;
  identifyingPrefix: string;
  featureOverrides?: Record<string, boolean>;
  variantUrlOverride?: string;
  variantInfoOverride?: any;
  show?: boolean;
  enviveOn?: boolean;
  publicKey?: string;
  featureGates?: OrgConfigFeatureGate[]; // New prop for feature gates
  featureFlagService?: FeatureFlagService;
  enviveConfigService?: EnviveConfigService;
  amplitudeService?: AmplitudeService;
  mockV3ConfigToDeprecatedConfig?: boolean;
  requestV3Config?: boolean;
  userIdentityService?: UserIdentityService;
  previewMode?: boolean;
  overrideConfig?: GraphQlConfigValues;
  mockSalesAgentData?: any;
  hardcopyOverride?: Partial<Record<WidgetTypeV3, HardcopyResponse>>;
  extensionService?: ExtensionStatusCallbacks;
}

const SearchAgentWrapper: React.FC<AgentWrapperProps> = ({ children, enabledAgents }) => {
  if (!enabledAgents.includes(EnviveAgent.SearchAgent)) {
    return children;
  }
  return <SearchProvider>{children}</SearchProvider>;
};

interface SalesAgentWrapperProps extends AgentWrapperProps {
  previewMode?: boolean;
  showDebugBar: boolean;
  mockSalesAgentData?: any;
}

const SalesAgentWrapper: React.FC<SalesAgentWrapperProps> = ({
  children,
  enabledAgents,
  previewMode,
  showDebugBar,
  mockSalesAgentData,
}) => {
  if (!enabledAgents.includes(EnviveAgent.SalesAgent)) {
    return children;
  }
  return (
    <SystemSettingsContextProvider showDebugBar={showDebugBar}>
      <SalesAgentProvider
        previewMode={previewMode}
        mockData={mockSalesAgentData}
      >
        {children}
      </SalesAgentProvider>
    </SystemSettingsContextProvider>
  );
};

export const EnviveProvider: React.FC<EnviveProviderProps> = ({
  children,
  previewMode = false,
  overrideConfig,
  enabledAgents,
  enviveConfigService: inputEnviveConfigService,
  featureFlagService: inputFeatureFlagService,
  amplitudeService: inputAmplitudeService,
  userIdentityService: inputUserIdentityService,
  mockV3ConfigToDeprecatedConfig = false,
  requestV3Config = false,
  mockSalesAgentData,
  hardcopyOverride,
  extensionService,
  ...config
}) => {
  const [userId, setUserId] = useState<string>('');
  const didReportSuccessRef = useRef(false);

  const invokeExtensionCallback = useCallback((callback?: () => Promise<unknown> | void) => {
    if (!callback) {
      return;
    }

    Promise.resolve(callback()).catch(error => {
      logger.logDebug('Extension status callback failed', error);
    });
  }, []);

  const invokeExtensionErrorCallback = useCallback(
    (error: Error) => {
      if (!extensionService?.onInjectionError) {
        return;
      }

      Promise.resolve(extensionService.onInjectionError(error)).catch(callbackError => {
        logger.logDebug('Extension error callback failed', callbackError);
      });
    },
    [extensionService],
  );

  const userIdService = useMemo(
    () =>
      inputUserIdentityService ??
      new UserIdentityService({ apiKey: config?.amplitudeApiKey || '' }),
    [config?.amplitudeApiKey, inputUserIdentityService],
  );

  useEffect(() => {
    const fetchUserId = async () => {
      const id = await userIdService.getUserIdOrDefault();
      setUserId(id);
    };

    fetchUserId();
  }, [config.amplitudeApiKey, inputUserIdentityService, userIdService]);

  const namespace = 'spiffy-react-components'; // TODO: Should this be different from the current namespace?
  const source = 'app'; // TODO: Make this dependent upon the "spiffy_on" query param
  const [enviveServiceConfig, setEnviveServiceConfig] = useState<EnviveServiceConfig | null>(null);

  useEffect(() => {
    invokeExtensionCallback(extensionService?.onInjectionLoading);

    // Really not happy with this approach, but I'm seeing 429 errors in the tests
    // because of the rate limiting on the API.
    const enviveConfigService =
      inputEnviveConfigService ??
      new EnviveConfigService({
        baseUrl: config?.baseUrl || '',
        apiKey: config?.orgLevelApiKey || '',
        userId,
        namespace: namespace || '',
        source: source || '',
      });
    const fetchEnviveConfig = async () => {
      try {
        const newConfig = await enviveConfigService.getEnviveConfig();
        setEnviveServiceConfig(newConfig);
      } catch (error) {
        const resolvedError =
          error instanceof Error ? error : new Error('Failed to load envive config');
        logger.logError('Failed to initialize EnviveProvider', resolvedError);
        invokeExtensionErrorCallback(resolvedError);
      }
    };

    fetchEnviveConfig();
  }, [
    config,
    userId,
    namespace,
    source,
    inputEnviveConfigService,
    extensionService,
    invokeExtensionCallback,
    invokeExtensionErrorCallback,
  ]);

  useEffect(() => {
    if (!enviveServiceConfig || didReportSuccessRef.current) {
      return;
    }

    didReportSuccessRef.current = true;
    invokeExtensionCallback(extensionService?.onInjectionSuccess);
  }, [enviveServiceConfig, extensionService, invokeExtensionCallback]);

  const enviveConfig = useMemo(
    () => ({
      ...config,
      featureGates: enviveServiceConfig?.gates || [],
      orgShortName: enviveServiceConfig?.org?.org?.short_name || '',
      orgId: enviveServiceConfig?.org?.org?.id || '',
    }),
    [config, enviveServiceConfig],
  );
  logger.logDebug('enviveContext: enviveServiceConfig', enviveServiceConfig, config);
  logger.logDebug('enviveContext: enviveConfig', enviveConfig);

  const featureFlagService = useMemo(
    () => inputFeatureFlagService ?? new FeatureFlagService(enviveConfig.featureGates),
    [enviveConfig.featureGates, inputFeatureFlagService],
  );

  if (enviveServiceConfig === null || enviveConfig === null) {
    return null;
  }

  return (
    <EnviveConfigProvider {...enviveConfig}>
      <GraphQLProvider
        mockV3ConfigToDeprecatedConfig={mockV3ConfigToDeprecatedConfig}
        requestV3Config={requestV3Config}
      >
        <NewOrgConfigProvider overrideConfig={overrideConfig}>
          <LocalStorageProvider>
            <FeatureFlagServiceProvider featureFlagService={featureFlagService}>
              <UserIdentityProvider userIdService={userIdService}>
                <AmplitudeProvider externalAmplitudeService={inputAmplitudeService}>
                  <PageProvider
                    previewMode={previewMode}
                    onUrlResolverNotReady={extensionService?.onInjectionError}
                  >
                    <WidgetConfigProvider>
                      <HardcopyProvider hardcopyOverride={hardcopyOverride}>
                        <EnviveCssProvider>
                          <SearchAgentWrapper enabledAgents={enabledAgents}>
                            <WindowChatToggleBinder />
                            <SalesAgentWrapper
                              enabledAgents={enabledAgents}
                              showDebugBar={config.enviveOn || false}
                              previewMode={previewMode}
                              mockSalesAgentData={mockSalesAgentData}
                            >
                              {children}
                            </SalesAgentWrapper>
                          </SearchAgentWrapper>
                        </EnviveCssProvider>
                      </HardcopyProvider>
                    </WidgetConfigProvider>
                  </PageProvider>
                </AmplitudeProvider>
              </UserIdentityProvider>
            </FeatureFlagServiceProvider>
          </LocalStorageProvider>
        </NewOrgConfigProvider>
      </GraphQLProvider>
    </EnviveConfigProvider>
  );
};
