/**
 * Tests for CommonTestAndPreview Selectors
 *
 * Tests all selectors to ensure they correctly extract data from state
 */

import { fromJS } from 'immutable';
import {
  makeSelectCommonTestAndPreview,
  makeSelectCustomers,
  makeSelectExtractedTags,
  makeSelectIsSearchingCustomer,
  makeSelectIsExtractingTags,
  makeSelectSearchError,
  makeSelectHasSearched,
  makeSelectApiWarnings,
  makeSelectApiErrors,
  makeSelectPreviewData,
  makeSelectIsUpdatingPreview,
  makeSelectIsSendingTestEmail,
  makeSelectTags,
  makeSelectTestCustomers,
  makeSelectIsFetchingTestCustomers,
  makeSelectTestGroups,
  makeSelectIsFetchingTestGroups,
  makeSelectMessageMetaConfigId,
  makeSelectIsCreatingMessageMeta,
  makeSelectPrefilledValues,
  makeSelectTestMessageResponse,
  makeSelectIsSendingTestMessage,
  makeSelectUpdatePreviewError,
  makeSelectUpdatePreviewErrors,
  makeSelectFetchPrefilledValuesError,
  makeSelectFetchPrefilledValuesErrors,
  makeSelectSenderDetailsForChannel,
  makeSelectSenderDetailsByChannel,
  makeSelectWeCrmAccounts,
  makeSelectIsLoadingSenderDetails,
  makeSelectFetchSenderDetailsError,
  makeSelectFetchWeCrmAccountsError,
} from '../selectors';

describe('CommonTestAndPreview Selectors', () => {
  const mockState = fromJS({
    commonTestAndPreview: {
      searchQuery: 'test@example.com',
      customers: [
        { id: 1, name: 'John Doe', email: 'john@example.com' },
        { id: 2, name: 'Jane Doe', email: 'jane@example.com' },
      ],
      isSearchingCustomer: false,
      searchError: null,
      hasSearched: true,
      extractedTags: [
        { name: 'firstName', fullPath: 'user.firstName' },
        { name: 'lastName', fullPath: 'user.lastName' },
      ],
      requiredTags: [
        { name: 'firstName', fullPath: 'user.firstName' },
      ],
      optionalTags: [
        { name: 'lastName', fullPath: 'user.lastName' },
      ],
      isExtractingTags: false,
      extractTagsError: null,
      apiWarnings: ['Warning 1', 'Warning 2'],
      apiErrors: ['Error 1'],
      previewData: {
        resolvedBody: '<html>Test</html>',
        resolvedTitle: 'Test Subject',
      },
      isUpdatingPreview: false,
      updatePreviewError: null,
      updatePreviewErrors: [
        { code: 7078, message: 'User profile not found' },
      ],
      isSendingTestMessage: false,
      isSendingTestEmail: false,
      sendTestMessageError: null,
      testMessageResponse: { messageId: '123', status: 'sent' },
      tags: {
        loading: false,
        error: null,
        required: [{ name: 'firstName' }],
        optional: [{ name: 'lastName' }],
      },
      testCustomers: [
        { id: 1, name: 'Test Customer 1' },
        { id: 2, name: 'Test Customer 2' },
      ],
      isFetchingTestCustomers: false,
      fetchTestCustomersError: null,
      testGroups: [
        { id: 1, name: 'Test Group 1' },
      ],
      isFetchingTestGroups: false,
      fetchTestGroupsError: null,
      messageMetaConfigId: 'config-123',
      isCreatingMessageMeta: false,
      createMessageMetaError: null,
      prefilledValues: {
        'user.name': 'John',
        'user.email': 'john@example.com',
      },
      isFetchingPrefilledValues: false,
      fetchPrefilledValuesError: null,
      fetchPrefilledValuesErrors: [
        { code: 7078, message: 'User profile not found' },
      ],
      senderDetailsByChannel: {
        SMS: [
          { domainId: 1, domainName: 'SMS Domain', gsmSenders: [{ value: 'sender1' }] },
        ],
        EMAIL: [],
        WHATSAPP: [],
      },
      wecrmAccounts: [
        { name: 'WeCRM Account 1', sourceAccountIdentifier: 'waba-1' },
      ],
      isLoadingSenderDetails: false,
      fetchSenderDetailsError: null,
      fetchWeCrmAccountsError: null,
    },
  });

  describe('makeSelectCommonTestAndPreview', () => {
    it('should select the commonTestAndPreview domain', () => {
      const selector = makeSelectCommonTestAndPreview();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.get('customers')).toBeDefined();
      expect(result.get('extractedTags')).toBeDefined();
    });

    it('should return undefined if domain does not exist', () => {
      const emptyState = fromJS({});
      const selector = makeSelectCommonTestAndPreview();
      const result = selector(emptyState);

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

  describe('makeSelectCustomers', () => {
    it('should select customers from state', () => {
      const selector = makeSelectCustomers();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.size).toBe(2);
      expect(result.get(0).get('name')).toBe('John Doe');
    });

    it('should return empty list if customers do not exist', () => {
      const emptyState = fromJS({
        commonTestAndPreview: {
          customers: [],
        },
      });
      const selector = makeSelectCustomers();
      const result = selector(emptyState);

      expect(result).toEqual(fromJS([]));
    });
  });

  describe('makeSelectExtractedTags', () => {
    it('should select extractedTags from state', () => {
      const selector = makeSelectExtractedTags();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.size).toBe(2);
      expect(result.get(0).get('name')).toBe('firstName');
    });

    it('should return empty list if tags do not exist', () => {
      const emptyState = fromJS({
        commonTestAndPreview: {
          extractedTags: [],
        },
      });
      const selector = makeSelectExtractedTags();
      const result = selector(emptyState);

      expect(result).toEqual(fromJS([]));
    });
  });

  describe('makeSelectIsSearchingCustomer', () => {
    it('should select isSearchingCustomer flag', () => {
      const selector = makeSelectIsSearchingCustomer();
      const result = selector(mockState);

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

    it('should return true when searching', () => {
      const searchingState = fromJS({
        commonTestAndPreview: {
          isSearchingCustomer: true,
        },
      });
      const selector = makeSelectIsSearchingCustomer();
      const result = selector(searchingState);

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

  describe('makeSelectIsExtractingTags', () => {
    it('should select isExtractingTags flag', () => {
      const selector = makeSelectIsExtractingTags();
      const result = selector(mockState);

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

  describe('makeSelectSearchError', () => {
    it('should select searchError from state', () => {
      const selector = makeSelectSearchError();
      const result = selector(mockState);

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

    it('should return error when present', () => {
      const errorState = fromJS({
        commonTestAndPreview: {
          searchError: 'Search failed',
        },
      });
      const selector = makeSelectSearchError();
      const result = selector(errorState);

      expect(result).toBe('Search failed');
    });
  });

  describe('makeSelectHasSearched', () => {
    it('should select hasSearched flag', () => {
      const selector = makeSelectHasSearched();
      const result = selector(mockState);

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

  describe('makeSelectApiWarnings', () => {
    it('should select apiWarnings from state', () => {
      const selector = makeSelectApiWarnings();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.size).toBe(2);
      expect(result.get(0)).toBe('Warning 1');
    });
  });

  describe('makeSelectApiErrors', () => {
    it('should select apiErrors from state', () => {
      const selector = makeSelectApiErrors();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.size).toBe(1);
      expect(result.get(0)).toBe('Error 1');
    });
  });

  describe('makeSelectPreviewData', () => {
    it('should select previewData from state', () => {
      const selector = makeSelectPreviewData();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.get('resolvedBody')).toBe('<html>Test</html>');
      expect(result.get('resolvedTitle')).toBe('Test Subject');
    });

    it('should return null if previewData does not exist', () => {
      const emptyState = fromJS({
        commonTestAndPreview: {
          previewData: null,
        },
      });
      const selector = makeSelectPreviewData();
      const result = selector(emptyState);

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

  describe('makeSelectIsUpdatingPreview', () => {
    it('should select isUpdatingPreview flag', () => {
      const selector = makeSelectIsUpdatingPreview();
      const result = selector(mockState);

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

  describe('makeSelectIsSendingTestEmail', () => {
    it('should select isSendingTestEmail flag', () => {
      const selector = makeSelectIsSendingTestEmail();
      const result = selector(mockState);

      // Note: This selector might be checking a different property
      expect(result).toBeDefined();
    });
  });

  describe('makeSelectTags', () => {
    it('should select tags object from state', () => {
      const selector = makeSelectTags();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.get('loading')).toBe(false);
      expect(result.get('error')).toBeNull();
      expect(result.get('required')).toBeDefined();
      expect(result.get('optional')).toBeDefined();
    });
  });

  describe('makeSelectTestCustomers', () => {
    it('should select testCustomers from state', () => {
      const selector = makeSelectTestCustomers();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.size).toBe(2);
      expect(result.get(0).get('name')).toBe('Test Customer 1');
    });
  });

  describe('makeSelectIsFetchingTestCustomers', () => {
    it('should select isFetchingTestCustomers flag', () => {
      const selector = makeSelectIsFetchingTestCustomers();
      const result = selector(mockState);

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

  describe('makeSelectTestGroups', () => {
    it('should select testGroups from state', () => {
      const selector = makeSelectTestGroups();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.size).toBe(1);
      expect(result.get(0).get('name')).toBe('Test Group 1');
    });
  });

  describe('makeSelectIsFetchingTestGroups', () => {
    it('should select isFetchingTestGroups flag', () => {
      const selector = makeSelectIsFetchingTestGroups();
      const result = selector(mockState);

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

  describe('makeSelectMessageMetaConfigId', () => {
    it('should select messageMetaConfigId from state', () => {
      const selector = makeSelectMessageMetaConfigId();
      const result = selector(mockState);

      expect(result).toBe('config-123');
    });

    it('should return null if messageMetaConfigId does not exist', () => {
      const emptyState = fromJS({
        commonTestAndPreview: {
          messageMetaConfigId: null,
        },
      });
      const selector = makeSelectMessageMetaConfigId();
      const result = selector(emptyState);

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

  describe('makeSelectIsCreatingMessageMeta', () => {
    it('should select isCreatingMessageMeta flag', () => {
      const selector = makeSelectIsCreatingMessageMeta();
      const result = selector(mockState);

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

  describe('makeSelectPrefilledValues', () => {
    it('should select prefilledValues and convert to JS', () => {
      const selector = makeSelectPrefilledValues();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(typeof result).toBe('object');
      expect(Array.isArray(result)).toBe(false); // Should be plain object, not array
      expect(result['user.name']).toBe('John');
      expect(result['user.email']).toBe('john@example.com');
    });

    it('should return empty object if prefilledValues does not exist', () => {
      const emptyState = fromJS({
        commonTestAndPreview: {
          prefilledValues: {},
        },
      });
      const selector = makeSelectPrefilledValues();
      const result = selector(emptyState);

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

    it('should handle null prefilledValues', () => {
      const nullState = fromJS({
        commonTestAndPreview: {
          prefilledValues: null,
        },
      });
      const selector = makeSelectPrefilledValues();
      const result = selector(nullState);

      // Should handle null gracefully - returns null when prefilledValues is null
      expect(result).toBeNull();
    });

    it('should return undefined when commonTestAndPreview substate is missing', () => {
      const selector = makeSelectPrefilledValues();
      const result = selector(fromJS({}));

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

    it('should return null when prefilledValues is undefined', () => {
      const stateWithUndefined = fromJS({
        commonTestAndPreview: {},
      });
      const selector = makeSelectPrefilledValues();
      const result = selector(stateWithUndefined);

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

  describe('makeSelectTestMessageResponse', () => {
    it('should select testMessageResponse from state', () => {
      const selector = makeSelectTestMessageResponse();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.get('messageId')).toBe('123');
      expect(result.get('status')).toBe('sent');
    });
  });

  describe('makeSelectIsSendingTestMessage', () => {
    it('should select isSendingTestMessage flag', () => {
      const selector = makeSelectIsSendingTestMessage();
      const result = selector(mockState);

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

  describe('makeSelectUpdatePreviewError', () => {
    it('should select updatePreviewError from state', () => {
      const selector = makeSelectUpdatePreviewError();
      const result = selector(mockState);

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

    it('should return error when present', () => {
      const errorState = fromJS({
        commonTestAndPreview: {
          updatePreviewError: 'Update failed',
        },
      });
      const selector = makeSelectUpdatePreviewError();
      const result = selector(errorState);

      expect(result).toBe('Update failed');
    });
  });

  describe('makeSelectUpdatePreviewErrors', () => {
    it('should select updatePreviewErrors and convert to JS array', () => {
      const selector = makeSelectUpdatePreviewErrors();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(Array.isArray(result)).toBe(true);
      expect(result.length).toBe(1);
      expect(result[0].code).toBe(7078);
      expect(result[0].message).toBe('User profile not found');
    });

    it('should return empty array if errors do not exist', () => {
      const emptyState = fromJS({
        commonTestAndPreview: {
          updatePreviewErrors: [],
        },
      });
      const selector = makeSelectUpdatePreviewErrors();
      const result = selector(emptyState);

      expect(result).toEqual([]);
    });

    it('should handle null errors', () => {
      const nullState = fromJS({
        commonTestAndPreview: {
          updatePreviewErrors: null,
        },
      });
      const selector = makeSelectUpdatePreviewErrors();
      const result = selector(nullState);

      expect(result).toEqual([]);
    });
  });

  describe('makeSelectFetchPrefilledValuesError', () => {
    it('should select fetchPrefilledValuesError from state', () => {
      const selector = makeSelectFetchPrefilledValuesError();
      const result = selector(mockState);

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

    it('should return error when present', () => {
      const errorState = fromJS({
        commonTestAndPreview: {
          fetchPrefilledValuesError: 'Fetch failed',
        },
      });
      const selector = makeSelectFetchPrefilledValuesError();
      const result = selector(errorState);

      expect(result).toBe('Fetch failed');
    });
  });

  describe('makeSelectFetchPrefilledValuesErrors', () => {
    it('should select fetchPrefilledValuesErrors and convert to JS array', () => {
      const selector = makeSelectFetchPrefilledValuesErrors();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(Array.isArray(result)).toBe(true);
      expect(result.length).toBe(1);
      expect(result[0].code).toBe(7078);
      expect(result[0].message).toBe('User profile not found');
    });

    it('should return empty array if errors do not exist', () => {
      const emptyState = fromJS({
        commonTestAndPreview: {
          fetchPrefilledValuesErrors: [],
        },
      });
      const selector = makeSelectFetchPrefilledValuesErrors();
      const result = selector(emptyState);

      expect(result).toEqual([]);
    });

    it('should handle null errors', () => {
      const nullState = fromJS({
        commonTestAndPreview: {
          fetchPrefilledValuesErrors: null,
        },
      });
      const selector = makeSelectFetchPrefilledValuesErrors();
      const result = selector(nullState);

      expect(result).toEqual([]);
    });
  });

  describe('makeSelectSenderDetailsForChannel', () => {
    it('should select sender details for given channel as JS array', () => {
      const selector = makeSelectSenderDetailsForChannel('SMS');
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(Array.isArray(result)).toBe(true);
      expect(result.length).toBe(1);
      expect(result[0].domainName).toBe('SMS Domain');
    });

    it('should return empty array for channel with no data', () => {
      const selector = makeSelectSenderDetailsForChannel('EMAIL');
      const result = selector(mockState);

      expect(result).toEqual([]);
    });

    it('should return empty array when substate or byChannel missing', () => {
      const emptyState = fromJS({});
      const selector = makeSelectSenderDetailsForChannel('SMS');
      const result = selector(emptyState);

      expect(result).toEqual([]);
    });
  });

  describe('makeSelectSenderDetailsByChannel', () => {
    it('should select senderDetailsByChannel and convert to JS object', () => {
      const selector = makeSelectSenderDetailsByChannel();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(result.SMS).toBeDefined();
      expect(Array.isArray(result.SMS)).toBe(true);
      expect(result.SMS[0].domainName).toBe('SMS Domain');
      expect(result.EMAIL).toEqual([]);
      expect(result.WHATSAPP).toEqual([]);
    });

    it('should return empty object when domain is missing', () => {
      const emptyState = fromJS({});
      const selector = makeSelectSenderDetailsByChannel();
      const result = selector(emptyState);

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

  describe('makeSelectWeCrmAccounts', () => {
    it('should select wecrmAccounts and convert to JS array', () => {
      const selector = makeSelectWeCrmAccounts();
      const result = selector(mockState);

      expect(result).toBeDefined();
      expect(Array.isArray(result)).toBe(true);
      expect(result.length).toBe(1);
      expect(result[0].name).toBe('WeCRM Account 1');
      expect(result[0].sourceAccountIdentifier).toBe('waba-1');
    });

    it('should return empty array when wecrmAccounts is missing', () => {
      const emptyState = fromJS({ commonTestAndPreview: {} });
      const selector = makeSelectWeCrmAccounts();
      const result = selector(emptyState);

      expect(result).toEqual([]);
    });
  });

  describe('makeSelectIsLoadingSenderDetails', () => {
    it('should select isLoadingSenderDetails flag', () => {
      const selector = makeSelectIsLoadingSenderDetails();
      const result = selector(mockState);

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

    it('should return true when loading', () => {
      const loadingState = fromJS({
        commonTestAndPreview: { isLoadingSenderDetails: true },
      });
      const selector = makeSelectIsLoadingSenderDetails();
      const result = selector(loadingState);

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

  describe('makeSelectFetchSenderDetailsError', () => {
    it('should select fetchSenderDetailsError from state', () => {
      const selector = makeSelectFetchSenderDetailsError();
      const result = selector(mockState);

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

    it('should return error when present', () => {
      const errorState = fromJS({
        commonTestAndPreview: { fetchSenderDetailsError: 'Sender fetch failed' },
      });
      const selector = makeSelectFetchSenderDetailsError();
      const result = selector(errorState);

      expect(result).toBe('Sender fetch failed');
    });
  });

  describe('makeSelectFetchWeCrmAccountsError', () => {
    it('should select fetchWeCrmAccountsError from state', () => {
      const selector = makeSelectFetchWeCrmAccountsError();
      const result = selector(mockState);

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

    it('should return error when present', () => {
      const errorState = fromJS({
        commonTestAndPreview: { fetchWeCrmAccountsError: 'WeCRM fetch failed' },
      });
      const selector = makeSelectFetchWeCrmAccountsError();
      const result = selector(errorState);

      expect(result).toBe('WeCRM fetch failed');
    });
  });

  describe('Edge Cases', () => {
    it('should handle undefined state gracefully', () => {
      const selector = makeSelectCustomers();
      const result = selector(undefined);

      // Should not throw, but may return undefined
      expect(result).toBeUndefined();
    });

    it('should handle missing commonTestAndPreview domain', () => {
      const stateWithoutDomain = fromJS({});
      const selector = makeSelectCustomers();
      const result = selector(stateWithoutDomain);

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

    it('should memoize selector results', () => {
      const selector = makeSelectCustomers();
      const result1 = selector(mockState);
      const result2 = selector(mockState);

      // Should return same reference for same input (memoization)
      expect(result1).toBe(result2);
    });

    it('should handle deeply nested state structures', () => {
      const nestedState = fromJS({
        commonTestAndPreview: {
          tags: {
            loading: true,
            error: 'Error',
            required: [{ name: 'tag1' }, { name: 'tag2' }],
            optional: [{ name: 'tag3' }],
          },
        },
      });
      const selector = makeSelectTags();
      const result = selector(nestedState);

      expect(result.get('loading')).toBe(true);
      expect(result.get('error')).toBe('Error');
      expect(result.get('required').size).toBe(2);
      expect(result.get('optional').size).toBe(1);
    });
  });
});
