import { fromJS } from 'immutable';
import {
  makeSelectInApp,
  makeSelectAccount,
  makeSelectBeePopupBuilderTokenFetching,
  makeSelectBeePopupBuilderToken,
  makeSelectGetTemplateDetailsInProgress,
} from '../selectors';

describe('InApp selectors', () => {
  // Mock state structures
  const mockInAppState = {
    beePopupBuilderTokenFetching: true,
    beePopupBuilderToken: { uuid: 'test-uuid-123', apiKey: 'test-api-key' },
    getTemplateDetailsInProgress: false,
    createTemplateInProgress: false,
    templateData: { id: 'template-1', name: 'Test Template' },
    createTemplateErrorMessage: null,
  };

  const mockTemplatesState = {
    selectedAccount: { id: 'account-1', name: 'Test Account' },
    accounts: [
      { id: 'account-1', name: 'Test Account' },
      { id: 'account-2', name: 'Another Account' },
    ],
    templateDetails: { id: 'template-details-1' },
  };

  const mockStateWithInApp = fromJS({
    inapp: mockInAppState,
    templates: mockTemplatesState,
    otherState: { value: 'test' },
  });

  const mockStateWithoutInApp = fromJS({
    templates: mockTemplatesState,
    otherState: { value: 'test' },
  });

  const mockStateWithEmptyInApp = fromJS({
    inapp: {},
    templates: mockTemplatesState,
    otherState: { value: 'test' },
  });

  const mockStateWithoutTemplates = fromJS({
    inapp: mockInAppState,
    otherState: { value: 'test' },
  });

  const mockStateWithPartialInApp = fromJS({
    inapp: {
      beePopupBuilderTokenFetching: false,
      beePopupBuilderToken: { uuid: 'partial-uuid' },
      templateData: { id: 'partial-template' },
      // Missing other properties to test defaults
    },
    templates: mockTemplatesState,
    otherState: { value: 'test' },
  });

  describe('makeSelectInApp', () => {
    it('should select the inapp state and convert to JS', () => {
      const selector = makeSelectInApp();
      const result = selector(mockStateWithInApp);

      expect(result).toEqual(mockInAppState);
      expect(typeof result).toBe('object');
      expect(Array.isArray(result)).toBe(false);
    });

    it('should return undefined when inapp state does not exist', () => {
      const selector = makeSelectInApp();
      const result = selector(mockStateWithoutInApp);

      expect(result).toBeUndefined();
    });

    it('should return empty object when inapp state is empty', () => {
      const selector = makeSelectInApp();
      const result = selector(mockStateWithEmptyInApp);

      expect(result).toEqual({});
    });

    it('should handle partial inapp state', () => {
      const selector = makeSelectInApp();
      const result = selector(mockStateWithPartialInApp);

      expect(result).toEqual({
        beePopupBuilderTokenFetching: false,
        beePopupBuilderToken: { uuid: 'partial-uuid' },
        templateData: { id: 'partial-template' },
      });
    });

    it('should handle null inapp state', () => {
      const stateWithNullInApp = fromJS({
        inapp: null,
        templates: mockTemplatesState,
      });

      const selector = makeSelectInApp();
      const result = selector(stateWithNullInApp);

      expect(result).toBeUndefined();
    });

    it('should handle complex nested data in inapp state', () => {
      const complexInAppState = fromJS({
        inapp: {
          templateData: {
            id: 'complex-template',
            versions: {
              base: {
                content: {
                  ANDROID: { title: 'Android Title', message: 'Android Message' },
                  IOS: { title: 'iOS Title', message: 'iOS Message' },
                },
              },
            },
            metadata: {
              createdAt: '2023-01-01',
              updatedAt: '2023-01-02',
              tags: ['tag1', 'tag2'],
            },
          },
          beePopupBuilderToken: { uuid: 'complex-uuid', permissions: ['read', 'write'] },
        },
        templates: mockTemplatesState,
      });

      const selector = makeSelectInApp();
      const result = selector(complexInAppState);

      expect(result.templateData.id).toBe('complex-template');
      expect(result.templateData.versions.base.content.ANDROID.title).toBe('Android Title');
      expect(result.templateData.versions.base.content.IOS.title).toBe('iOS Title');
      expect(result.templateData.metadata.tags).toEqual(['tag1', 'tag2']);
      expect(result.beePopupBuilderToken.permissions).toEqual(['read', 'write']);
    });
  });

  describe('makeSelectAccount', () => {
    it('should select the templates state and convert to JS', () => {
      const selector = makeSelectAccount();
      const result = selector(mockStateWithInApp);

      expect(result).toEqual(mockTemplatesState);
      expect(typeof result).toBe('object');
      expect(Array.isArray(result)).toBe(false);
    });

    it('should throw error when templates state does not exist', () => {
      const selector = makeSelectAccount();

      expect(() => {
        selector(mockStateWithoutTemplates);
      }).toThrow();
    });

    it('should handle empty templates state', () => {
      const stateWithEmptyTemplates = fromJS({
        inapp: mockInAppState,
        templates: {},
        otherState: { value: 'test' },
      });

      const selector = makeSelectAccount();
      const result = selector(stateWithEmptyTemplates);

      expect(result).toEqual({});
    });

    it('should throw error when templates state is null', () => {
      const stateWithNullTemplates = fromJS({
        inapp: mockInAppState,
        templates: null,
      });

      const selector = makeSelectAccount();

      expect(() => {
        selector(stateWithNullTemplates);
      }).toThrow();
    });

    it('should handle partial templates state', () => {
      const stateWithPartialTemplates = fromJS({
        inapp: mockInAppState,
        templates: {
          selectedAccount: { id: 'partial-account' },
        },
      });

      const selector = makeSelectAccount();
      const result = selector(stateWithPartialTemplates);

      expect(result).toEqual({
        selectedAccount: { id: 'partial-account' },
      });
    });

    it('should handle complex templates state with nested data', () => {
      const complexTemplatesState = fromJS({
        inapp: mockInAppState,
        templates: {
          selectedAccount: {
            id: 'complex-account',
            configs: {
              android: { version: '1.0', enabled: true },
              ios: { version: '2.0', enabled: false },
            },
            metadata: {
              region: 'US',
              tier: 'premium',
            },
          },
          accounts: [
            { id: 'account-1', type: 'production' },
            { id: 'account-2', type: 'staging' },
          ],
        },
      });

      const selector = makeSelectAccount();
      const result = selector(complexTemplatesState);

      expect(result.selectedAccount.id).toBe('complex-account');
      expect(result.selectedAccount.configs.android.version).toBe('1.0');
      expect(result.selectedAccount.configs.ios.enabled).toBe(false);
      expect(result.selectedAccount.metadata.region).toBe('US');
      expect(result.accounts).toHaveLength(2);
      expect(result.accounts[0].type).toBe('production');
    });
  });

  describe('makeSelectBeePopupBuilderTokenFetching', () => {
    it('should select beePopupBuilderTokenFetching when it is true', () => {
      const selector = makeSelectBeePopupBuilderTokenFetching();
      const result = selector(mockStateWithInApp);

      expect(result).toBe(true);
    });

    it('should select beePopupBuilderTokenFetching when it is false', () => {
      const selector = makeSelectBeePopupBuilderTokenFetching();
      const result = selector(mockStateWithPartialInApp);

      expect(result).toBe(false);
    });

    it('should throw error when beePopupBuilderTokenFetching does not exist', () => {
      const selector = makeSelectBeePopupBuilderTokenFetching();

      expect(() => {
        selector(mockStateWithoutInApp);
      }).toThrow();
    });

    it('should return undefined when inapp state is empty', () => {
      const selector = makeSelectBeePopupBuilderTokenFetching();
      const result = selector(mockStateWithEmptyInApp);

      expect(result).toBeUndefined();
    });

    it('should handle null beePopupBuilderTokenFetching', () => {
      const stateWithNullFetching = fromJS({
        inapp: {
          beePopupBuilderTokenFetching: null,
          beePopupBuilderToken: {},
        },
      });

      const selector = makeSelectBeePopupBuilderTokenFetching();
      const result = selector(stateWithNullFetching);

      expect(result).toBeNull();
    });

    it('should handle undefined beePopupBuilderTokenFetching in partial state', () => {
      const stateWithoutFetching = fromJS({
        inapp: {
          beePopupBuilderToken: { uuid: 'test' },
          templateData: {},
        },
      });

      const selector = makeSelectBeePopupBuilderTokenFetching();
      const result = selector(stateWithoutFetching);

      expect(result).toBeUndefined();
    });
  });

  describe('makeSelectBeePopupBuilderToken', () => {
    it('should select beePopupBuilderToken when it exists', () => {
      const selector = makeSelectBeePopupBuilderToken();
      const result = selector(mockStateWithInApp);

      expect(result.get('uuid')).toBe('test-uuid-123');
      expect(result.get('apiKey')).toBe('test-api-key');
    });

    it('should select beePopupBuilderToken from partial state', () => {
      const selector = makeSelectBeePopupBuilderToken();
      const result = selector(mockStateWithPartialInApp);

      expect(result.get('uuid')).toBe('partial-uuid');
    });

    it('should throw error when beePopupBuilderToken does not exist', () => {
      const selector = makeSelectBeePopupBuilderToken();

      expect(() => {
        selector(mockStateWithoutInApp);
      }).toThrow();
    });

    it('should return undefined when inapp state is empty', () => {
      const selector = makeSelectBeePopupBuilderToken();
      const result = selector(mockStateWithEmptyInApp);

      expect(result).toBeUndefined();
    });

    it('should handle null beePopupBuilderToken', () => {
      const stateWithNullToken = fromJS({
        inapp: {
          beePopupBuilderToken: null,
          beePopupBuilderTokenFetching: false,
        },
      });

      const selector = makeSelectBeePopupBuilderToken();
      const result = selector(stateWithNullToken);

      expect(result).toBeNull();
    });

    it('should handle complex beePopupBuilderToken with nested data', () => {
      const stateWithComplexToken = fromJS({
        inapp: {
          beePopupBuilderToken: {
            uuid: 'complex-uuid',
            apiKey: 'complex-api-key',
            permissions: ['read', 'write', 'admin'],
            expiresAt: '2024-12-31T23:59:59Z',
            metadata: {
              userId: 'user-123',
              organizationId: 'org-456',
              scopes: ['templates', 'assets'],
            },
          },
        },
      });

      const selector = makeSelectBeePopupBuilderToken();
      const result = selector(stateWithComplexToken);

      expect(result.get('uuid')).toBe('complex-uuid');
      expect(result.get('apiKey')).toBe('complex-api-key');
      expect(result.get('permissions').toJS()).toEqual(['read', 'write', 'admin']);
      expect(result.get('expiresAt')).toBe('2024-12-31T23:59:59Z');
      expect(result.getIn(['metadata', 'userId'])).toBe('user-123');
      expect(result.getIn(['metadata', 'organizationId'])).toBe('org-456');
      expect(result.getIn(['metadata', 'scopes']).toJS()).toEqual(['templates', 'assets']);
    });

    it('should handle empty beePopupBuilderToken object', () => {
      const stateWithEmptyToken = fromJS({
        inapp: {
          beePopupBuilderToken: {},
        },
      });

      const selector = makeSelectBeePopupBuilderToken();
      const result = selector(stateWithEmptyToken);

      expect(result).toEqual(fromJS({}));
    });
  });

  describe('makeSelectGetTemplateDetailsInProgress', () => {
    it('should select getTemplateDetailsInProgress when it is false', () => {
      const selector = makeSelectGetTemplateDetailsInProgress();
      const result = selector(mockStateWithInApp);

      expect(result).toBe(false);
    });

    it('should select getTemplateDetailsInProgress when it is true', () => {
      const stateWithProgressTrue = fromJS({
        inapp: {
          ...mockInAppState,
          getTemplateDetailsInProgress: true,
        },
      });

      const selector = makeSelectGetTemplateDetailsInProgress();
      const result = selector(stateWithProgressTrue);

      expect(result).toBe(true);
    });

    it('should throw error when getTemplateDetailsInProgress does not exist', () => {
      const selector = makeSelectGetTemplateDetailsInProgress();

      expect(() => {
        selector(mockStateWithoutInApp);
      }).toThrow();
    });

    it('should return undefined when inapp state is empty', () => {
      const selector = makeSelectGetTemplateDetailsInProgress();
      const result = selector(mockStateWithEmptyInApp);

      expect(result).toBeUndefined();
    });

    it('should return undefined when getTemplateDetailsInProgress is not in partial state', () => {
      const selector = makeSelectGetTemplateDetailsInProgress();
      const result = selector(mockStateWithPartialInApp);

      expect(result).toBeUndefined();
    });

    it('should handle null getTemplateDetailsInProgress', () => {
      const stateWithNullProgress = fromJS({
        inapp: {
          getTemplateDetailsInProgress: null,
          beePopupBuilderToken: {},
        },
      });

      const selector = makeSelectGetTemplateDetailsInProgress();
      const result = selector(stateWithNullProgress);

      expect(result).toBeNull();
    });
  });

  describe('Selector memoization', () => {
    it('should return the same result for identical state (memoization test)', () => {
      const selector = makeSelectInApp();
      const result1 = selector(mockStateWithInApp);
      const result2 = selector(mockStateWithInApp);

      expect(result1).toBe(result2);
    });

    it('should return different results for different states', () => {
      const selector = makeSelectBeePopupBuilderTokenFetching();
      const result1 = selector(mockStateWithInApp);
      const result2 = selector(mockStateWithPartialInApp);

      expect(result1).toBe(true);
      expect(result2).toBe(false);
      expect(result1).not.toBe(result2);
    });

    it('should maintain memoization across different selectors', () => {
      const tokenSelector = makeSelectBeePopupBuilderToken();
      const fetchingSelector = makeSelectBeePopupBuilderTokenFetching();

      const tokenResult1 = tokenSelector(mockStateWithInApp);
      const tokenResult2 = tokenSelector(mockStateWithInApp);
      const fetchingResult1 = fetchingSelector(mockStateWithInApp);
      const fetchingResult2 = fetchingSelector(mockStateWithInApp);

      expect(tokenResult1).toBe(tokenResult2);
      expect(fetchingResult1).toBe(fetchingResult2);
    });
  });

  describe('Edge cases', () => {
    it('should handle undefined state gracefully', () => {
      const selector = makeSelectInApp();
      const undefinedState = fromJS({});
      const result = selector(undefinedState);

      expect(result).toBeUndefined();
    });

    it('should throw error when inapp state is null', () => {
      const stateWithNullInApp = fromJS({
        inapp: null,
      });

      const selector = makeSelectBeePopupBuilderToken();

      expect(() => {
        selector(stateWithNullInApp);
      }).toThrow();
    });

    it('should throw error when accessing deeply nested state', () => {
      const complexState = fromJS({
        level1: {
          level2: {
            inapp: mockInAppState,
          },
        },
      });

      // This should throw error since inapp is not at root level
      const selector = makeSelectBeePopupBuilderTokenFetching();

      expect(() => {
        selector(complexState);
      }).toThrow();
    });

    it('should handle state with missing top-level properties', () => {
      const incompleteState = fromJS({
        someOtherProperty: 'value',
      });

      const inAppSelector = makeSelectInApp();
      const accountSelector = makeSelectAccount();

      const inAppResult = inAppSelector(incompleteState);

      expect(inAppResult).toBeUndefined();

      expect(() => {
        accountSelector(incompleteState);
      }).toThrow();
    });
  });

  describe('Multiple selector usage', () => {
    it('should work correctly when multiple selectors are used on same state', () => {
      const inAppSelector = makeSelectInApp();
      const accountSelector = makeSelectAccount();
      const tokenSelector = makeSelectBeePopupBuilderToken();
      const fetchingSelector = makeSelectBeePopupBuilderTokenFetching();
      const progressSelector = makeSelectGetTemplateDetailsInProgress();

      const inAppResult = inAppSelector(mockStateWithInApp);
      const accountResult = accountSelector(mockStateWithInApp);
      const tokenResult = tokenSelector(mockStateWithInApp);
      const fetchingResult = fetchingSelector(mockStateWithInApp);
      const progressResult = progressSelector(mockStateWithInApp);

      expect(inAppResult).toEqual(mockInAppState);
      expect(accountResult).toEqual(mockTemplatesState);
      expect(tokenResult.get('uuid')).toBe('test-uuid-123');
      expect(fetchingResult).toBe(true);
      expect(progressResult).toBe(false);
    });

    it('should maintain independent results across different selectors', () => {
      const tokenSelector = makeSelectBeePopupBuilderToken();
      const fetchingSelector = makeSelectBeePopupBuilderTokenFetching();
      const progressSelector = makeSelectGetTemplateDetailsInProgress();

      const tokenResult = tokenSelector(mockStateWithPartialInApp);
      const fetchingResult = fetchingSelector(mockStateWithPartialInApp);
      const progressResult = progressSelector(mockStateWithPartialInApp);

      expect(tokenResult.get('uuid')).toBe('partial-uuid');
      expect(fetchingResult).toBe(false);
      expect(progressResult).toBeUndefined();
    });

    it('should handle all selectors together on empty states', () => {
      const inAppSelector = makeSelectInApp();
      const accountSelector = makeSelectAccount();
      const tokenSelector = makeSelectBeePopupBuilderToken();
      const fetchingSelector = makeSelectBeePopupBuilderTokenFetching();
      const progressSelector = makeSelectGetTemplateDetailsInProgress();

      const inAppResult = inAppSelector(mockStateWithEmptyInApp);
      const accountResult = accountSelector(mockStateWithEmptyInApp);
      const tokenResult = tokenSelector(mockStateWithEmptyInApp);
      const fetchingResult = fetchingSelector(mockStateWithEmptyInApp);
      const progressResult = progressSelector(mockStateWithEmptyInApp);

      expect(inAppResult).toEqual({});
      expect(accountResult).toEqual(mockTemplatesState);
      expect(tokenResult).toBeUndefined();
      expect(fetchingResult).toBeUndefined();
      expect(progressResult).toBeUndefined();
    });
  });

  describe('Selector function creation', () => {
    it('should create new selector instances each time factory is called', () => {
      const selector1 = makeSelectInApp();
      const selector2 = makeSelectInApp();

      expect(selector1).not.toBe(selector2);
    });

    it('should maintain separate memoization for different selector instances', () => {
      const selector1 = makeSelectInApp();
      const selector2 = makeSelectInApp();

      const result1a = selector1(mockStateWithInApp);
      const result1b = selector1(mockStateWithInApp);
      const result2a = selector2(mockStateWithInApp);

      // Same selector instance should return identical objects (memoized)
      expect(result1a).toBe(result1b);
      // Different selector instances should return equal but not identical objects
      expect(result1a).toEqual(result2a);
    });
  });
});
