import React from 'react';
import { mountWithIntl, shallowWithIntl } from '../../../../../helpers/intl-enzym-test-helpers';
import CapIcon from '@capillarytech/cap-ui-library/CapIcon';
import CapRow from '@capillarytech/cap-ui-library/CapRow';
import CapColumn from '@capillarytech/cap-ui-library/CapColumn';
import ButtonItem from '../ButtonItem';

describe('ButtonItem', () => {
  const mockButton = {
    text: 'Click Me',
    url: 'https://example.com',
    type: 'primary',
  };

  const mockOnEdit = jest.fn();
  const mockOnDelete = jest.fn();
  const mockOnDragStart = jest.fn();
  const mockOnDragOver = jest.fn();
  const mockOnDrop = jest.fn();
  const mockOnDragEnd = jest.fn();

  const defaultProps = {
    button: mockButton,
    index: 0,
    onEdit: mockOnEdit,
    onDelete: mockOnDelete,
    onDragStart: mockOnDragStart,
    onDragOver: mockOnDragOver,
    onDrop: mockOnDrop,
    onDragEnd: mockOnDragEnd,
    disabled: false,
  };

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

  describe('Rendering', () => {
    it('should render correctly with default props', () => {
      const wrapper = shallowWithIntl(<ButtonItem {...defaultProps} />);
      expect(wrapper).toMatchSnapshot();
    });

    it('should render button text', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const textElement = wrapper.find('.button-item-text');
      expect(textElement.exists()).toBe(true);
      expect(textElement.text()).toBe(mockButton.text);
    });

    it('should render button URL', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const urlElement = wrapper.find('.button-item-url');
      expect(urlElement.exists()).toBe(true);
      expect(urlElement.text()).toBe(mockButton.url);
    });

    it('should render drag icon', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const dragIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'drag');
      expect(dragIcon.exists()).toBe(true);
      expect(dragIcon.prop('className')).toBe('drag-icon');
    });

    it('should render link icon', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const linkIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'link').first();
      expect(linkIcon.exists()).toBe(true);
      expect(linkIcon.prop('className')).toBe('link-icon');
    });

    it('should render edit icon', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const editIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'edit');
      expect(editIcon.exists()).toBe(true);
      expect(editIcon.prop('className')).toBe('action-icon');
    });

    it('should render delete icon', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const deleteIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'delete');
      expect(deleteIcon.exists()).toBe(true);
      expect(deleteIcon.prop('className')).toBe('action-icon delete-icon');
    });

    it('should render CapRow with correct props', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const capRow = wrapper.find(CapRow).first();
      expect(capRow.exists()).toBe(true);
      expect(capRow.prop('align')).toBe('middle');
      expect(capRow.prop('className')).toBe('button-item-content');
    });

    it('should render CapColumns with correct spans', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const columns = wrapper.find(CapColumn);
      
      expect(columns.at(0).prop('span')).toBe(1);
      expect(columns.at(0).prop('className')).toBe('button-item-drag-handle');
      
      expect(columns.at(1).prop('span')).toBe(1);
      expect(columns.at(1).prop('className')).toBe('button-item-icon');
      
      expect(columns.at(2).prop('span')).toBe(14);
      expect(columns.at(2).prop('className')).toBe('button-item-info');
      
      expect(columns.at(3).prop('span')).toBe(8);
      expect(columns.at(3).prop('className')).toBe('button-item-actions');
    });

    it('should render with different button data', () => {
      const customButton = {
        text: 'Custom Button',
        url: 'https://custom.com',
        type: 'secondary',
      };
      const wrapper = mountWithIntl(
        <ButtonItem {...defaultProps} button={customButton} />
      );
      
      expect(wrapper.find('.button-item-text').text()).toBe(customButton.text);
      expect(wrapper.find('.button-item-url').text()).toBe(customButton.url);
    });
  });

  describe('Disabled State', () => {
    it('should add disabled class when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      expect(container.hasClass('disabled')).toBe(true);
    });

    it('should not add disabled class when disabled is false', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={false} />);
      const container = wrapper.find('.webpush-button-item');
      expect(container.hasClass('disabled')).toBe(false);
    });

    it('should not add disabled class when disabled prop is not provided (defaults to false)', () => {
      const { disabled, ...propsWithoutDisabled } = defaultProps;
      const wrapper = mountWithIntl(<ButtonItem {...propsWithoutDisabled} />);
      const container = wrapper.find('.webpush-button-item');
      expect(container.hasClass('disabled')).toBe(false);
    });

    it('should set draggable to false when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      expect(container.prop('draggable')).toBe(false);
    });

    it('should set draggable to true when disabled is false', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={false} />);
      const container = wrapper.find('.webpush-button-item');
      expect(container.prop('draggable')).toBe(true);
    });

    it('should set draggable to true when disabled prop is not provided', () => {
      const { disabled, ...propsWithoutDisabled } = defaultProps;
      const wrapper = mountWithIntl(<ButtonItem {...propsWithoutDisabled} />);
      const container = wrapper.find('.webpush-button-item');
      expect(container.prop('draggable')).toBe(true);
    });
  });

  describe('Drag and Drop - handleDragStart', () => {
    it('should call onDragStart with correct index when drag starts', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} index={2} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        dataTransfer: {
          effectAllowed: '',
          setData: jest.fn(),
        },
        currentTarget: container.instance(),
      };
      
      container.simulate('dragStart', mockEvent);
      
      expect(mockOnDragStart).toHaveBeenCalledWith(2);
      expect(mockOnDragStart).toHaveBeenCalledTimes(1);
    });

    it('should set dataTransfer.effectAllowed to "move" on drag start', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        dataTransfer: {
          effectAllowed: '',
          setData: jest.fn(),
        },
        currentTarget: container.instance(),
      };
      
      container.simulate('dragStart', mockEvent);
      
      expect(mockEvent.dataTransfer.effectAllowed).toBe('move');
    });

    it('should call setData with correct parameters on drag start', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        dataTransfer: {
          effectAllowed: '',
          setData: jest.fn(),
        },
        currentTarget: container.instance(),
      };
      
      container.simulate('dragStart', mockEvent);
      
      expect(mockEvent.dataTransfer.setData).toHaveBeenCalledWith('text/html', mockEvent.currentTarget);
    });

    it('should not call onDragStart when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        dataTransfer: {
          effectAllowed: '',
          setData: jest.fn(),
        },
        currentTarget: container.instance(),
      };
      
      container.simulate('dragStart', mockEvent);
      
      expect(mockOnDragStart).not.toHaveBeenCalled();
    });

    it('should not modify dataTransfer when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        dataTransfer: {
          effectAllowed: '',
          setData: jest.fn(),
        },
        currentTarget: container.instance(),
      };
      
      const originalEffectAllowed = mockEvent.dataTransfer.effectAllowed;
      container.simulate('dragStart', mockEvent);
      
      expect(mockEvent.dataTransfer.effectAllowed).toBe(originalEffectAllowed);
      expect(mockEvent.dataTransfer.setData).not.toHaveBeenCalled();
    });
  });

  describe('Drag and Drop - handleDragOver', () => {
    it('should call onDragOver with correct index when dragging over', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} index={3} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        dataTransfer: {
          dropEffect: '',
        },
      };
      
      container.simulate('dragOver', mockEvent);
      
      expect(mockOnDragOver).toHaveBeenCalledWith(3);
      expect(mockOnDragOver).toHaveBeenCalledTimes(1);
    });

    it('should call preventDefault on drag over', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        dataTransfer: {
          dropEffect: '',
        },
      };
      
      container.simulate('dragOver', mockEvent);
      
      expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1);
    });

    it('should set dataTransfer.dropEffect to "move" on drag over', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        dataTransfer: {
          dropEffect: '',
        },
      };
      
      container.simulate('dragOver', mockEvent);
      
      expect(mockEvent.dataTransfer.dropEffect).toBe('move');
    });

    it('should not call onDragOver when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        dataTransfer: {
          dropEffect: '',
        },
      };
      
      container.simulate('dragOver', mockEvent);
      
      expect(mockOnDragOver).not.toHaveBeenCalled();
    });

    it('should not call preventDefault when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        dataTransfer: {
          dropEffect: '',
        },
      };
      
      container.simulate('dragOver', mockEvent);
      
      expect(mockEvent.preventDefault).not.toHaveBeenCalled();
    });
  });

  describe('Drag and Drop - handleDrop', () => {
    it('should call onDrop with correct index when drop occurs', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} index={1} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        stopPropagation: jest.fn(),
      };
      
      container.simulate('drop', mockEvent);
      
      expect(mockOnDrop).toHaveBeenCalledWith(1);
      expect(mockOnDrop).toHaveBeenCalledTimes(1);
    });

    it('should call preventDefault on drop', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        stopPropagation: jest.fn(),
      };
      
      container.simulate('drop', mockEvent);
      
      expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1);
    });

    it('should call stopPropagation on drop', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        stopPropagation: jest.fn(),
      };
      
      container.simulate('drop', mockEvent);
      
      expect(mockEvent.stopPropagation).toHaveBeenCalledTimes(1);
    });

    it('should not call onDrop when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        stopPropagation: jest.fn(),
      };
      
      container.simulate('drop', mockEvent);
      
      expect(mockOnDrop).not.toHaveBeenCalled();
    });

    it('should not call preventDefault or stopPropagation when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockEvent = {
        preventDefault: jest.fn(),
        stopPropagation: jest.fn(),
      };
      
      container.simulate('drop', mockEvent);
      
      expect(mockEvent.preventDefault).not.toHaveBeenCalled();
      expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
    });
  });

  describe('Drag and Drop - onDragEnd', () => {
    it('should call onDragEnd when drag ends', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const container = wrapper.find('.webpush-button-item');
      
      container.simulate('dragEnd');
      
      expect(mockOnDragEnd).toHaveBeenCalledTimes(1);
    });

    it('should call onDragEnd even when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      
      container.simulate('dragEnd');
      
      expect(mockOnDragEnd).toHaveBeenCalledTimes(1);
    });
  });

  describe('Edit Functionality', () => {
    it('should call onEdit with correct index when edit icon is clicked', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} index={2} />);
      const editIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'edit').first();
      
      editIcon.prop('onClick')();
      
      expect(mockOnEdit).toHaveBeenCalledWith(2);
      expect(mockOnEdit).toHaveBeenCalledTimes(1);
    });

    it('should not call onEdit when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const editIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'edit').first();
      
      editIcon.prop('onClick')();
      
      expect(mockOnEdit).not.toHaveBeenCalled();
    });

    it('should call onEdit when disabled is false', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={false} />);
      const editIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'edit').first();
      
      editIcon.prop('onClick')();
      
      expect(mockOnEdit).toHaveBeenCalledWith(0);
    });
  });

  describe('Delete Functionality', () => {
    it('should call onDelete with correct index when delete icon is clicked', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} index={1} />);
      const deleteIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'delete').first();
      
      deleteIcon.prop('onClick')();
      
      expect(mockOnDelete).toHaveBeenCalledWith(1);
      expect(mockOnDelete).toHaveBeenCalledTimes(1);
    });

    it('should not call onDelete when disabled is true', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const deleteIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'delete').first();
      
      deleteIcon.prop('onClick')();
      
      expect(mockOnDelete).not.toHaveBeenCalled();
    });

    it('should call onDelete when disabled is false', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={false} />);
      const deleteIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'delete').first();
      
      deleteIcon.prop('onClick')();
      
      expect(mockOnDelete).toHaveBeenCalledWith(0);
    });
  });

  describe('Props Validation', () => {
    it('should handle different index values', () => {
      const indices = [0, 1, 5, 10];
      indices.forEach((index) => {
        const wrapper = mountWithIntl(<ButtonItem {...defaultProps} index={index} />);
        const editIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'edit').first();
        editIcon.prop('onClick')();
        expect(mockOnEdit).toHaveBeenCalledWith(index);
        mockOnEdit.mockClear();
      });
    });

    it('should handle empty button text', () => {
      const emptyButton = {
        text: '',
        url: 'https://example.com',
        type: 'primary',
      };
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} button={emptyButton} />);
      const textElement = wrapper.find('.button-item-text');
      expect(textElement.text()).toBe('');
    });

    it('should handle empty button URL', () => {
      const emptyUrlButton = {
        text: 'Button Text',
        url: '',
        type: 'primary',
      };
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} button={emptyUrlButton} />);
      const urlElement = wrapper.find('.button-item-url');
      expect(urlElement.text()).toBe('');
    });

    it('should handle long button text', () => {
      const longTextButton = {
        text: 'A'.repeat(100),
        url: 'https://example.com',
        type: 'primary',
      };
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} button={longTextButton} />);
      const textElement = wrapper.find('.button-item-text');
      expect(textElement.text()).toBe(longTextButton.text);
    });

    it('should handle long button URL', () => {
      const longUrlButton = {
        text: 'Button',
        url: 'https://example.com/' + 'a'.repeat(200),
        type: 'primary',
      };
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} button={longUrlButton} />);
      const urlElement = wrapper.find('.button-item-url');
      expect(urlElement.text()).toBe(longUrlButton.url);
    });
  });

  describe('Edge Cases', () => {
    it('should handle all drag events when disabled prevents all interactions', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} disabled={true} />);
      const container = wrapper.find('.webpush-button-item');
      
      const dragStartEvent = {
        dataTransfer: { effectAllowed: '', setData: jest.fn() },
        currentTarget: container.instance(),
      };
      const dragOverEvent = {
        preventDefault: jest.fn(),
        dataTransfer: { dropEffect: '' },
      };
      const dropEvent = {
        preventDefault: jest.fn(),
        stopPropagation: jest.fn(),
      };
      
      container.simulate('dragStart', dragStartEvent);
      container.simulate('dragOver', dragOverEvent);
      container.simulate('drop', dropEvent);
      
      expect(mockOnDragStart).not.toHaveBeenCalled();
      expect(mockOnDragOver).not.toHaveBeenCalled();
      expect(mockOnDrop).not.toHaveBeenCalled();
    });

    it('should handle rapid successive drag operations', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const container = wrapper.find('.webpush-button-item');
      
      const mockDragStartEvent = {
        dataTransfer: { effectAllowed: '', setData: jest.fn() },
        currentTarget: container.instance(),
      };
      
      // Simulate rapid drag starts
      container.simulate('dragStart', mockDragStartEvent);
      container.simulate('dragStart', mockDragStartEvent);
      container.simulate('dragStart', mockDragStartEvent);
      
      expect(mockOnDragStart).toHaveBeenCalledTimes(3);
    });

    it('should handle edit and delete clicks when not disabled', () => {
      const wrapper = mountWithIntl(<ButtonItem {...defaultProps} />);
      const editIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'edit').first();
      const deleteIcon = wrapper.find(CapIcon).findWhere((node) => node.prop('type') === 'delete').first();
      
      editIcon.prop('onClick')();
      deleteIcon.prop('onClick')();
      
      expect(mockOnEdit).toHaveBeenCalledTimes(1);
      expect(mockOnDelete).toHaveBeenCalledTimes(1);
    });
  });
});

