/**
 * index.lazy.js Tests
 *
 * Tests for lazy loading wrapper, fallback component, and preload utilities
 */

import React from 'react';
import {
  render, screen, waitFor, act,
} from '@testing-library/react';
import '@testing-library/jest-dom';

// Mock CapSpin before importing the component
jest.mock('@capillarytech/cap-ui-library/CapSpin', () => function MockCapSpin({ size }) {
  return <div data-testid="cap-spin" data-size={size}>Loading...</div>;
});

// Mock the HTMLEditor component
// Note: React.lazy() expects a function that returns a promise
// The mock needs to be set up before the lazy import happens
jest.mock('../HTMLEditor', () => {
  const MockHTMLEditor = function MockHTMLEditor(props) {
    return (
      <div data-testid="html-editor">
Mock HTML Editor -
        {props.variant}
      </div>
    );
  };
  return {
    __esModule: true,
    default: MockHTMLEditor,
  };
});

describe('index.lazy.js', () => {
  // Reset modules before each test to ensure clean state
  beforeEach(() => {
    jest.clearAllMocks();
    // Clear the window flag
    if (typeof window !== 'undefined') {
      delete window.__htmlEditorLoaded;
    }
  });

  describe('HTMLEditorFallback', () => {
    it('renders loading fallback with default message', async () => {
      // Import after mocks are set up
      const { HTMLEditorFallback } = await import('../index.lazy');

      render(<HTMLEditorFallback />);

      expect(screen.getByTestId('cap-spin')).toBeInTheDocument();
      expect(screen.getByText('Loading HTML Editor...')).toBeInTheDocument();
    });

    it('renders loading fallback with custom message', async () => {
      const { HTMLEditorFallback } = await import('../index.lazy');

      render(<HTMLEditorFallback message="Initializing editor..." />);

      expect(screen.getByTestId('cap-spin')).toBeInTheDocument();
      expect(screen.getByText('Initializing editor...')).toBeInTheDocument();
    });

    it('renders CapSpin with large size', async () => {
      const { HTMLEditorFallback } = await import('../index.lazy');

      render(<HTMLEditorFallback />);

      const spinner = screen.getByTestId('cap-spin');
      expect(spinner).toHaveAttribute('data-size', 'large');
    });

    it('applies correct container CSS class', async () => {
      const { HTMLEditorFallback } = await import('../index.lazy');

      const { container } = render(<HTMLEditorFallback />);
      const fallbackDiv = container.firstChild;

      expect(fallbackDiv).toHaveClass('html-editor-lazy-fallback');
    });

    it('applies correct container CSS class for styling', async () => {
      const { HTMLEditorFallback } = await import('../index.lazy');

      const { container } = render(<HTMLEditorFallback />);
      const fallbackDiv = container.firstChild;

      expect(fallbackDiv).toHaveClass('html-editor-lazy-fallback');
    });

    it('applies correct layout CSS class', async () => {
      const { HTMLEditorFallback } = await import('../index.lazy');

      const { container } = render(<HTMLEditorFallback />);
      const fallbackDiv = container.firstChild;

      expect(fallbackDiv).toHaveClass('html-editor-lazy-fallback');
    });

    it('renders message with correct CSS class and content', async () => {
      const { HTMLEditorFallback } = await import('../index.lazy');

      const { container } = render(<HTMLEditorFallback message="Test message" />);
      const messageDiv = container.querySelector('.html-editor-lazy-fallback__message');

      expect(messageDiv).toHaveClass('html-editor-lazy-fallback__message');
      expect(messageDiv).toHaveTextContent('Test message');
    });
  });

  describe('HTMLEditorLazy', () => {
    it('renders the lazy loaded component', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      await act(async () => {
        render(<HTMLEditorLazy variant="email" />);
      });

      // Wait for lazy component to load - increase timeout and wait for Suspense to resolve
      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      }, { timeout: 3000 });

      const editor = screen.getByTestId('html-editor');
      expect(editor.textContent).toBe('Mock HTML Editor -email');
    });

    it('shows fallback while loading', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      const { container } = render(<HTMLEditorLazy variant="email" />);

      // Fallback should be visible initially (though it may load very quickly in tests)
      // The Suspense boundary is in place
      expect(container.firstChild).toBeInTheDocument();
    });

    it('forwards all props to the underlying component', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;
      const mockOnSave = jest.fn();

      render(
        <HTMLEditorLazy
          variant="inapp"
          onSave={mockOnSave}
          readOnly
          className="custom-class"
        />
      );

      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });

      const editor = screen.getByTestId('html-editor');
      expect(editor.textContent).toBe('Mock HTML Editor -inapp');
    });

    it('has correct display name', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.displayName).toBe('HTMLEditorLazy');
    });

    it('accepts variant prop', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      render(<HTMLEditorLazy variant="email" />);

      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });
    });

    it('accepts layoutType prop', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      render(<HTMLEditorLazy layoutType="modal" />);

      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });
    });

    it('accepts initialContent as string', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      render(<HTMLEditorLazy initialContent="<p>Test</p>" />);

      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });
    });

    it('accepts initialContent as object', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      render(<HTMLEditorLazy initialContent={{ android: '<p>Android</p>', ios: '<p>iOS</p>' }} />);

      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });
    });

    it('accepts event handler props', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;
      const mockOnSave = jest.fn();
      const mockOnContentChange = jest.fn();

      render(
        <HTMLEditorLazy
          onSave={mockOnSave}
          onContentChange={mockOnContentChange}
        />
      );

      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });
    });

    it('accepts configuration props', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      render(
        <HTMLEditorLazy
          className="custom-editor"
          readOnly
          showFullscreenButton={false}
          autoSave={false}
          autoSaveInterval={60000}
        />
      );

      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });
    });
  });

  describe('Default Props', () => {
    it('has correct default variant', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.defaultProps.variant).toBe('email');
    });

    it('has correct default initialContent', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.defaultProps.initialContent).toBe(null);
    });

    it('has correct default event handlers', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.defaultProps.onSave).toBe(null);
      expect(HTMLEditorLazy.defaultProps.onContentChange).toBe(null);
    });

    it('has correct default configuration', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.defaultProps.className).toBe('');
      expect(HTMLEditorLazy.defaultProps.readOnly).toBe(false);
      expect(HTMLEditorLazy.defaultProps.showFullscreenButton).toBe(true);
      expect(HTMLEditorLazy.defaultProps.autoSave).toBe(true);
      expect(HTMLEditorLazy.defaultProps.autoSaveInterval).toBe(30000);
    });
  });

  describe('PropTypes', () => {
    it('defines variant propType', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.propTypes.variant).toBeDefined();
    });

    it('defines layoutType propType', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.propTypes.layoutType).toBeDefined();
    });

    it('defines initialContent propType', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.propTypes.initialContent).toBeDefined();
    });

    it('defines event handler propTypes', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.propTypes.onSave).toBeDefined();
      expect(HTMLEditorLazy.propTypes.onContentChange).toBeDefined();
    });

    it('defines configuration propTypes', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(HTMLEditorLazy.propTypes.className).toBeDefined();
      expect(HTMLEditorLazy.propTypes.readOnly).toBeDefined();
      expect(HTMLEditorLazy.propTypes.showFullscreenButton).toBeDefined();
      expect(HTMLEditorLazy.propTypes.autoSave).toBeDefined();
      expect(HTMLEditorLazy.propTypes.autoSaveInterval).toBeDefined();
    });
  });

  describe('preloadHTMLEditor', () => {
    it('is exported as a function', async () => {
      const { preloadHTMLEditor } = await import('../index.lazy');

      expect(typeof preloadHTMLEditor).toBe('function');
    });

    it('can be called without errors', async () => {
      const { preloadHTMLEditor } = await import('../index.lazy');

      expect(() => {
        preloadHTMLEditor();
      }).not.toThrow();
    });

    it('handles import failures gracefully', async () => {
      const { preloadHTMLEditor } = await import('../index.lazy');

      // Should not throw even if import fails (preloadHTMLEditor catches errors internally)
      expect(() => {
        preloadHTMLEditor();
      }).not.toThrow();
    });

    it('returns undefined', async () => {
      const { preloadHTMLEditor } = await import('../index.lazy');

      const result = preloadHTMLEditor();
      expect(result).toBeUndefined();
    });
  });

  describe('isHTMLEditorLoaded', () => {
    it('is exported as a function', async () => {
      const { isHTMLEditorLoaded } = await import('../index.lazy');

      expect(typeof isHTMLEditorLoaded).toBe('function');
    });

    it('returns false when window.__htmlEditorLoaded is not set', async () => {
      const { isHTMLEditorLoaded } = await import('../index.lazy');

      // The function returns undefined when window.__htmlEditorLoaded is not set
      expect(isHTMLEditorLoaded()).toBeFalsy();
    });

    it('returns true when window.__htmlEditorLoaded is set', async () => {
      const { isHTMLEditorLoaded } = await import('../index.lazy');

      if (typeof window !== 'undefined') {
        window.__htmlEditorLoaded = true;
      }

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

    it('handles non-browser environment', async () => {
      const { isHTMLEditorLoaded } = await import('../index.lazy');

      // In Node.js test environment with jsdom, window is defined
      // The function should return a value (true or falsy)
      const result = isHTMLEditorLoaded();
      expect([true, false, undefined]).toContain(result);
    });

    it('handles different window states', async () => {
      const { isHTMLEditorLoaded } = await import('../index.lazy');

      // Should not throw regardless of window state
      expect(() => {
        isHTMLEditorLoaded();
      }).not.toThrow();
    });
  });

  describe('Module Exports', () => {
    it('exports HTMLEditorLazy as default', async () => {
      const module = await import('../index.lazy');

      expect(module.default).toBeDefined();
      expect(typeof module.default).toBe('function');
    });

    it('exports HTMLEditorFallback as named export', async () => {
      const { HTMLEditorFallback } = await import('../index.lazy');

      expect(HTMLEditorFallback).toBeDefined();
      expect(typeof HTMLEditorFallback).toBe('function');
    });

    it('exports preloadHTMLEditor as named export', async () => {
      const { preloadHTMLEditor } = await import('../index.lazy');

      expect(preloadHTMLEditor).toBeDefined();
      expect(typeof preloadHTMLEditor).toBe('function');
    });

    it('exports isHTMLEditorLoaded as named export', async () => {
      const { isHTMLEditorLoaded } = await import('../index.lazy');

      expect(isHTMLEditorLoaded).toBeDefined();
      expect(typeof isHTMLEditorLoaded).toBe('function');
    });
  });

  describe('Integration Tests', () => {
    it('renders complete lazy loading flow', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      const { container } = render(<HTMLEditorLazy variant="email" />);

      // Container should exist
      expect(container.firstChild).toBeInTheDocument();

      // Wait for actual component to load
      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });
    });

    it('can preload and then render', async () => {
      const { default: HTMLEditorLazy, preloadHTMLEditor } = await import('../index.lazy');

      // Preload
      preloadHTMLEditor();

      // Then render
      render(<HTMLEditorLazy variant="email" />);

      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });
    });

    it('handles multiple instances', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      render(
        <>
          <HTMLEditorLazy variant="email" />
          <HTMLEditorLazy variant="inapp" />
        </>
      );

      await waitFor(() => {
        const editors = screen.getAllByTestId('html-editor');
        expect(editors).toHaveLength(2);
      });
    });

    it('fallback and main component work together', async () => {
      const { default: HTMLEditorLazy, HTMLEditorFallback } = await import('../index.lazy');

      // Render fallback independently
      const { unmount } = render(<HTMLEditorFallback message="Custom loading" />);
      expect(screen.getByText('Custom loading')).toBeInTheDocument();
      unmount();

      // Render lazy component
      render(<HTMLEditorLazy variant="email" />);

      await waitFor(() => {
        expect(screen.getByTestId('html-editor')).toBeInTheDocument();
      });
    });
  });

  describe('Edge Cases', () => {
    it('handles undefined props gracefully', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(() => {
        render(<HTMLEditorLazy variant={undefined} />);
      }).not.toThrow();
    });

    it('handles null props gracefully', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      expect(() => {
        render(<HTMLEditorLazy onSave={null} />);
      }).not.toThrow();
    });

    it('fallback handles empty message', async () => {
      const { HTMLEditorFallback } = await import('../index.lazy');

      render(<HTMLEditorFallback message="" />);

      expect(screen.getByTestId('cap-spin')).toBeInTheDocument();
    });

    it('fallback handles null message', async () => {
      const { HTMLEditorFallback } = await import('../index.lazy');

      render(<HTMLEditorFallback message={null} />);

      // Should render with default message
      expect(screen.getByTestId('cap-spin')).toBeInTheDocument();
    });

    it('handles rapid mount/unmount', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      const { unmount } = render(<HTMLEditorLazy variant="email" />);

      // Unmount immediately
      unmount();

      // Should not throw
      expect(true).toBe(true);
    });
  });

  describe('Performance and Code Splitting', () => {
    it('lazy component uses React.lazy', async () => {
      const HTMLEditorLazy = (await import('../index.lazy')).default;

      // The component should be wrapped in Suspense
      const { container } = render(<HTMLEditorLazy variant="email" />);

      expect(container.firstChild).toBeInTheDocument();
    });

    it('does not immediately load the actual editor', async () => {
      // This test verifies that import() is used for lazy loading
      const module = await import('../index.lazy');

      // The default export should be a wrapper component, not the actual editor
      expect(module.default.displayName).toBe('HTMLEditorLazy');
    });
  });
});
