import { fireEvent, render, waitFor } from '@testing-library/react-native';
import {
  DeviceEventEmitter,
  LayoutChangeEvent,
  Platform,
  Text,
  View,
} from 'react-native';
import WebView from 'react-native-webview';
import Logger from '../../core/logging/logger';
import { CSWebView } from '../../webview/CSWebView.android';
import { injectWebView, removeWebViewInjection } from '../../webview/webView';

jest.mock('../../core/logging/logger', () => ({
  error: jest.fn(),
  warn: jest.fn(),
}));

jest.mock('../../webview/webView', () => ({
  injectWebView: jest.fn(),
  removeWebViewInjection: jest.fn(),
}));

jest.mock('react-native-webview', () => ({
  __esModule: true,
  default: 'WebView',
}));

describe('CSWebView', () => {
  beforeEach(() => {
    Platform.OS = 'android';
    jest.clearAllMocks();
  });

  afterEach(() => {
    jest.restoreAllMocks();
    jest.restoreAllMocks();
  });

  describe('Implementation 2: Child WebView', () => {
    it('should render a child webview with the correct props', () => {
      const mockOnLayout = jest.fn();

      const { getByTestId } = render(
        <CSWebView>
          <View onLayout={mockOnLayout} testID="child-webview" />
        </CSWebView>
      );

      const childWebView = getByTestId('child-webview');
      expect(childWebView).toBeTruthy();
    });

    it('should log an error if more than one child is passed', () => {
      render(
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-ignore - Testing with multiple children
        <CSWebView>
          <View />
          <View />
        </CSWebView>
      );

      expect(Logger.error).toHaveBeenCalledWith(
        'CSWebView component expects exactly 1 child, but received multiple. Ensure that only a single child element is passed'
      );
    });

    it('should inject webview on layout', () => {
      const mockEvent = {
        nativeEvent: { target: 123 },
      } as unknown as LayoutChangeEvent;

      const { getByTestId } = render(
        <CSWebView>
          <View testID="child-webview" />
        </CSWebView>
      );

      const childWebView = getByTestId('child-webview');
      fireEvent(childWebView, 'layout', mockEvent);

      expect(injectWebView).toHaveBeenCalledWith(123);
    });

    it('should call onLayout prop from children', () => {
      const mockOnLayout = jest.fn();

      const { getByTestId } = render(
        <CSWebView>
          <View onLayout={mockOnLayout} testID="child-webview" />
        </CSWebView>
      );

      const childWebView = getByTestId('child-webview');
      fireEvent(childWebView, 'layout', {
        nativeEvent: { layout: { width: 100, height: 100 } },
      });

      expect(mockOnLayout).toHaveBeenCalled();
    });

    it('should set applicationNameForUserAgent to "CS_WebView" when no original applicationNameForUserAgent is provided', () => {
      const { getByTestId } = render(
        <CSWebView>
          <WebView testID="webview" />
        </CSWebView>
      );

      const webView = getByTestId('webview');
      expect(webView.props.applicationNameForUserAgent).toContain('CS_WebView');
    });

    it('should append "CS_WebView" to existing userAgent', () => {
      const originalUserAgent = 'Mozilla/5.0 (Linux; Android 10; SM-G975F)';

      const { getByTestId } = render(
        <CSWebView>
          <WebView applicationNameForUserAgent={originalUserAgent} testID="webview" />
        </CSWebView>
      );

      const webView = getByTestId('webview');
      expect(webView.props.applicationNameForUserAgent).toContain(`${originalUserAgent} CS_WebView`);
    });

    it('should work with empty string applicationNameForUserAgent', () => {
      const { getByTestId } = render(
        <CSWebView>
          <WebView applicationNameForUserAgent="" testID="webview" />
        </CSWebView>
      );

      const webView = getByTestId('webview');
      expect(webView.props.applicationNameForUserAgent).toContain('CS_WebView');
    });

    it('should work with undefined userAgent', () => {
      const { getByTestId } = render(
        <CSWebView>
          <WebView applicationNameForUserAgent={undefined} testID="webview" />
        </CSWebView>
      );

      const webView = getByTestId('webview');
      expect(webView.props.applicationNameForUserAgent).toContain('CS_WebView');
    });
  });

  it('should remove onCSWebViewInjectedListener on unmount', () => {
    const mockRenderWebView = jest.fn((onLayout, webViewUrl) => (
      <View onLayout={onLayout} testID="webview">
        <Text>{webViewUrl}</Text>
      </View>
    ));

    const removeListener = jest.fn();
    jest.spyOn(DeviceEventEmitter, 'addListener').mockImplementation(() => ({
      remove: removeListener,
    }));

    const { unmount } = render(
      <CSWebView renderWebView={mockRenderWebView} url="https://example.com" />
    );

    unmount();
    expect(removeListener).toHaveBeenCalled();
  });

  it('should call removeWebViewInjection on unmount', async () => {
    const { getByTestId, unmount } = render(
      <CSWebView>
        <View testID="child-webview" />
      </CSWebView>
    );

    const childWebView = getByTestId('child-webview');
    fireEvent(childWebView, 'layout', {
      nativeEvent: { layout: { width: 100, height: 100 }, target: 123 },
    });

    await waitFor(() => expect(injectWebView).toHaveBeenCalledWith(123));
    DeviceEventEmitter.emit('onCSWebViewInjected');
    unmount();
    await waitFor(() => expect(removeWebViewInjection).toHaveBeenCalled());
  });
});
