import { renderHook } from '@testing-library/react';
import { act } from 'react';
import { useTagManagement } from './useTagManagement';
import { TAG, EMBEDDED, DEFAULT, FULL, ALL, LIBRARY } from '../../../Whatsapp/constants';
import { SMS } from '../../../CreativesContainer/constants';

describe('useTagManagement', () => {
  const mockFetchSchemaForEntity = jest.fn();
  const mockGlobalActions = {
    fetchSchemaForEntity: mockFetchSchemaForEntity,
  };

  const defaultLocation = {
    query: {
      type: '',
      module: '',
    },
  };

  const defaultMetaEntities = {
    tags: {
      standard: [
        { id: 1, name: 'Tag 1' },
        { id: 2, name: 'Tag 2' },
      ],
    },
  };

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('Initialization', () => {
    it('should initialize with empty tags array', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: null,
        })
      );

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

    it('should initialize with tags from metaEntities', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
        })
      );

      expect(result.current.tags).toEqual(defaultMetaEntities.tags.standard);
    });
  });

  describe('Tag Fetching', () => {
    it('should fetch tags on mount with default context', () => {
      renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: null,
        })
      );

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: DEFAULT,
        embedded: FULL,
      });
    });

    it('should fetch tags with embedded context when type is EMBEDDED', () => {
      const location = {
        query: {
          type: EMBEDDED,
          module: 'test-module',
        },
      };

      renderHook(() =>
        useTagManagement({
          location,
          globalActions: mockGlobalActions,
          metaEntities: null,
        })
      );

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: 'test-module',
        embedded: EMBEDDED,
      });
    });

    it('should use getDefaultTags as context when provided', () => {
      const getDefaultTags = 'custom-context';

      renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: null,
          getDefaultTags,
        })
      );

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: 'custom-context',
        embedded: FULL,
      });
    });

    it('should not fetch if globalActions.fetchSchemaForEntity is missing', () => {
      renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: {},
          metaEntities: null,
        })
      );

      expect(mockFetchSchemaForEntity).not.toHaveBeenCalled();
    });

    it('should not fetch again if tagFetchKey has not changed', () => {
      const { rerender } = renderHook(
        ({ location, globalActions }) =>
          useTagManagement({
            location,
            globalActions,
            metaEntities: null,
          }),
        {
          initialProps: {
            location: defaultLocation,
            globalActions: mockGlobalActions,
          },
        }
      );

      expect(mockFetchSchemaForEntity).toHaveBeenCalledTimes(1);

      rerender({
        location: defaultLocation,
        globalActions: mockGlobalActions,
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledTimes(1);
    });

    it('should fetch again when location type changes', () => {
      const { rerender } = renderHook(
        ({ location, globalActions }) =>
          useTagManagement({
            location,
            globalActions: mockGlobalActions,
            metaEntities: null,
          }),
        {
          initialProps: {
            location: defaultLocation,
            globalActions: mockGlobalActions,
          },
        }
      );

      expect(mockFetchSchemaForEntity).toHaveBeenCalledTimes(1);

      const newLocation = {
        query: {
          type: EMBEDDED,
          module: 'test',
        },
      };

      rerender({
        location: newLocation,
        globalActions: mockGlobalActions,
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledTimes(2);
    });

    it('should fetch again when getDefaultTags changes', () => {
      const { rerender } = renderHook(
        ({ getDefaultTags, globalActions }) =>
          useTagManagement({
            location: defaultLocation,
            globalActions,
            metaEntities: null,
            getDefaultTags,
          }),
        {
          initialProps: {
            getDefaultTags: 'context1',
            globalActions: mockGlobalActions,
          },
        }
      );

      expect(mockFetchSchemaForEntity).toHaveBeenCalledTimes(1);

      rerender({
        getDefaultTags: 'context2',
        globalActions: mockGlobalActions,
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledTimes(2);
    });
  });

  describe('Tag Updates from metaEntities', () => {
    it('should update tags when metaEntities changes', () => {
      const { result, rerender } = renderHook(
        ({ metaEntities }) =>
          useTagManagement({
            location: defaultLocation,
            globalActions: mockGlobalActions,
            metaEntities,
          }),
        {
          initialProps: {
            metaEntities: null,
          },
        }
      );

      expect(result.current.tags).toEqual([]);

      rerender({ metaEntities: defaultMetaEntities });

      expect(result.current.tags).toEqual(defaultMetaEntities.tags.standard);
    });

    it('should use supportedTags when embedded in library mode without getDefaultTags', () => {
      // Define supportedTags outside to maintain stable reference
      const supportedTagsArray = [
        { id: 3, name: 'Supported Tag 1' },
        { id: 4, name: 'Supported Tag 2' },
      ];

      const location = {
        query: {
          type: EMBEDDED,
          module: LIBRARY,
        },
      };

      const { result } = renderHook(
        ({ supportedTags }) =>
          useTagManagement({
            location,
            globalActions: mockGlobalActions,
            metaEntities: defaultMetaEntities,
            supportedTags,
          }),
        {
          initialProps: {
            supportedTags: supportedTagsArray,
          },
        }
      );

      expect(result.current.tags).toEqual(supportedTagsArray);
    });

    it('should use metaEntities tags when embedded in library mode with getDefaultTags', () => {
      // Define supportedTags outside to maintain stable reference
      const supportedTagsArray = [
        { id: 3, name: 'Supported Tag 1' },
      ];

      const location = {
        query: {
          type: EMBEDDED,
          module: LIBRARY,
        },
      };

      const { result } = renderHook(
        ({ supportedTags }) =>
          useTagManagement({
            location,
            globalActions: mockGlobalActions,
            metaEntities: defaultMetaEntities,
            supportedTags,
            getDefaultTags: 'custom',
          }),
        {
          initialProps: {
            supportedTags: supportedTagsArray,
          },
        }
      );

      expect(result.current.tags).toEqual(defaultMetaEntities.tags.standard);
    });

    it('should handle empty metaEntities.tags.standard', () => {
      const emptyMetaEntities = {
        tags: {
          standard: [],
        },
      };

      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: emptyMetaEntities,
        })
      );

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

    it('should handle missing metaEntities.tags.standard', () => {
      const metaEntitiesWithoutTags = {
        tags: {},
      };

      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: metaEntitiesWithoutTags,
        })
      );

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

    it('should handle null metaEntities', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: null,
        })
      );

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

  describe('handleOnTagsContextChange', () => {
    it('should fetch tags with new context', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
        })
      );

      act(() => {
        result.current.handleOnTagsContextChange('new-context');
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: 'new-context',
        embedded: FULL,
      });
    });

    it('should use DEFAULT context when data is ALL', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
        })
      );

      act(() => {
        result.current.handleOnTagsContextChange(ALL);
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: DEFAULT,
        embedded: FULL,
      });
    });

    it('should use DEFAULT context when data is "all" (case insensitive)', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
        })
      );

      act(() => {
        result.current.handleOnTagsContextChange('ALL');
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: DEFAULT,
        embedded: FULL,
      });
    });

    it('should use embedded context when location type is EMBEDDED', () => {
      const location = {
        query: {
          type: EMBEDDED,
          module: 'test-module',
        },
      };

      const { result } = renderHook(() =>
        useTagManagement({
          location,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
        })
      );

      act(() => {
        result.current.handleOnTagsContextChange('new-context');
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: 'new-context',
        embedded: EMBEDDED,
      });
    });

    it('should use getDefaultTags as context when provided', () => {
      const getDefaultTags = 'custom-context';

      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
          getDefaultTags,
        })
      );

      act(() => {
        result.current.handleOnTagsContextChange('new-context');
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: 'custom-context',
        embedded: FULL,
      });
    });

    it('should handle null data', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
        })
      );

      act(() => {
        result.current.handleOnTagsContextChange(null);
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: DEFAULT,
        embedded: FULL,
      });
    });

    it('should handle empty string data', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
        })
      );

      act(() => {
        result.current.handleOnTagsContextChange('');
      });

      expect(mockFetchSchemaForEntity).toHaveBeenCalledWith({
        layout: SMS,
        type: TAG,
        context: DEFAULT,
        embedded: FULL,
      });
    });

    it('should not throw when globalActions is missing', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: {},
          metaEntities: defaultMetaEntities,
        })
      );

      expect(() => {
        act(() => {
          result.current.handleOnTagsContextChange('context');
        });
      }).not.toThrow();
    });
  });

  describe('validationConfig', () => {
    it('should return validation config with tags', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: defaultLocation,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
          getDefaultTags: 'custom',
        })
      );

      expect(result.current.validationConfig).toEqual({
        tagsParam: defaultMetaEntities.tags.standard,
        location: defaultLocation,
        tagModule: 'custom',
      });
    });

    it('should update validation config when tags change', () => {
      const { result, rerender } = renderHook(
        ({ metaEntities }) =>
          useTagManagement({
            location: defaultLocation,
            globalActions: mockGlobalActions,
            metaEntities,
          }),
        {
          initialProps: {
            metaEntities: defaultMetaEntities,
          },
        }
      );

      const firstConfig = result.current.validationConfig;

      const newMetaEntities = {
        tags: {
          standard: [{ id: 7, name: 'New Tag' }],
        },
      };

      rerender({ metaEntities: newMetaEntities });

      expect(result.current.validationConfig.tagsParam).toEqual(newMetaEntities.tags.standard);
      expect(result.current.validationConfig).not.toBe(firstConfig);
    });

    it('should memoize validation config when dependencies do not change', () => {
      const { result, rerender } = renderHook(
        ({ metaEntities }) =>
          useTagManagement({
            location: defaultLocation,
            globalActions: mockGlobalActions,
            metaEntities,
          }),
        {
          initialProps: {
            metaEntities: defaultMetaEntities,
          },
        }
      );

      const firstConfig = result.current.validationConfig;

      rerender({ metaEntities: defaultMetaEntities });

      // Verify memoization by checking reference equality
      expect(result.current.validationConfig).toBe(firstConfig);
    });
  });

  describe('Edge Cases', () => {
    it('should handle location without query', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: {},
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
        })
      );

      expect(result.current.tags).toEqual(defaultMetaEntities.tags.standard);
    });

    it('should handle location as null', () => {
      const { result } = renderHook(() =>
        useTagManagement({
          location: null,
          globalActions: mockGlobalActions,
          metaEntities: defaultMetaEntities,
        })
      );

      expect(result.current.tags).toEqual(defaultMetaEntities.tags.standard);
    });
  });
});

