import { renderHook } from '@testing-library/react';
import { useCharacterCount } from './useCharacterCount';

describe('useCharacterCount', () => {
  let mockFormatMessage;
  const mockMessages = {
    characterCount: {
      defaultMessage: '{currentLength}/{maxLength}',
    },
  };

  beforeEach(() => {
    mockFormatMessage = jest.fn((message, values) => {
      if (values) {
        return `${values.currentLength}/${values.maxLength}`;
      }
      return message.defaultMessage || message;
    });
  });

  describe('updateCharacterCount', () => {
    it('should update character count when labelRef exists', () => {
      const { result } = renderHook(() => useCharacterCount(mockFormatMessage, mockMessages));
      const mockRef = { current: { textContent: '' } };

      result.current.updateCharacterCount(mockRef, 10, 100);

      expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.characterCount, {
        currentLength: 10,
        maxLength: 100,
      });
      expect(mockRef.current.textContent).toBe('10/100');
    });

    it('should not update when labelRef is null', () => {
      const { result } = renderHook(() => useCharacterCount(mockFormatMessage, mockMessages));

      result.current.updateCharacterCount(null, 10, 100);

      // formatMessage should not be called when labelRef is null
      expect(mockFormatMessage).not.toHaveBeenCalled();
    });

    it('should not update when labelRef.current is null', () => {
      const { result } = renderHook(() => useCharacterCount(mockFormatMessage, mockMessages));
      const mockRef = { current: null };

      expect(() => {
        result.current.updateCharacterCount(mockRef, 10, 100);
      }).not.toThrow();
    });

    it('should not update when labelRef.current is undefined', () => {
      const { result } = renderHook(() => useCharacterCount(mockFormatMessage, mockMessages));
      const mockRef = { current: undefined };

      expect(() => {
        result.current.updateCharacterCount(mockRef, 10, 100);
      }).not.toThrow();
    });

    it('should handle zero character count', () => {
      const { result } = renderHook(() => useCharacterCount(mockFormatMessage, mockMessages));
      const mockRef = { current: { textContent: '' } };

      result.current.updateCharacterCount(mockRef, 0, 50);

      expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.characterCount, {
        currentLength: 0,
        maxLength: 50,
      });
      expect(mockRef.current.textContent).toBe('0/50');
    });

    it('should handle maximum character count', () => {
      const { result } = renderHook(() => useCharacterCount(mockFormatMessage, mockMessages));
      const mockRef = { current: { textContent: '' } };

      result.current.updateCharacterCount(mockRef, 100, 100);

      expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.characterCount, {
        currentLength: 100,
        maxLength: 100,
      });
      expect(mockRef.current.textContent).toBe('100/100');
    });

    it('should update multiple times correctly', () => {
      const { result } = renderHook(() => useCharacterCount(mockFormatMessage, mockMessages));
      const mockRef = { current: { textContent: '' } };

      result.current.updateCharacterCount(mockRef, 5, 50);
      expect(mockRef.current.textContent).toBe('5/50');

      result.current.updateCharacterCount(mockRef, 25, 50);
      expect(mockRef.current.textContent).toBe('25/50');

      result.current.updateCharacterCount(mockRef, 50, 50);
      expect(mockRef.current.textContent).toBe('50/50');
    });

    it('should use memoized callback', () => {
      const { result, rerender } = renderHook(
        ({ formatMessage, messages }) => useCharacterCount(formatMessage, messages),
        {
          initialProps: { formatMessage: mockFormatMessage, messages: mockMessages },
        }
      );

      const firstUpdateCharacterCount = result.current.updateCharacterCount;

      rerender({ formatMessage: mockFormatMessage, messages: mockMessages });

      // Should be the same function reference if formatMessage and messages haven't changed
      expect(result.current.updateCharacterCount).toBe(firstUpdateCharacterCount);
    });

    it('should create new callback when formatMessage changes', () => {
      const newFormatMessage = jest.fn();
      const { result, rerender } = renderHook(
        ({ formatMessage, messages }) => useCharacterCount(formatMessage, messages),
        {
          initialProps: { formatMessage: mockFormatMessage, messages: mockMessages },
        }
      );

      const firstUpdateCharacterCount = result.current.updateCharacterCount;

      rerender({ formatMessage: newFormatMessage, messages: mockMessages });

      expect(result.current.updateCharacterCount).not.toBe(firstUpdateCharacterCount);
    });

    it('should create new callback when messages changes', () => {
      const newMessages = { characterCount: { defaultMessage: 'New message' } };
      const { result, rerender } = renderHook(
        ({ formatMessage, messages }) => useCharacterCount(formatMessage, messages),
        {
          initialProps: { formatMessage: mockFormatMessage, messages: mockMessages },
        }
      );

      const firstUpdateCharacterCount = result.current.updateCharacterCount;

      rerender({ formatMessage: mockFormatMessage, messages: newMessages });

      expect(result.current.updateCharacterCount).not.toBe(firstUpdateCharacterCount);
    });
  });
});

