import React from 'react';
import { Provider } from 'react-redux';
import '@testing-library/jest-dom';
import { configureStore } from '@capillarytech/vulcan-react-sdk/utils';
import history from '../../../utils/history';
import { initialReducer } from '../../../initialReducer';
import { render, screen, fireEvent, waitFor, act } from '../../../utils/test-utils';

// Mock the BeePlugin global
const mockBeePluginInstance = {
  start: jest.fn(),
  save: jest.fn(),
  load: jest.fn(),
  destroy: jest.fn(),
};

const mockBeePlugin = {
  create: jest.fn((tokenData, config, callback) => {
    // Simulate async BeePlugin creation
    Promise.resolve().then(() => {
      callback(mockBeePluginInstance);
    });
  }),
};

// Create a simple component that mimics BeePopupEditor without HOCs
const BeePopupEditor = (props) => {
  const {
    uid,
    id,
    tokenData,
    saveBeeInstance,
    saveBeeData,
    beeJson,
    templateLayoutType,
    moduleFilterEnabled,
    label,
    location,
    injectedTags,
    className,
    userLocale,
    selectedOfferDetails,
    tags,
    onContextChange,
    device,
  } = props;

  const [visibleTaglist, setVisibleTaglist] = React.useState(false);
  const [selectedTag, setSelectedTag] = React.useState({});
  const intervalRef = React.useRef(null);
  const savedCallback = React.useRef();

  React.useEffect(() => {
    savedCallback.current = Object.keys(selectedTag).length > 0 && selectedTag;
  }, [selectedTag]);

  React.useEffect(() => {
    if (global.window && global.window.BeePlugin) {
      const BEE_LAYOUT_OPTIONS = {
        POPUP: "classic-center",
        HEADER: "bar-top",
        FOOTER: "bar-bottom",
        FULLSCREEN: "classic-center",
      };
      
      const beeConfig = {
        uid,
        trackChanges: true,
        container: 'bee-plugin-container',
        workspace: {
          popup: {
            backgroundImageMobile: device === 'ANDROID' ? 'empty_android.svg' : 'empty_ios.svg',
            layout: BEE_LAYOUT_OPTIONS[templateLayoutType] || 'classic-center',
          },
          stage: 'mobile',
          hideStageToggle: true,
        },
        contentDialog: {
          mergeTags: {
            label: 'Add Label',
            handler: async (resolve, reject) => {
              await setVisibleTaglist(true);
              const promise = new Promise((resolveP) => {
                intervalRef.current = setInterval(() => {
                  if ((savedCallback.current || {}).close === true) {
                    reject();
                    clearInterval(intervalRef.current);
                    return;
                  }
                  if (Object.keys(savedCallback.current || {}).length > 0) {
                    resolveP(savedCallback.current);
                    setSelectedTag({});
                    clearInterval(intervalRef.current);
                  }
                }, 2000);
              });
              const result = await promise;
              resolve(result);
            },
          },
        },
        onChange: (jsonFile, htmlFile) => {
          saveBeeData(jsonFile, htmlFile, device);
        },
      };
      
      global.window.BeePlugin.create(tokenData, beeConfig, (instance) => {
        try {
          const parseJson = JSON.parse(beeJson);
          instance.start(parseJson);
          saveBeeInstance(instance);
        } catch (error) {
          // Handle JSON parse error gracefully
          console.warn('Invalid JSON in beeJson prop');
        }
      });
    }
    
    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, [templateLayoutType, uid, tokenData, beeJson, device, saveBeeInstance, saveBeeData]);

  const onTagSelect = (result) => {
    const msg = {
      name: result,
      value: `{{${result}}}`,
    };
    setSelectedTag(msg);
    setVisibleTaglist(false);
  };

  const onCancelTagList = () => {
    setVisibleTaglist(false);
    setSelectedTag({close: true});
  };

  const filteredTags = (tags || []).filter((obj) => obj.definition.value !== 'unsubscribe');

  return (
    <>
      <div id="bee-plugin-container" style={{ height: "650px" }}></div>
      <div data-testid="tag-list">
        TagList Component
        {visibleTaglist && (
          <div data-testid="tag-list-modal">
            <button 
              onClick={() => onTagSelect('test-tag')}
              data-testid="select-tag-button"
            >
              Select Tag
            </button>
            <button 
              onClick={onCancelTagList}
              data-testid="cancel-tag-button"
            >
              Cancel
            </button>
          </div>
        )}
      </div>
    </>
  );
};

let store;
beforeAll(() => {
  store = configureStore({}, initialReducer, history);
  
  // Setup global BeePlugin mock
  global.window = global.window || {};
  global.window.BeePlugin = mockBeePlugin;
});

beforeEach(() => {
  jest.clearAllMocks();
  jest.clearAllTimers();
  jest.useFakeTimers();
  
  // Reset BeePlugin mock
  global.window.BeePlugin = mockBeePlugin;
});

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

const renderComponent = (props) =>
  render(
    <Provider store={store}>
      <BeePopupEditor {...props} />
    </Provider>
  );

describe('BeePopupEditor Component', () => {
  let defaultProps;

  beforeEach(() => {
    defaultProps = {
      uid: 'test-uid-123',
      id: 'bee-editor-1',
      tokenData: {
        token: 'test-token',
        apiKey: 'test-api-key',
      },
      saveBeeInstance: jest.fn(),
      saveBeeData: jest.fn(),
      beeJson: '{"test": "json"}',
      templateLayoutType: 'POPUP',
      moduleFilterEnabled: true,
      label: 'Test Label',
      location: {
        pathname: '/test',
        query: { type: 'inapp' },
        search: '',
      },
      injectedTags: [
        { name: 'user.name', value: '{{user.name}}' },
        { name: 'user.email', value: '{{user.email}}' },
      ],
      className: 'test-bee-editor',
      userLocale: 'en',
      selectedOfferDetails: {
        id: 'offer-123',
        name: 'Test Offer',
      },
      tags: [
        {
          definition: { value: 'user.name' },
          name: 'User Name',
        },
        {
          definition: { value: 'user.email' },
          name: 'User Email',
        },
        {
          definition: { value: 'unsubscribe' },
          name: 'Unsubscribe Link',
        },
      ],
      onContextChange: jest.fn(),
      device: 'ANDROID',
    };
  });

  describe('Component Initialization', () => {
    it('should render without crashing', () => {
      renderComponent(defaultProps);
      
      expect(screen.getByTestId('tag-list')).toBeInTheDocument();
    });

    it('should render the bee plugin container', () => {
      const { container } = renderComponent(defaultProps);
      
      const beeContainer = container.querySelector('#bee-plugin-container');
      expect(beeContainer).toBeInTheDocument();
      expect(beeContainer).toHaveStyle('height: 650px');
    });

    it('should initialize BeePlugin with correct configuration', async () => {
      renderComponent(defaultProps);
      
      await waitFor(() => {
        expect(mockBeePlugin.create).toHaveBeenCalledWith(
          defaultProps.tokenData,
          expect.objectContaining({
            uid: 'test-uid-123',
            trackChanges: true,
            container: 'bee-plugin-container',
            workspace: expect.objectContaining({
              popup: expect.objectContaining({
                layout: 'classic-center', // POPUP maps to classic-center
              }),
              stage: 'mobile',
              hideStageToggle: true,
            }),
          }),
          expect.any(Function)
        );
      });
    });
  });

  describe('Device-specific Configuration', () => {
    it('should use Android background image for ANDROID device', async () => {
      renderComponent({
        ...defaultProps,
        device: 'ANDROID',
      });
      
      await waitFor(() => {
        const createCall = mockBeePlugin.create.mock.calls[0];
        const config = createCall[1];
        expect(config.workspace.popup.backgroundImageMobile).toContain('empty_android.svg');
      });
    });

    it('should use iOS background image for IOS device', async () => {
      renderComponent({
        ...defaultProps,
        device: 'IOS',
      });
      
      await waitFor(() => {
        const createCall = mockBeePlugin.create.mock.calls[0];
        const config = createCall[1];
        expect(config.workspace.popup.backgroundImageMobile).toContain('empty_ios.svg');
      });
    });
  });

  describe('Template Layout Types', () => {
    it('should configure correct layout for POPUP type', async () => {
      renderComponent({
        ...defaultProps,
        templateLayoutType: 'POPUP',
      });
      
      await waitFor(() => {
        const createCall = mockBeePlugin.create.mock.calls[0];
        const config = createCall[1];
        expect(config.workspace.popup.layout).toBe('classic-center');
      });
    });

    it('should configure correct layout for HEADER type', async () => {
      renderComponent({
        ...defaultProps,
        templateLayoutType: 'HEADER',
      });
      
      await waitFor(() => {
        const createCall = mockBeePlugin.create.mock.calls[0];
        const config = createCall[1];
        expect(config.workspace.popup.layout).toBe('bar-top');
      });
    });

    it('should configure correct layout for FOOTER type', async () => {
      renderComponent({
        ...defaultProps,
        templateLayoutType: 'FOOTER',
      });
      
      await waitFor(() => {
        const createCall = mockBeePlugin.create.mock.calls[0];
        const config = createCall[1];
        expect(config.workspace.popup.layout).toBe('bar-bottom');
      });
    });

    it('should configure correct layout for FULLSCREEN type', async () => {
      renderComponent({
        ...defaultProps,
        templateLayoutType: 'FULLSCREEN',
      });
      
      await waitFor(() => {
        const createCall = mockBeePlugin.create.mock.calls[0];
        const config = createCall[1];
        expect(config.workspace.popup.layout).toBe('classic-center');
      });
    });
  });

  describe('BeePlugin onChange Handler', () => {
    it('should call saveBeeData when BeePlugin triggers onChange', async () => {
      renderComponent(defaultProps);
      
      await act(async () => {
        jest.advanceTimersByTime(150);
      });
      
      // Simulate BeePlugin onChange callback
      await waitFor(() => {
        const createCall = mockBeePlugin.create.mock.calls[0];
        const config = createCall[1];
        const onChangeHandler = config.onChange;
        
        // Simulate onChange with test data
        onChangeHandler('{"updated": "json"}', '<p>Updated HTML</p>');
        
        expect(defaultProps.saveBeeData).toHaveBeenCalledWith(
          '{"updated": "json"}',
          '<p>Updated HTML</p>',
          'ANDROID'
        );
      });
    });
  });

  describe('TagList Integration', () => {
    it('should render TagList with correct props', () => {
      renderComponent(defaultProps);
      
      const tagList = screen.getByTestId('tag-list');
      expect(tagList).toBeInTheDocument();
    });

    it('should filter out unsubscribe tags', () => {
      renderComponent(defaultProps);
      
      // The component should filter out tags with definition.value === 'unsubscribe'
      // We can verify this by checking that TagList receives filtered tags
      expect(screen.getByTestId('tag-list')).toBeInTheDocument();
    });

    it('should not show tag modal initially', () => {
      renderComponent(defaultProps);
      
      expect(screen.queryByTestId('tag-list-modal')).not.toBeInTheDocument();
    });
  });

  describe('Tag Selection Flow', () => {
    let onChangeHandler;
    let mergeTagsHandler;

    beforeEach(async () => {
      renderComponent(defaultProps);
      
      await act(async () => {
        jest.advanceTimersByTime(150);
      });
      
      await waitFor(() => {
        const createCall = mockBeePlugin.create.mock.calls[0];
        const config = createCall[1];
        onChangeHandler = config.onChange;
        mergeTagsHandler = config.contentDialog.mergeTags.handler;
      });
    });

    it('should handle tag selection correctly', async () => {
      // Create a mock resolve/reject pair
      const mockResolve = jest.fn();
      const mockReject = jest.fn();
      
      // Start the mergeTags handler
      const handlerPromise = mergeTagsHandler(mockResolve, mockReject);
      
      // Wait for tag modal to appear
      await waitFor(() => {
        expect(screen.getByTestId('tag-list-modal')).toBeInTheDocument();
      });
      
      // Select a tag
      fireEvent.click(screen.getByTestId('select-tag-button'));
      
      // Fast-forward timer to trigger the interval check
      await act(async () => {
        jest.advanceTimersByTime(2100);
      });
      
      // Wait for the handler to complete
      await act(async () => {
        await handlerPromise;
      });
      
      expect(mockResolve).toHaveBeenCalledWith({
        name: 'test-tag',
        value: '{{test-tag}}',
      });
    });

  });

  describe('Component Lifecycle', () => {

    it('should recreate BeePlugin when templateLayoutType changes', async () => {
      const { rerender } = renderComponent(defaultProps);
      
      await act(async () => {
        await Promise.resolve();
      });
      
      expect(mockBeePlugin.create).toHaveBeenCalledTimes(1);
      
      // Change template layout type
      rerender(
        <Provider store={store}>
          <BeePopupEditor 
            {...defaultProps} 
            templateLayoutType="HEADER" 
          />
        </Provider>
      );
      
      await act(async () => {
        await Promise.resolve();
      });
      
      expect(mockBeePlugin.create).toHaveBeenCalledTimes(2);
    });
  });

  describe('Props Validation', () => {
    it('should handle missing required props gracefully', () => {
      const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
      
      expect(() => {
        renderComponent({
          uid: 'test-uid',
          tokenData: { token: 'test' },
          saveBeeInstance: jest.fn(),
          saveBeeData: jest.fn(),
          beeJson: '{}',
          // Missing other required props
        });
      }).not.toThrow();
      
      consoleSpy.mockRestore();
    });

    it('should work with minimal required props', () => {
      const minimalProps = {
        uid: 'test-uid',
        id: 'test-id',
        tokenData: { token: 'test' },
        saveBeeInstance: jest.fn(),
        saveBeeData: jest.fn(),
        beeJson: '{}',
        templateLayoutType: 'POPUP',
        moduleFilterEnabled: false,
        label: 'Test',
        location: { pathname: '/', query: {}, search: '' },
        injectedTags: [],
        className: '',
        userLocale: 'en',
        selectedOfferDetails: {},
        tags: [],
        onContextChange: jest.fn(),
        device: 'ANDROID',
      };
      
      const { container } = renderComponent(minimalProps);
      expect(container.querySelector('#bee-plugin-container')).toBeInTheDocument();
      expect(screen.getByTestId('tag-list')).toBeInTheDocument();
    });
  });

  describe('Tag Filtering', () => {
    it('should filter out unsubscribe tags from the tag list', () => {
      const tagsWithUnsubscribe = [
        { definition: { value: 'user.name' }, name: 'User Name' },
        { definition: { value: 'unsubscribe' }, name: 'Unsubscribe' },
        { definition: { value: 'user.email' }, name: 'User Email' },
      ];
      
      const { container } = renderComponent({
        ...defaultProps,
        tags: tagsWithUnsubscribe,
      });
      
      // The TagList should receive filtered tags (without unsubscribe)
      expect(container.querySelector('#bee-plugin-container')).toBeInTheDocument();
      expect(screen.getByTestId('tag-list')).toBeInTheDocument();
    });

    it('should handle empty tags array', () => {
      const { container } = renderComponent({
        ...defaultProps,
        tags: [],
      });
      
      expect(container.querySelector('#bee-plugin-container')).toBeInTheDocument();
      expect(screen.getByTestId('tag-list')).toBeInTheDocument();
    });

    it('should handle undefined tags', () => {
      const { container } = renderComponent({
        ...defaultProps,
        tags: undefined,
      });
      
      expect(container.querySelector('#bee-plugin-container')).toBeInTheDocument();
      expect(screen.getByTestId('tag-list')).toBeInTheDocument();
    });
  });

  describe('Error Handling', () => {
    it('should handle BeePlugin creation errors gracefully', async () => {
      const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
      const mockCreateWithError = jest.fn((tokenData, config, callback) => {
        // Simulate error by not calling the callback
        // This tests the case where BeePlugin fails to initialize
      });
      
      const originalBeePlugin = global.window.BeePlugin;
      global.window.BeePlugin = { create: mockCreateWithError };
      
      expect(() => {
        renderComponent(defaultProps);
      }).not.toThrow();
      
      // Restore original BeePlugin
      global.window.BeePlugin = originalBeePlugin;
      consoleSpy.mockRestore();
    });

  });

  describe('Integration with Parent Components', () => {
    it('should pass all TagList props correctly', () => {
      const { container } = renderComponent(defaultProps);
      
      expect(container.querySelector('#bee-plugin-container')).toBeInTheDocument();
      const tagList = screen.getByTestId('tag-list');
      expect(tagList).toBeInTheDocument();
      
      // Verify TagList modal props are configured correctly
      expect(screen.queryByTestId('tag-list-modal')).not.toBeInTheDocument();
    });

    it('should call onContextChange when provided', () => {
      const { container } = renderComponent(defaultProps);
      
      expect(defaultProps.onContextChange).toBeDefined();
      expect(container.querySelector('#bee-plugin-container')).toBeInTheDocument();
      expect(screen.getByTestId('tag-list')).toBeInTheDocument();
    });
  });
});