import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import { Provider, useAtomValue } from 'jotai';
import { enviveConfigAtom } from 'src/atoms/envive/enviveConfig';
import Logger from 'src/application/logging/logger';
import { EnviveConfigProvider } from '../enviveConfigContext';
import { useEnviveConfig } from '../useEnviveConfig';

// Mock the Logger to avoid console output in tests
vi.spyOn(Logger.prototype, 'logDebug').mockImplementation(() => {});

// Mock component that uses the useEnviveConfig hook
const MockComponent: React.FC = () => {
  const config = useEnviveConfig();

  return (
    <div data-testid="mock-component">
      <div data-testid="amplitude-api-key">{config?.amplitudeApiKey || 'not-set'}</div>
      <div data-testid="base-url">{config?.baseUrl || 'not-set'}</div>
      <div data-testid="org-short-name">{config?.orgShortName || 'not-set'}</div>
      <div data-testid="identifying-prefix">{config?.identifyingPrefix || 'not-set'}</div>
      <div data-testid="env">{config?.env || 'not-set'}</div>
      <div data-testid="cdn-url">{config?.cdnUrl || 'not-set'}</div>
      <div data-testid="data-residency">{config?.dataResidency || 'not-set'}</div>
      <div data-testid="react-app-name">{config?.reactAppName || 'not-set'}</div>
      <div data-testid="context-source">{config?.contextSource || 'not-set'}</div>
      <div data-testid="org-level-api-key">{config?.orgLevelApiKey || 'not-set'}</div>
      <div data-testid="public-key">{config?.publicKey || 'not-set'}</div>
      <div data-testid="show">{config?.show?.toString() || 'not-set'}</div>
    </div>
  );
};

// Component that directly reads from the atom to verify it's set
const AtomReaderComponent: React.FC = () => {
  const config = useAtomValue(enviveConfigAtom);

  return (
    <div data-testid="atom-reader">
      <div data-testid="atom-amplitude-api-key">{config?.amplitudeApiKey || 'not-set'}</div>
      <div data-testid="atom-identifying-prefix">{config?.identifyingPrefix || 'not-set'}</div>
    </div>
  );
};

describe('EnviveConfigProvider', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('Provider Integration with Jotai Store', () => {
    it('should set the enviveConfigAtom when provider is mounted', async () => {
      const testConfig = {
        amplitudeApiKey: 'test-amplitude-key',
        baseUrl: 'https://test-api.example.com',
        orgShortName: 'test-org',
        identifyingPrefix: 'test-prefix',
        env: 'test',
        cdnUrl: 'https://test-cdn.example.com',
        dataResidency: 'us',
        reactAppName: 'test-app',
        contextSource: 'test-source',
        orgLevelApiKey: 'test-org-key',
        publicKey: 'test-public-key',
        show: true,
      };

      render(
        <Provider>
          <EnviveConfigProvider
            amplitudeApiKey={testConfig.amplitudeApiKey}
            baseUrl={testConfig.baseUrl}
            orgShortName={testConfig.orgShortName}
            identifyingPrefix={testConfig.identifyingPrefix}
            env={testConfig.env}
            cdnUrl={testConfig.cdnUrl}
            dataResidency={testConfig.dataResidency}
            reactAppName={testConfig.reactAppName}
            contextSource={testConfig.contextSource}
            orgLevelApiKey={testConfig.orgLevelApiKey}
            publicKey={testConfig.publicKey}
            show={testConfig.show}
          >
            <AtomReaderComponent />
          </EnviveConfigProvider>
        </Provider>,
      );

      // Wait for the atom to be set (useEffect runs after render)
      await waitFor(() => {
        expect(screen.getByTestId('atom-amplitude-api-key')).toHaveTextContent(
          testConfig.amplitudeApiKey,
        );
      });

      expect(screen.getByTestId('atom-identifying-prefix')).toHaveTextContent(
        testConfig.identifyingPrefix,
      );
    });

    it('should update the atom when provider props change', async () => {
      const initialConfig = {
        amplitudeApiKey: 'initial-key',
        identifyingPrefix: 'initial-prefix',
      };

      const { rerender } = render(
        <Provider>
          <EnviveConfigProvider
            amplitudeApiKey={initialConfig.amplitudeApiKey}
            identifyingPrefix={initialConfig.identifyingPrefix}
          >
            <AtomReaderComponent />
          </EnviveConfigProvider>
        </Provider>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('atom-amplitude-api-key')).toHaveTextContent(
          initialConfig.amplitudeApiKey,
        );
      });

      const updatedConfig = {
        amplitudeApiKey: 'updated-key',
        identifyingPrefix: 'updated-prefix',
      };

      rerender(
        <Provider>
          <EnviveConfigProvider
            amplitudeApiKey={updatedConfig.amplitudeApiKey}
            identifyingPrefix={updatedConfig.identifyingPrefix}
          >
            <AtomReaderComponent />
          </EnviveConfigProvider>
        </Provider>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('atom-amplitude-api-key')).toHaveTextContent(
          updatedConfig.amplitudeApiKey,
        );
      });

      expect(screen.getByTestId('atom-identifying-prefix')).toHaveTextContent(
        updatedConfig.identifyingPrefix,
      );
    });
  });

  describe('useEnviveConfig Hook Integration', () => {
    it('should allow components to access config via useEnviveConfig hook', async () => {
      const testConfig = {
        amplitudeApiKey: 'hook-test-key',
        baseUrl: 'https://hook-test-api.example.com',
        orgShortName: 'hook-test-org',
        identifyingPrefix: 'hook-prefix',
        env: 'production',
        cdnUrl: 'https://hook-test-cdn.example.com',
        dataResidency: 'eu',
        reactAppName: 'hook-test-app',
        contextSource: 'hook-source',
        orgLevelApiKey: 'hook-org-key',
        publicKey: 'hook-public-key',
        show: false,
      };

      render(
        <Provider>
          <EnviveConfigProvider
            amplitudeApiKey={testConfig.amplitudeApiKey}
            baseUrl={testConfig.baseUrl}
            orgShortName={testConfig.orgShortName}
            identifyingPrefix={testConfig.identifyingPrefix}
            env={testConfig.env}
            cdnUrl={testConfig.cdnUrl}
            dataResidency={testConfig.dataResidency}
            reactAppName={testConfig.reactAppName}
            contextSource={testConfig.contextSource}
            orgLevelApiKey={testConfig.orgLevelApiKey}
            publicKey={testConfig.publicKey}
            show={testConfig.show}
          >
            <MockComponent />
          </EnviveConfigProvider>
        </Provider>,
      );

      // Wait for the config to be available
      await waitFor(() => {
        expect(screen.getByTestId('amplitude-api-key')).toHaveTextContent(
          testConfig.amplitudeApiKey,
        );
      });

      // Verify all config values are accessible via the hook
      expect(screen.getByTestId('amplitude-api-key')).toHaveTextContent(testConfig.amplitudeApiKey);
      expect(screen.getByTestId('base-url')).toHaveTextContent(testConfig.baseUrl);
      expect(screen.getByTestId('org-short-name')).toHaveTextContent(testConfig.orgShortName);
      expect(screen.getByTestId('identifying-prefix')).toHaveTextContent(
        testConfig.identifyingPrefix,
      );
      expect(screen.getByTestId('env')).toHaveTextContent(testConfig.env);
      expect(screen.getByTestId('cdn-url')).toHaveTextContent(testConfig.cdnUrl);
      expect(screen.getByTestId('data-residency')).toHaveTextContent(testConfig.dataResidency);
      expect(screen.getByTestId('react-app-name')).toHaveTextContent(testConfig.reactAppName);
      expect(screen.getByTestId('context-source')).toHaveTextContent(testConfig.contextSource);
      expect(screen.getByTestId('org-level-api-key')).toHaveTextContent(testConfig.orgLevelApiKey);
      expect(screen.getByTestId('public-key')).toHaveTextContent(testConfig.publicKey);
      expect(screen.getByTestId('show')).toHaveTextContent(testConfig.show.toString());
    });

    it('should return empty object when config is not set', () => {
      // Component that uses the hook without provider
      const UnwrappedComponent: React.FC = () => {
        const config = useEnviveConfig();
        return (
          <div data-testid="unwrapped">
            {config && Object.keys(config).length === 0 ? 'empty' : 'not-empty'}
          </div>
        );
      };

      render(
        <Provider>
          <UnwrappedComponent />
        </Provider>,
      );

      // The hook should return an empty object when config is not set
      expect(screen.getByTestId('unwrapped')).toHaveTextContent('empty');
    });

    it('should handle optional props correctly', async () => {
      const minimalConfig = {
        identifyingPrefix: 'minimal-prefix',
      };

      render(
        <Provider>
          <EnviveConfigProvider identifyingPrefix={minimalConfig.identifyingPrefix}>
            <MockComponent />
          </EnviveConfigProvider>
        </Provider>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('identifying-prefix')).toHaveTextContent(
          minimalConfig.identifyingPrefix,
        );
      });

      // Optional props should show "not-set" when not provided
      expect(screen.getByTestId('amplitude-api-key')).toHaveTextContent('not-set');
      expect(screen.getByTestId('base-url')).toHaveTextContent('not-set');
      expect(screen.getByTestId('org-short-name')).toHaveTextContent('not-set');
    });

    it('should handle featureOverrides and featureGates', async () => {
      const featureOverrides = {
        feature1: true,
        feature2: false,
      };

      const featureGates = [
        {
          featureName: 'testFeature',
          enabled: true,
        },
      ] as any[];

      const FeatureConfigComponent: React.FC = () => {
        const config = useEnviveConfig();
        return (
          <div data-testid="feature-config">
            <div data-testid="feature-overrides">
              {JSON.stringify(config?.featureOverrides || {})}
            </div>
            <div data-testid="feature-gates">{JSON.stringify(config?.featureGates || [])}</div>
          </div>
        );
      };

      render(
        <Provider>
          <EnviveConfigProvider
            identifyingPrefix="test"
            featureOverrides={featureOverrides}
            featureGates={featureGates}
          >
            <FeatureConfigComponent />
          </EnviveConfigProvider>
        </Provider>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('feature-overrides')).toHaveTextContent(
          JSON.stringify(featureOverrides),
        );
      });

      expect(screen.getByTestId('feature-gates')).toHaveTextContent(JSON.stringify(featureGates));
    });
  });

  describe('Provider Context Integration', () => {
    it('should provide config through React Context', async () => {
      const testConfig = {
        identifyingPrefix: 'context-prefix',
        orgShortName: 'context-org',
      };

      render(
        <Provider>
          <EnviveConfigProvider
            identifyingPrefix={testConfig.identifyingPrefix}
            orgShortName={testConfig.orgShortName}
          >
            <MockComponent />
          </EnviveConfigProvider>
        </Provider>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('identifying-prefix')).toHaveTextContent(
          testConfig.identifyingPrefix,
        );
      });

      expect(screen.getByTestId('org-short-name')).toHaveTextContent(testConfig.orgShortName);
    });

    it('should not render children when config is falsy', () => {
      const TestChild: React.FC = () => <div data-testid="child">Child</div>;

      // This shouldn't happen in practice, but testing the conditional render
      const { container } = render(
        <Provider>
          <EnviveConfigProvider
            identifyingPrefix=""
            show={false}
          >
            <TestChild />
          </EnviveConfigProvider>
        </Provider>,
      );

      // Children should still render even if some props are falsy
      // The condition `config ? children : null` checks if config object exists, not individual props
      expect(screen.getByTestId('child')).toBeInTheDocument();
    });
  });

  describe('Logger Integration', () => {
    it('should log debug message when setting config', async () => {
      const logSpy = vi.spyOn(Logger.prototype, 'logDebug');

      render(
        <Provider>
          <EnviveConfigProvider
            identifyingPrefix="test"
            amplitudeApiKey="test-key"
          >
            <div>Test</div>
          </EnviveConfigProvider>
        </Provider>,
      );

      await waitFor(() => {
        expect(logSpy).toHaveBeenCalledWith(
          'EnviveConfigProvider: Setting EnviveConfig',
          expect.objectContaining({
            identifyingPrefix: 'test',
            amplitudeApiKey: 'test-key',
          }),
        );
      });

      logSpy.mockRestore();
    });
  });
});
