/**
 * useInAppContent Hook Tests
 *
 * Tests for the useInAppContent custom hook that manages device-specific content
 */

import React from 'react';
import { render, screen, act } from '@testing-library/react';
import '@testing-library/jest-dom';
import { useInAppContent } from '../useInAppContent';
import { DEVICE_TYPES } from '../../constants';

// Test wrapper component
const TestComponent = ({ initialContent, options, onStateChange }) => {
  const inAppState = useInAppContent(initialContent, options);

  React.useEffect(() => {
    if (onStateChange) {
      onStateChange(inAppState);
    }
  });

  return (
    <div>
      <div data-testid="active-device">{inAppState.activeDevice}</div>
      <div data-testid="current-content">{inAppState.content}</div>
      <div data-testid="android-content">{inAppState.deviceContent[DEVICE_TYPES.ANDROID]}</div>
      <div data-testid="ios-content">{inAppState.deviceContent[DEVICE_TYPES.IOS]}</div>
      <div data-testid="keep-same">{String(inAppState.keepContentSame)}</div>
      <div data-testid="is-dirty">{String(inAppState.isDirty)}</div>
      <div data-testid="has-content">{String(inAppState.hasContent)}</div>
      <div data-testid="content-size">{inAppState.getContentSize()}</div>

      <button onClick={() => inAppState.updateContent('Updated')} data-testid="update-btn">
        Update
      </button>
      <button onClick={() => inAppState.switchDevice(DEVICE_TYPES.IOS)} data-testid="switch-ios">
        Switch to iOS
      </button>
      <button onClick={() => inAppState.switchDevice(DEVICE_TYPES.ANDROID)} data-testid="switch-android">
        Switch to Android
      </button>
      <button onClick={() => inAppState.toggleContentSync(true)} data-testid="enable-sync">
        Enable Sync
      </button>
      <button onClick={() => inAppState.toggleContentSync(false)} data-testid="disable-sync">
        Disable Sync
      </button>
      <button onClick={() => inAppState.markAsSaved()} data-testid="mark-saved">
        Mark Saved
      </button>
    </div>
  );
};

describe('useInAppContent', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.runOnlyPendingTimers();
    jest.useRealTimers();
  });

  describe('Initial State', () => {
    it('initializes with default content for both devices', () => {
      render(<TestComponent />);

      expect(screen.getByTestId('active-device')).toHaveTextContent(DEVICE_TYPES.ANDROID);
      expect(screen.getByTestId('android-content')).toBeInTheDocument();
      expect(screen.getByTestId('ios-content')).toBeInTheDocument();
      expect(screen.getByTestId('keep-same')).toHaveTextContent('false');
      expect(screen.getByTestId('is-dirty')).toHaveTextContent('false');
    });

    it('initializes with custom content for Android', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '<p>Custom Android</p>',
      };

      render(<TestComponent initialContent={customContent} />);

      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Custom Android</p>');
    });

    it('initializes with custom content for iOS', () => {
      const customContent = {
        [DEVICE_TYPES.IOS]: '<p>Custom iOS</p>',
      };

      render(<TestComponent initialContent={customContent} />);

      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>Custom iOS</p>');
    });

    it('initializes with custom content for both devices', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '<p>Android</p>',
        [DEVICE_TYPES.IOS]: '<p>iOS</p>',
      };

      render(<TestComponent initialContent={customContent} />);

      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Android</p>');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>iOS</p>');
    });

    it('starts with Android as active device', () => {
      render(<TestComponent />);

      expect(screen.getByTestId('active-device')).toHaveTextContent(DEVICE_TYPES.ANDROID);
    });

    it('has no content by default (empty strings)', () => {
      render(<TestComponent />);

      expect(screen.getByTestId('has-content')).toHaveTextContent('false');
    });
  });

  describe('Device Switching', () => {
    it('switches from Android to iOS', () => {
      render(<TestComponent />);

      expect(screen.getByTestId('active-device')).toHaveTextContent(DEVICE_TYPES.ANDROID);

      act(() => {
        screen.getByTestId('switch-ios').click();
      });

      expect(screen.getByTestId('active-device')).toHaveTextContent(DEVICE_TYPES.IOS);
    });

    it('switches from iOS to Android', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.switchDevice(DEVICE_TYPES.IOS);
      });

      expect(screen.getByTestId('active-device')).toHaveTextContent(DEVICE_TYPES.IOS);

      act(() => {
        screen.getByTestId('switch-android').click();
      });

      expect(screen.getByTestId('active-device')).toHaveTextContent(DEVICE_TYPES.ANDROID);
    });

    it('updates current content when switching devices', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '<p>Android Content</p>',
        [DEVICE_TYPES.IOS]: '<p>iOS Content</p>',
      };

      render(<TestComponent initialContent={customContent} />);

      expect(screen.getByTestId('current-content')).toHaveTextContent('<p>Android Content</p>');

      act(() => {
        screen.getByTestId('switch-ios').click();
      });

      expect(screen.getByTestId('current-content')).toHaveTextContent('<p>iOS Content</p>');
    });

    it('ignores invalid device types', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      const initialDevice = screen.getByTestId('active-device').textContent;

      act(() => {
        inAppState.switchDevice('InvalidDevice');
      });

      expect(screen.getByTestId('active-device')).toHaveTextContent(initialDevice);
    });

    it('does not switch if already on target device', () => {
      render(<TestComponent />);

      expect(screen.getByTestId('active-device')).toHaveTextContent(DEVICE_TYPES.ANDROID);

      act(() => {
        screen.getByTestId('switch-android').click();
      });

      expect(screen.getByTestId('active-device')).toHaveTextContent(DEVICE_TYPES.ANDROID);
    });
  });

  describe('Content Updates', () => {
    it('updates content for current device', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.updateContent('<p>New Content</p>');
      });

      expect(screen.getByTestId('current-content')).toHaveTextContent('<p>New Content</p>');
    });

    it('marks content as dirty after update', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

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

      act(() => {
        inAppState.updateContent('<p>Updated</p>');
      });

      expect(screen.getByTestId('is-dirty')).toHaveTextContent('true');
    });

    it('updates only current device when sync is disabled', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '<p>Android</p>',
        [DEVICE_TYPES.IOS]: '<p>iOS</p>',
      };

      let inAppState;
      render(<TestComponent
        initialContent={customContent}
        onStateChange={(state) => { inAppState = state; }}
      />);

      // Currently on Android
      act(() => {
        inAppState.updateContent('<p>Updated Android</p>');
      });

      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Updated Android</p>');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>iOS</p>');
    });

    it('calls onChange callback when content is updated', () => {
      const mockOnChange = jest.fn();
      let inAppState;

      render(<TestComponent
        options={{ onChange: mockOnChange }}
        onStateChange={(state) => { inAppState = state; }}
      />);

      act(() => {
        inAppState.updateContent('<p>New</p>');
      });

      expect(mockOnChange).toHaveBeenCalled();
      expect(mockOnChange).toHaveBeenCalledWith(
        expect.objectContaining({
          [DEVICE_TYPES.ANDROID]: '<p>New</p>',
        }),
        DEVICE_TYPES.ANDROID
      );
    });
  });

  describe('Content Sync', () => {
    it('enables content sync', () => {
      render(<TestComponent />);

      expect(screen.getByTestId('keep-same')).toHaveTextContent('false');

      act(() => {
        screen.getByTestId('enable-sync').click();
      });

      expect(screen.getByTestId('keep-same')).toHaveTextContent('true');
    });

    it('disables content sync', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.toggleContentSync(true);
      });

      expect(screen.getByTestId('keep-same')).toHaveTextContent('true');

      act(() => {
        screen.getByTestId('disable-sync').click();
      });

      expect(screen.getByTestId('keep-same')).toHaveTextContent('false');
    });

    it('syncs content to both devices when sync is enabled', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '<p>Android</p>',
        [DEVICE_TYPES.IOS]: '<p>iOS</p>',
      };

      render(<TestComponent initialContent={customContent} />);

      // Initially different content
      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Android</p>');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>iOS</p>');

      act(() => {
        screen.getByTestId('enable-sync').click();
      });

      // After enabling sync, both should have Android content (current device)
      const syncedContent = screen.getByTestId('android-content').textContent;
      expect(screen.getByTestId('ios-content')).toHaveTextContent(syncedContent);
    });

    it('updates both devices when content is updated with sync enabled', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.toggleContentSync(true);
      });

      act(() => {
        inAppState.updateContent('<p>Synced Content</p>');
      });

      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Synced Content</p>');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>Synced Content</p>');
    });

    it('marks as dirty when sync is enabled', () => {
      render(<TestComponent />);

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

      act(() => {
        screen.getByTestId('enable-sync').click();
      });

      expect(screen.getByTestId('is-dirty')).toHaveTextContent('true');
    });
  });

  describe('Save Management', () => {
    it('marks content as saved', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.updateContent('<p>New</p>');
      });

      expect(screen.getByTestId('is-dirty')).toHaveTextContent('true');

      act(() => {
        screen.getByTestId('mark-saved').click();
      });

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

    it('calls onSave callback when marked as saved', () => {
      const mockOnSave = jest.fn();
      let inAppState;

      render(<TestComponent
        options={{ onSave: mockOnSave }}
        onStateChange={(state) => { inAppState = state; }}
      />);

      act(() => {
        inAppState.markAsSaved();
      });

      expect(mockOnSave).toHaveBeenCalled();
    });

    it('triggers auto-save after interval', async () => {
      const mockOnSave = jest.fn();
      let inAppState;

      render(<TestComponent
        options={{ autoSave: true, autoSaveInterval: 5000, onSave: mockOnSave }}
        onStateChange={(state) => { inAppState = state; }}
      />);

      await act(async () => {
        inAppState.updateContent('<p>Auto Save Test</p>');
      });

      expect(mockOnSave).not.toHaveBeenCalled();

      await act(async () => {
        jest.advanceTimersByTime(5000);
      });

      expect(mockOnSave).toHaveBeenCalled();
    });

    it('disables auto-save when autoSave is false', () => {
      const mockOnSave = jest.fn();
      let inAppState;

      render(<TestComponent
        options={{ autoSave: false, autoSaveInterval: 5000, onSave: mockOnSave }}
        onStateChange={(state) => { inAppState = state; }}
      />);

      act(() => {
        inAppState.updateContent('<p>No Auto Save</p>');
      });

      act(() => {
        jest.advanceTimersByTime(10000);
      });

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

    it('cancels previous auto-save timer on new update', async () => {
      const mockOnSave = jest.fn();
      let inAppState;

      render(<TestComponent
        options={{ autoSave: true, autoSaveInterval: 5000, onSave: mockOnSave }}
        onStateChange={(state) => { inAppState = state; }}
      />);

      await act(async () => {
        inAppState.updateContent('<p>First</p>');
      });

      await act(async () => {
        jest.advanceTimersByTime(3000);
      });

      await act(async () => {
        inAppState.updateContent('<p>Second</p>');
      });

      await act(async () => {
        jest.advanceTimersByTime(3000);
      });

      // Should not have saved yet (timer was reset)
      expect(mockOnSave).not.toHaveBeenCalled();

      await act(async () => {
        jest.advanceTimersByTime(2000);
      });

      // Should save after 5 seconds from last update
      expect(mockOnSave).toHaveBeenCalledTimes(1);
    });
  });

  describe('Content Queries', () => {
    it('reports hasContent as true for non-empty content', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '<p>Content</p>',
      };

      render(<TestComponent initialContent={customContent} />);

      expect(screen.getByTestId('has-content')).toHaveTextContent('true');
    });

    it('reports hasContent as false for empty content', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '',
        [DEVICE_TYPES.IOS]: '',
      };

      let inAppState;
      render(<TestComponent
        initialContent={customContent}
        onStateChange={(state) => { inAppState = state; }}
      />);

      // Explicitly update to empty string to override any default
      act(() => {
        inAppState.updateContent('');
      });

      expect(screen.getByTestId('has-content')).toHaveTextContent('false');
    });

    it('calculates content size correctly', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '12345',
      };

      render(<TestComponent initialContent={customContent} />);

      expect(screen.getByTestId('content-size')).toHaveTextContent('5');
    });

    it('returns 0 size for empty content', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '',
        [DEVICE_TYPES.IOS]: '',
      };

      let inAppState;
      render(<TestComponent
        initialContent={customContent}
        onStateChange={(state) => { inAppState = state; }}
      />);

      // Explicitly update to empty string
      act(() => {
        inAppState.updateContent('');
      });

      expect(screen.getByTestId('content-size')).toHaveTextContent('0');
    });

    it('getDeviceContent returns content for specific device', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '<p>Android</p>',
        [DEVICE_TYPES.IOS]: '<p>iOS</p>',
      };

      let inAppState;
      render(<TestComponent
        initialContent={customContent}
        onStateChange={(state) => { inAppState = state; }}
      />);

      expect(inAppState.getDeviceContent(DEVICE_TYPES.ANDROID)).toBe('<p>Android</p>');
      expect(inAppState.getDeviceContent(DEVICE_TYPES.IOS)).toBe('<p>iOS</p>');
    });

    it('getDeviceContent returns empty string for missing device', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      expect(inAppState.getDeviceContent('InvalidDevice')).toBe('');
    });
  });

  describe('setDeviceContent Method', () => {
    it('sets content for specific device', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.setDeviceContent(DEVICE_TYPES.IOS, '<p>New iOS Content</p>');
      });

      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>New iOS Content</p>');
    });

    it('marks as dirty when setting device content', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

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

      act(() => {
        inAppState.setDeviceContent(DEVICE_TYPES.ANDROID, '<p>New</p>');
      });

      expect(screen.getByTestId('is-dirty')).toHaveTextContent('true');
    });

    it('updates both devices when sync is enabled', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.toggleContentSync(true);
      });

      act(() => {
        inAppState.setDeviceContent(DEVICE_TYPES.ANDROID, '<p>Synced via setDeviceContent</p>');
      });

      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Synced via setDeviceContent</p>');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>Synced via setDeviceContent</p>');
    });

    it('ignores invalid device types', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      // Get initial content lengths to compare (not full text due to default content)
      const initialAndroidLength = screen.getByTestId('android-content').textContent.length;
      const initialIOSLength = screen.getByTestId('ios-content').textContent.length;

      act(() => {
        inAppState.setDeviceContent('InvalidDevice', '<p>Should not update</p>');
      });

      // Content should remain unchanged
      expect(screen.getByTestId('android-content').textContent.length).toBe(initialAndroidLength);
      expect(screen.getByTestId('ios-content').textContent.length).toBe(initialIOSLength);
    });
  });

  describe('Cleanup', () => {
    it('clears auto-save timer on unmount', () => {
      const mockOnSave = jest.fn();
      let inAppState;

      const { unmount } = render(<TestComponent
        options={{ autoSave: true, autoSaveInterval: 5000, onSave: mockOnSave }}
        onStateChange={(state) => { inAppState = state; }}
      />);

      act(() => {
        inAppState.updateContent('<p>Test</p>');
      });

      act(() => {
        jest.advanceTimersByTime(3000);
      });

      unmount();

      act(() => {
        jest.advanceTimersByTime(5000);
      });

      // Should not save after unmount
      expect(mockOnSave).not.toHaveBeenCalled();
    });
  });

  describe('Edge Cases', () => {
    it('handles undefined initialContent gracefully', () => {
      expect(() => {
        render(<TestComponent initialContent={undefined} />);
      }).not.toThrow();
    });

    it('handles null initialContent gracefully', () => {
      expect(() => {
        render(<TestComponent initialContent={null} />);
      }).not.toThrow();
    });

    it('handles empty options object', () => {
      expect(() => {
        render(<TestComponent options={{}} />);
      }).not.toThrow();
    });

    it('handles undefined options', () => {
      expect(() => {
        render(<TestComponent options={undefined} />);
      }).not.toThrow();
    });

    it('handles very large content', () => {
      const largeContent = 'a'.repeat(100000);
      const customContent = {
        [DEVICE_TYPES.ANDROID]: largeContent,
      };

      let inAppState;
      render(<TestComponent
        initialContent={customContent}
        onStateChange={(state) => { inAppState = state; }}
      />);

      expect(inAppState.getContentSize()).toBe(100000);
    });

    it('handles rapid device switching', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        for (let i = 0; i < 10; i++) {
          inAppState.switchDevice(i % 2 === 0 ? DEVICE_TYPES.IOS : DEVICE_TYPES.ANDROID);
        }
      });

      expect(screen.getByTestId('active-device')).toBeInTheDocument();
    });

    it('handles multiple rapid content updates', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        for (let i = 0; i < 5; i++) {
          inAppState.updateContent(`<p>Update ${i}</p>`);
        }
      });

      expect(screen.getByTestId('current-content')).toHaveTextContent('<p>Update 4</p>');
    });
  });

  describe('Integration Scenarios', () => {
    it('switches device and updates content', () => {
      const customContent = {
        [DEVICE_TYPES.ANDROID]: '<p>Android</p>',
        [DEVICE_TYPES.IOS]: '<p>iOS</p>',
      };

      let inAppState;
      render(<TestComponent
        initialContent={customContent}
        onStateChange={(state) => { inAppState = state; }}
      />);

      // Switch to iOS
      act(() => {
        inAppState.switchDevice(DEVICE_TYPES.IOS);
      });

      // Update iOS content
      act(() => {
        inAppState.updateContent('<p>Updated iOS</p>');
      });

      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>Updated iOS</p>');
      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Android</p>');
    });

    it('enables sync, updates content, then disables sync', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      // Enable sync
      act(() => {
        inAppState.toggleContentSync(true);
      });

      // Update content (should update both)
      act(() => {
        inAppState.updateContent('<p>Synced</p>');
      });

      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Synced</p>');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>Synced</p>');

      // Disable sync
      act(() => {
        inAppState.toggleContentSync(false);
      });

      // Update Android only
      act(() => {
        inAppState.updateContent('<p>Android Only</p>');
      });

      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Android Only</p>');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>Synced</p>');
    });

    it('updates content with auto-save and manual save', () => {
      const mockOnSave = jest.fn();
      let inAppState;

      render(<TestComponent
        options={{ autoSave: true, autoSaveInterval: 5000, onSave: mockOnSave }}
        onStateChange={(state) => { inAppState = state; }}
      />);

      // Update content
      act(() => {
        inAppState.updateContent('<p>Test</p>');
      });

      // Manual save before auto-save
      act(() => {
        inAppState.markAsSaved();
      });

      expect(mockOnSave).toHaveBeenCalledTimes(1);

      // Auto-save should not trigger after manual save
      act(() => {
        jest.advanceTimersByTime(10000);
      });

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

  describe('setDeviceContent Coverage', () => {
    it('sets content for specific device when sync is disabled', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.setDeviceContent(DEVICE_TYPES.ANDROID, '<p>Android Only</p>');
      });

      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Android Only</p>');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('');
    });

    it('sets content for both devices when sync is enabled', () => {
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      // Enable sync first
      act(() => {
        inAppState.toggleContentSync(true);
      });

      // Then set content
      act(() => {
        inAppState.setDeviceContent(DEVICE_TYPES.ANDROID, '<p>Synced Content</p>');
      });

      expect(screen.getByTestId('android-content')).toHaveTextContent('<p>Synced Content</p>');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('<p>Synced Content</p>');
    });

    it('warns when setting content for invalid device', () => {
      const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.setDeviceContent('InvalidDevice', '<p>Content</p>');
      });

      expect(consoleSpy).toHaveBeenCalledWith(
        expect.stringContaining('Invalid device type')
      );

      consoleSpy.mockRestore();
    });

    it('warns when setting non-string content', () => {
      const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.setDeviceContent(DEVICE_TYPES.ANDROID, 123);
      });

      expect(consoleSpy).toHaveBeenCalledWith(
        expect.stringContaining('content must be a string')
      );

      consoleSpy.mockRestore();
    });
  });

  describe('Edge Cases Coverage', () => {
    it('handles updateContent with non-string input', () => {
      const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.updateContent(123);
      });

      expect(consoleSpy).toHaveBeenCalledWith(
        expect.stringContaining('newContent must be a string')
      );

      consoleSpy.mockRestore();
    });

    it('handles switchDevice with invalid device type', () => {
      const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
      let inAppState;
      render(<TestComponent onStateChange={(state) => { inAppState = state; }} />);

      act(() => {
        inAppState.switchDevice('InvalidDevice');
      });

      expect(consoleSpy).toHaveBeenCalledWith(
        expect.stringContaining('Invalid device type')
      );

      consoleSpy.mockRestore();
    });

    it('handles auto-save with content length below minimum', () => {
      const onSave = jest.fn();
      let inAppState;
      render(
        <TestComponent
          options={{ autoSave: true, autoSaveInterval: 100, onSave }}
          onStateChange={(state) => { inAppState = state; }}
        />
      );

      act(() => {
        inAppState.updateContent(''); // Empty content
      });

      act(() => {
        jest.advanceTimersByTime(200);
      });

      // onSave should not be called for empty content
      expect(onSave).not.toHaveBeenCalled();
    });

    it('handles auto-save interval below minimum', () => {
      const onSave = jest.fn();
      let inAppState;
      render(
        <TestComponent
          options={{ autoSave: true, autoSaveInterval: 500, onSave }} // Below 1000ms minimum
          onStateChange={(state) => { inAppState = state; }}
        />
      );

      act(() => {
        inAppState.updateContent('<p>Content</p>');
      });

      act(() => {
        jest.advanceTimersByTime(1000);
      });

      // onSave should not be called when interval is below minimum
      expect(onSave).not.toHaveBeenCalled();
    });
  });

  describe('setDeviceContent State Updates (L95-L109)', () => {
    it('updates Android content when newAndroidContent differs from current', () => {
      // With the fix, initialContent effect only runs on first load or when transitioning from empty to content
      // After initial load, changing initialContent won't update content (prevents overriding user edits)
      const initialContent = {
        [DEVICE_TYPES.ANDROID]: '',  // Start with empty
        [DEVICE_TYPES.IOS]: '',
      };

      const { rerender } = render(
        <TestComponent initialContent={initialContent} />
      );

      // Initially empty
      expect(screen.getByTestId('android-content')).toHaveTextContent('');

      // Transition from empty to meaningful content - this SHOULD update
      const updatedContent = {
        [DEVICE_TYPES.ANDROID]: 'updated android',
        [DEVICE_TYPES.IOS]: '',
      };

      rerender(<TestComponent initialContent={updatedContent} />);

      expect(screen.getByTestId('android-content')).toHaveTextContent('updated android');
    });

    it('updates iOS content when newIosContent differs from current', () => {
      // With the fix, initialContent effect only runs on first load or when transitioning from empty to content
      const initialContent = {
        [DEVICE_TYPES.ANDROID]: '',
        [DEVICE_TYPES.IOS]: '',  // Start with empty
      };

      const { rerender } = render(
        <TestComponent initialContent={initialContent} />
      );

      expect(screen.getByTestId('ios-content')).toHaveTextContent('');

      // Transition from empty to meaningful content - this SHOULD update
      const updatedContent = {
        [DEVICE_TYPES.ANDROID]: '',
        [DEVICE_TYPES.IOS]: 'updated ios',
      };

      rerender(<TestComponent initialContent={updatedContent} />);

      expect(screen.getByTestId('ios-content')).toHaveTextContent('updated ios');
    });

    it('updates both Android and iOS content when both differ', () => {
      // With the fix, initialContent effect only runs on first load or when transitioning from empty to content
      const initialContent = {
        [DEVICE_TYPES.ANDROID]: '',
        [DEVICE_TYPES.IOS]: '',
      };

      const { rerender } = render(
        <TestComponent initialContent={initialContent} />
      );

      expect(screen.getByTestId('android-content')).toHaveTextContent('');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('');

      // Transition from empty to meaningful content - this SHOULD update
      const updatedContent = {
        [DEVICE_TYPES.ANDROID]: 'updated android',
        [DEVICE_TYPES.IOS]: 'updated ios',
      };

      rerender(<TestComponent initialContent={updatedContent} />);

      expect(screen.getByTestId('android-content')).toHaveTextContent('updated android');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('updated ios');
    });

    it('does not update when content is the same', () => {
      const initialContent = {
        [DEVICE_TYPES.ANDROID]: 'same android',
        [DEVICE_TYPES.IOS]: 'same ios',
      };

      const { rerender } = render(
        <TestComponent initialContent={initialContent} />
      );

      const sameContent = {
        [DEVICE_TYPES.ANDROID]: 'same android',
        [DEVICE_TYPES.IOS]: 'same ios',
      };

      rerender(<TestComponent initialContent={sameContent} />);

      // Content should remain the same
      expect(screen.getByTestId('android-content')).toHaveTextContent('same android');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('same ios');
    });

    it('handles undefined newAndroidContent gracefully', () => {
      // With the fix, after initial load, rerendering with different initialContent won't update
      // This test now verifies that content persists even when initialContent changes
      const initialContent = {
        [DEVICE_TYPES.ANDROID]: 'initial android',
        [DEVICE_TYPES.IOS]: 'initial ios',
      };

      const { rerender } = render(
        <TestComponent initialContent={initialContent} />
      );

      const updatedContent = {
        [DEVICE_TYPES.IOS]: 'updated ios',  // Android is undefined
      };

      rerender(<TestComponent initialContent={updatedContent} />);

      // With the fix, content should NOT change after initial load (prevents overriding user edits)
      expect(screen.getByTestId('android-content')).toHaveTextContent('initial android');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('initial ios');
    });

    it('handles undefined newIosContent gracefully', () => {
      // With the fix, after initial load, rerendering with different initialContent won't update
      const initialContent = {
        [DEVICE_TYPES.ANDROID]: 'initial android',
        [DEVICE_TYPES.IOS]: 'initial ios',
      };

      const { rerender } = render(
        <TestComponent initialContent={initialContent} />
      );

      const updatedContent = {
        [DEVICE_TYPES.ANDROID]: 'updated android',  // iOS is undefined
      };

      rerender(<TestComponent initialContent={updatedContent} />);

      // With the fix, content should NOT change after initial load (prevents overriding user edits)
      expect(screen.getByTestId('android-content')).toHaveTextContent('initial android');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('initial ios');
    });

    it('returns updated content object when changes are made', () => {
      // Test that the transition from empty to content works correctly
      const initialContent = {
        [DEVICE_TYPES.ANDROID]: '',
        [DEVICE_TYPES.IOS]: '',
      };

      const { rerender } = render(
        <TestComponent initialContent={initialContent} />
      );

      // Transition from empty to content - this SHOULD update
      const updatedContent = {
        [DEVICE_TYPES.ANDROID]: 'updated',
        [DEVICE_TYPES.IOS]: 'updated',
      };

      rerender(<TestComponent initialContent={updatedContent} />);

      expect(screen.getByTestId('android-content')).toHaveTextContent('updated');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('updated');
    });

    it('returns previous content object when no changes', () => {
      const initialContent = {
        [DEVICE_TYPES.ANDROID]: 'same',
        [DEVICE_TYPES.IOS]: 'same',
      };

      let firstRender = true;
      let previousState;

      const TestObserver = ({ initialContent, onStateChange }) => {
        const inAppState = useInAppContent(initialContent);

        React.useEffect(() => {
          if (firstRender) {
            previousState = inAppState.deviceContent;
            firstRender = false;
          } else {
            // Check if object reference is the same
            onStateChange?.(inAppState, previousState);
          }
        });

        return (
          <div>
            <div data-testid="android-content">{inAppState.deviceContent[DEVICE_TYPES.ANDROID]}</div>
            <div data-testid="ios-content">{inAppState.deviceContent[DEVICE_TYPES.IOS]}</div>
          </div>
        );
      };

      const onStateChange = jest.fn();
      const { rerender } = render(
        <TestObserver initialContent={initialContent} onStateChange={onStateChange} />
      );

      const sameContent = {
        [DEVICE_TYPES.ANDROID]: 'same',
        [DEVICE_TYPES.IOS]: 'same',
      };

      rerender(<TestObserver initialContent={sameContent} onStateChange={onStateChange} />);

      // Content should be the same
      expect(screen.getByTestId('android-content')).toHaveTextContent('same');
      expect(screen.getByTestId('ios-content')).toHaveTextContent('same');
    });
  });
});

