/**
 * useInAppWrapper Hook Tests
 *
 * Tests for the useInAppWrapper custom hook that manages InAppWrapper component business logic
 */

import React from 'react';
import { render, screen, fireEvent, act } from '@testing-library/react';
import '@testing-library/jest-dom';
import useInAppWrapper from '../useInAppWrapper';
import { STEPS, INAPP_EDITOR_TYPES } from '../../constants';

// Test wrapper component
const TestComponent = ({ hookProps, onStateChange }) => {
  const hookState = useInAppWrapper(hookProps);

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

  return (
    <div>
      <div data-testid="template-name">{hookState.templateName}</div>
      <div data-testid="is-template-name-empty">{String(hookState.isTemplateNameEmpty)}</div>
      <div data-testid="selected-editor-type">{hookState.selectedEditorType}</div>
      <div data-testid="is-show-inapp-create">{String(hookState.isShowInAppCreate)}</div>
      <div data-testid="modes-count">{hookState.modes?.length || 0}</div>
      
      <input
        data-testid="template-name-input"
        onChange={hookState.onTemplateNameChange}
        value={hookState.templateName}
      />
      <button
        data-testid="editor-type-button"
        onClick={(e) => {
          e.target.value = INAPP_EDITOR_TYPES.HTML_EDITOR;
          hookState.onChange(e);
        }}
      >
        Change Editor Type
      </button>
      <button
        data-testid="editor-type-selection-button"
        onClick={hookState.handleEditorTypeSelection}
      >
        Select Editor Type
      </button>
    </div>
  );
};

describe('useInAppWrapper', () => {
  const defaultHookProps = {
    intl: {
      formatMessage: jest.fn((msg) => msg.defaultMessage || msg.id || 'formatted'),
    },
    onInAppEditorTypeChange: jest.fn(),
    inAppEditorType: '',
    step: STEPS.MODE_SELECTION,
    showNextStep: jest.fn(),
    onResetStep: jest.fn(),
    onEnterTemplateName: jest.fn(),
    onRemoveTemplateName: jest.fn(),
    setIsLoadingContent: jest.fn(),
    isGetFormData: false,
    getFormdata: jest.fn(),
    type: null,
    isFullMode: true,
    cap: {},
    showTemplateName: jest.fn(),
    showLiquidErrorInFooter: jest.fn(),
    onValidationFail: jest.fn(),
    forwardedTags: [],
    selectedOfferDetails: null,
    onPreviewContentClicked: jest.fn(),
    onTestContentClicked: jest.fn(),
    eventContextTags: {},
    onCreateComplete: jest.fn(),
    handleClose: jest.fn(),
    templateData: null,
    getDefaultTags: null,
  };

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

  describe('Initial State', () => {
    it('initializes with default state', () => {
      render(<TestComponent hookProps={defaultHookProps} />);

      expect(screen.getByTestId('template-name')).toHaveTextContent('');
      expect(screen.getByTestId('is-template-name-empty')).toHaveTextContent('true');
      expect(screen.getByTestId('selected-editor-type')).toHaveTextContent('');
      expect(screen.getByTestId('is-show-inapp-create')).toHaveTextContent('false');
      expect(screen.getByTestId('modes-count')).toHaveTextContent('2');
    });

    it('initializes modes with correct structure', () => {
      let capturedState = null;
      render(
        <TestComponent
          hookProps={defaultHookProps}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      expect(capturedState.modes).toHaveLength(2);
      expect(capturedState.modes[0].value).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
      expect(capturedState.modes[1].value).toBe(INAPP_EDITOR_TYPES.DRAG_DROP_EDITOR);
    });
  });

  describe('Template Name Management', () => {
    it('updates template name when input changes', () => {
      render(<TestComponent hookProps={defaultHookProps} />);

      const input = screen.getByTestId('template-name-input');
      fireEvent.change(input, { target: { value: 'Test Template' } });

      expect(screen.getByTestId('template-name')).toHaveTextContent('Test Template');
      expect(screen.getByTestId('is-template-name-empty')).toHaveTextContent('false');
    });

    it('calls onEnterTemplateName when template name is entered', () => {
      const onEnterTemplateName = jest.fn();
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            onEnterTemplateName,
          }}
        />
      );

      const input = screen.getByTestId('template-name-input');
      fireEvent.change(input, { target: { value: 'Test Template' } });

      expect(onEnterTemplateName).toHaveBeenCalled();
    });

    it('calls onRemoveTemplateName when template name is cleared', () => {
      const onRemoveTemplateName = jest.fn();
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            onRemoveTemplateName,
          }}
        />
      );

      const input = screen.getByTestId('template-name-input');
      fireEvent.change(input, { target: { value: 'Test' } });
      fireEvent.change(input, { target: { value: '' } });

      expect(onRemoveTemplateName).toHaveBeenCalled();
    });

    it('handles template name with only whitespace', () => {
      render(<TestComponent hookProps={defaultHookProps} />);

      const input = screen.getByTestId('template-name-input');
      fireEvent.change(input, { target: { value: '   ' } });

      expect(screen.getByTestId('is-template-name-empty')).toHaveTextContent('true');
    });
  });

  describe('Editor Type Selection', () => {
    it('calls onInAppEditorTypeChange when editor type changes', () => {
      const onInAppEditorTypeChange = jest.fn();
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            onInAppEditorTypeChange,
          }}
        />
      );

      const button = screen.getByTestId('editor-type-button');
      fireEvent.click(button);

      expect(onInAppEditorTypeChange).toHaveBeenCalled();
    });

    it('handles editor type selection when template name is not empty', () => {
      const showNextStep = jest.fn();
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
            showNextStep,
          }}
        />
      );

      // First set template name
      const input = screen.getByTestId('template-name-input');
      fireEvent.change(input, { target: { value: 'Test Template' } });

      // Then select editor type
      const button = screen.getByTestId('editor-type-selection-button');
      fireEvent.click(button);

      expect(showNextStep).toHaveBeenCalled();
    });

    it('does not call showNextStep when template name is empty', () => {
      const showNextStep = jest.fn();
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
            showNextStep,
          }}
        />
      );

      const button = screen.getByTestId('editor-type-selection-button');
      fireEvent.click(button);

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

    it('does not call showNextStep when editor type is not selected', () => {
      const showNextStep = jest.fn();
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            inAppEditorType: '',
            showNextStep,
          }}
        />
      );

      const input = screen.getByTestId('template-name-input');
      fireEvent.change(input, { target: { value: 'Test Template' } });

      const button = screen.getByTestId('editor-type-selection-button');
      fireEvent.click(button);

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

  describe('Step Management', () => {
    it('sets selectedEditorType when step is CREATE_TEMPLATE_CONTENT and editor type is set', () => {
      let capturedState = null;
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            step: STEPS.CREATE_TEMPLATE_CONTENT,
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      expect(capturedState.selectedEditorType).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
    });

    it('does not set selectedEditorType when step is MODE_SELECTION', () => {
      let capturedState = null;
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            step: STEPS.MODE_SELECTION,
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      expect(capturedState.selectedEditorType).toBe('');
    });

    it('skips setting selectedEditorType if already set', () => {
      let capturedState = null;
      const { rerender } = render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            step: STEPS.CREATE_TEMPLATE_CONTENT,
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      const initialType = capturedState.selectedEditorType;

      // Change step but keep editor type
      rerender(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            step: STEPS.MODE_SELECTION,
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      // Should remain the same since it was already set
      expect(capturedState.selectedEditorType).toBe(initialType);
    });

    it('handles default case in step switch statement', () => {
      let capturedState = null;
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            step: 'UNKNOWN_STEP', // Use an unknown step to trigger default case
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      // Should not set selectedEditorType for unknown step
      expect(capturedState.selectedEditorType).toBe('');
    });
  });

  describe('Cleanup', () => {
    it('calls onResetStep on unmount', () => {
      const onResetStep = jest.fn();
      const { unmount } = render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            onResetStep,
          }}
        />
      );

      unmount();

      expect(onResetStep).toHaveBeenCalled();
    });
  });

  describe('InApp Props Generation', () => {
    it('generates correct inAppProps with all required fields', () => {
      let capturedState = null;
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            templateName: 'Test Template',
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      const input = screen.getByTestId('template-name-input');
      fireEvent.change(input, { target: { value: 'Test Template' } });

      expect(capturedState.inAppProps).toBeDefined();
      expect(capturedState.inAppProps.defaultData['template-name']).toBe('Test Template');
      expect(capturedState.inAppProps.defaultData['editor-type']).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
      expect(capturedState.inAppProps.key).toBe('inapp-create-template');
      expect(capturedState.inAppProps.route.name).toBe('inapp');
    });

    it('uses getDefaultTags from type prop when available', () => {
      let capturedState = null;
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            type: 'customType',
            getDefaultTags: 'defaultTags',
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      expect(capturedState.inAppProps.getDefaultTags).toBe('customType');
    });

    it('uses getDefaultTags prop when type is not available', () => {
      let capturedState = null;
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            type: null,
            getDefaultTags: 'defaultTags',
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      expect(capturedState.inAppProps.getDefaultTags).toBe('defaultTags');
    });

  });

  describe('isShowInAppCreate', () => {
    it('returns truthy value when selectedEditorType and inAppEditorType are set', () => {
      let capturedState = null;
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            step: STEPS.CREATE_TEMPLATE_CONTENT,
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      // isShowInAppCreate = !isEmpty(selectedEditorType) && inAppEditorType
      // When both are truthy, it returns inAppEditorType (the string value)
      // After useEffect runs, selectedEditorType should be set
      expect(capturedState.selectedEditorType).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
      // isShowInAppCreate returns the inAppEditorType value when both conditions are true
      expect(capturedState.isShowInAppCreate).toBeTruthy();
      expect(capturedState.isShowInAppCreate).toBe(INAPP_EDITOR_TYPES.HTML_EDITOR);
    });

    it('returns false when selectedEditorType is not set', () => {
      let capturedState = null;
      render(
        <TestComponent
          hookProps={{
            ...defaultHookProps,
            step: STEPS.MODE_SELECTION,
            inAppEditorType: INAPP_EDITOR_TYPES.HTML_EDITOR,
          }}
          onStateChange={(state) => {
            capturedState = state;
          }}
        />
      );

      expect(capturedState.isShowInAppCreate).toBe(false);
    });
  });
});

