import React from 'react';
import { act, render, screen, waitFor } from '@testing-library/react';
import Logger from 'src/application/logging/logger';
import { useNewOrgConfig } from 'src/hooks/NewOrgConfig';
import { SparkleIconColor } from '@envive-ai/react-toolkit-v3/WelcomeMessage';
import { ChatHeaderVariant } from '@envive-ai/react-toolkit-v3/ChatHeader';
import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
import { FloatingButtonLocation } from '@envive-ai/react-toolkit-v3/FloatingButton';
import { useUiConfig } from '../uiConfigContext';
import { ConfigVersionEnum, OrgUIConfigV3 } from '../../typesV3';

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

// Mock the useNewOrgConfig hook
vi.mock('src/hooks/NewOrgConfig', () => ({
  useNewOrgConfig: vi.fn(),
}));

// Component that uses the useUiConfig hook
const MockUiConfigComponent: React.FC = () => {
  const { getUiConfig, isLoading } = useUiConfig();
  const [uiConfig, setUiConfig] = React.useState<OrgUIConfigV3 | null>(null);
  const [error, setError] = React.useState<string | null>(null);
  const [isLoadingState, setIsLoadingState] = React.useState(false);

  const handleGetConfig = async () => {
    setIsLoadingState(true);
    try {
      const config = await getUiConfig();
      setUiConfig(config ?? null);
      setError(null);
    } catch (err: unknown) {
      setError(err instanceof Error ? err.message : 'Unknown error');
      setUiConfig(null);
    } finally {
      setIsLoadingState(false);
    }
  };

  return (
    <div data-testid="ui-config-component">
      <div data-testid="is-loading">{isLoading.toString()}</div>
      <div data-testid="is-loading-state">{isLoadingState.toString()}</div>
      <div data-testid="has-ui-config">{uiConfig ? 'true' : 'false'}</div>
      <div data-testid="ui-config-short-name">{uiConfig?.shortName || 'not-set'}</div>
      {error && <div data-testid="error-message">{error}</div>}
      <button
        data-testid="get-config-btn"
        type="button"
        onClick={handleGetConfig}
      >
        Get Config
      </button>
    </div>
  );
};

// Wrapper component for testing
const TestWrapper: React.FC<{
  children: React.ReactNode;
  mockNewOrgConfig?: {
    frontendConfig?: {
      uiConfigs?: OrgUIConfigV3;
    } | null;
    loading?: boolean;
  };
}> = ({ children, mockNewOrgConfig = {} }) => {
  const mockUseNewOrgConfig = useNewOrgConfig as ReturnType<typeof vi.fn>;
  mockUseNewOrgConfig.mockReturnValue({
    frontendConfig: mockNewOrgConfig.frontendConfig ?? null,
    loading: mockNewOrgConfig.loading ?? false,
  });

  return children;
};

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

  // Helper function to create a mock UI config
  const createMockUiConfig = (): OrgUIConfigV3 => ({
    version: ConfigVersionEnum.V3,
    shortName: 'test-merchant',
    lookAndFeel: {
      theme: Theme.STANDARD,
      agentName: 'Test Agent',
      chatHeaderLogoDarkSrc: '',
      chatHeaderLogoLightSrc: '',
      widgetLogoSrc: '',
      hideWidgetLogo: false,
      elementRadius: 8,
      imageAspectRatio: '1/1',
      buttonColors: {
        lightButtonBackgroundColor: '#ffffff',
        lightButtonIconColor: '#000000',
        darkButtonBackgroundColor: '#000000',
        darkButtonIconColor: '#ffffff',
      },
      typography: {
        titleFontFamily: 'Arial',
        headerFontFamily: 'Arial',
        bodyFontFamily: 'Arial',
      },
    },
    floatingButton: {
      position: FloatingButtonLocation.BOTTOM_RIGHT,
      backgroundColor: '#000000',
      mode: 'light',
      showOption: 'always',
      style: 'attached',
    },
    floatingChat: {
      headerBackgroundColor: '#ffffff',
      headerMode: ChatHeaderVariant.LIGHT,
      welcomeMessageIconColor: SparkleIconColor.ACCENT_PRIMARY,
      showVerifiedBuyer: true,
      userQueryInputEnabled: true,
      showEnviveLogo: false,
      welcomeMessageTitle: 'Welcome to Envive AI',
      welcomeMessageText:
        'Your personal shopper trained on the collective knowledge of thousands of real experts!',
      chatFooterTextFieldPlaceholderText: 'Ask me anything...',
    },
  });

  describe('useUiConfig Hook Integration', () => {
    it('should provide ui config data via useUiConfig hook', async () => {
      const mockUiConfig = createMockUiConfig();

      render(
        <TestWrapper
          mockNewOrgConfig={{
            frontendConfig: {
              uiConfigs: mockUiConfig,
            },
            loading: false,
          }}
        >
          <MockUiConfigComponent />
        </TestWrapper>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('ui-config-component')).toBeInTheDocument();
      });

      expect(screen.getByTestId('is-loading')).toHaveTextContent('false');
    });
  });

  describe('getUiConfig', () => {
    it('should successfully retrieve ui config when config exists', async () => {
      const mockUiConfig = createMockUiConfig();

      render(
        <TestWrapper
          mockNewOrgConfig={{
            frontendConfig: {
              uiConfigs: mockUiConfig,
            },
            loading: false,
          }}
        >
          <MockUiConfigComponent />
        </TestWrapper>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('ui-config-component')).toBeInTheDocument();
      });

      const getConfigButton = screen.getByTestId('get-config-btn');
      await act(async () => {
        getConfigButton.click();
      });

      await waitFor(() => {
        expect(screen.getByTestId('has-ui-config')).toHaveTextContent('true');
        expect(screen.getByTestId('ui-config-short-name')).toHaveTextContent('test-merchant');
      });

      expect(screen.queryByTestId('error-message')).not.toBeInTheDocument();
    });

    it('should throw error when frontend config is loading', async () => {
      const mockUiConfig = createMockUiConfig();
      render(
        <TestWrapper
          mockNewOrgConfig={{
            frontendConfig: {
              uiConfigs: mockUiConfig,
            },
            loading: true,
          }}
        >
          <MockUiConfigComponent />
        </TestWrapper>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('ui-config-component')).toBeInTheDocument();
      });

      const getConfigButton = screen.getByTestId('get-config-btn');
      await act(async () => {
        getConfigButton.click();
      });

      await waitFor(() => {
        expect(screen.getByTestId('error-message')).toHaveTextContent('Frontend config is loading');
      });
    });

    it('should return undefined when frontend config is null', async () => {
      render(
        <TestWrapper
          mockNewOrgConfig={{
            frontendConfig: null,
            loading: false,
          }}
        >
          <MockUiConfigComponent />
        </TestWrapper>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('ui-config-component')).toBeInTheDocument();
      });

      const getConfigButton = screen.getByTestId('get-config-btn');
      await act(async () => {
        getConfigButton.click();
      });

      await waitFor(() => {
        expect(screen.getByTestId('has-ui-config')).toHaveTextContent('false');
      });

      expect(screen.queryByTestId('error-message')).not.toBeInTheDocument();
    });
  });

  describe('isLoading state', () => {
    it('should show loading state when frontend config is loading', async () => {
      const mockUiConfig = createMockUiConfig();
      render(
        <TestWrapper
          mockNewOrgConfig={{
            frontendConfig: {
              uiConfigs: mockUiConfig,
            },
            loading: true,
          }}
        >
          <MockUiConfigComponent />
        </TestWrapper>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('is-loading')).toHaveTextContent('true');
      });
    });

    it('should show loading state when getUiConfig is called', async () => {
      const mockUiConfig = createMockUiConfig();

      render(
        <TestWrapper
          mockNewOrgConfig={{
            frontendConfig: {
              uiConfigs: mockUiConfig,
            },
            loading: false,
          }}
        >
          <MockUiConfigComponent />
        </TestWrapper>,
      );

      await waitFor(() => {
        expect(screen.getByTestId('ui-config-component')).toBeInTheDocument();
      });

      expect(screen.getByTestId('is-loading')).toHaveTextContent('false');

      const getConfigButton = screen.getByTestId('get-config-btn');

      await act(async () => {
        getConfigButton.click();
      });

      await waitFor(() => {
        expect(screen.getByTestId('has-ui-config')).toHaveTextContent('true');
        expect(screen.getByTestId('is-loading-state')).toHaveTextContent('false');
      });

      expect(screen.getByTestId('is-loading')).toHaveTextContent('false');
    });
  });
});
