import React, { act } from 'react';
import { mountWithIntl, shallowWithIntl } from '../../../../../helpers/intl-enzym-test-helpers';
import CapButton from '@capillarytech/cap-ui-library/CapButton';
import ButtonList from '../ButtonList';
import ButtonItem from '../ButtonItem';
import messages from '../../messages';

// Mock ButtonItem component
jest.mock('../ButtonItem', () => {
  return function MockButtonItem({
    button,
    index,
    onEdit,
    onDelete,
    onDragStart,
    onDragOver,
    onDrop,
    onDragEnd,
    disabled,
  }) {
    return (
      <div
        className="mock-button-item"
        data-testid={`button-item-${index}`}
        data-disabled={disabled}
        draggable={!disabled}
        onDragStart={(e) => {
          if (!disabled) {
            e.dataTransfer = { effectAllowed: 'move', setData: jest.fn() };
            onDragStart(index);
          }
        }}
        onDragOver={(e) => {
          if (!disabled) {
            e.preventDefault();
            e.dataTransfer = { dropEffect: 'move' };
            onDragOver(index);
          }
        }}
        onDrop={(e) => {
          if (!disabled) {
            e.preventDefault();
            onDrop(index);
          }
        }}
        onDragEnd={() => {
          if (!disabled) {
            onDragEnd();
          }
        }}
      >
        <span className="button-text">{button.text}</span>
        <span className="button-url">{button.url}</span>
        <span className="button-type">{button.type}</span>
        <button
          className="edit-button"
          onClick={() => !disabled && onEdit(index)}
          disabled={disabled}
        >
          Edit
        </button>
        <button
          className="delete-button"
          onClick={() => !disabled && onDelete(index)}
          disabled={disabled}
        >
          Delete
        </button>
      </div>
    );
  };
});

describe('ButtonList', () => {
  const mockOnEdit = jest.fn();
  const mockOnDelete = jest.fn();
  const mockOnReorder = jest.fn();
  const mockOnAddPrimary = jest.fn();
  const mockOnAddSecondary = jest.fn();
  const mockRenderInlineForm = jest.fn(() => <div className="inline-form">Inline Form</div>);

  const mockButtons = [
    { text: 'Primary Button', url: 'https://example.com', type: 'primary' },
    { text: 'Secondary Button', url: 'https://example2.com', type: 'secondary' },
  ];

  const defaultProps = {
    buttons: mockButtons,
    onEdit: mockOnEdit,
    onDelete: mockOnDelete,
    onReorder: mockOnReorder,
    onAddPrimary: mockOnAddPrimary,
    onAddSecondary: mockOnAddSecondary,
    showAddPrimary: true,
    showAddSecondary: true,
    disabled: false,
    disableSecondaryButton: false,
    isInlineFormVisible: false,
    inlineFormIndex: null,
    renderInlineForm: null,
  };

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

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

    it('should return null when buttons array is empty', () => {
      const wrapper = shallowWithIntl(
        <ButtonList {...defaultProps} buttons={[]} />
      );
      expect(wrapper.isEmptyRender()).toBe(true);
    });

    it('should render all buttons', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      const buttonItems = wrapper.find(ButtonItem);
      expect(buttonItems).toHaveLength(2);
    });

    it('should render button items with correct props', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      const firstButtonItem = wrapper.find(ButtonItem).first();
      
      expect(firstButtonItem.prop('button')).toEqual(mockButtons[0]);
      expect(firstButtonItem.prop('index')).toBe(0);
      expect(firstButtonItem.prop('onEdit')).toBe(mockOnEdit);
      expect(firstButtonItem.prop('onDelete')).toBe(mockOnDelete);
      expect(firstButtonItem.prop('disabled')).toBe(false);
    });
  });

  describe('Add Primary Button', () => {
    it('should render add primary button when showAddPrimary is true', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} showAddPrimary={true} />);
      const addPrimaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-primary-button button-add-trigger'
      );
      
      expect(addPrimaryButton.exists()).toBe(true);
    });

    it('should not render add primary button when showAddPrimary is false', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} showAddPrimary={false} />);
      const addPrimaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-primary-button button-add-trigger'
      );
      
      expect(addPrimaryButton.exists()).toBe(false);
    });

    it('should call onAddPrimary when add primary button is clicked', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} showAddPrimary={true} />);
      const addPrimaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-primary-button button-add-trigger'
      ).first();
      
      addPrimaryButton.prop('onClick')();
      
      expect(mockOnAddPrimary).toHaveBeenCalledTimes(1);
    });

    it('should disable add primary button when disabled prop is true', () => {
      const wrapper = mountWithIntl(
        <ButtonList {...defaultProps} showAddPrimary={true} disabled={true} />
      );
      const addPrimaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-primary-button button-add-trigger'
      ).first();
      
      expect(addPrimaryButton.prop('disabled')).toBe(true);
    });

    it('should not call onAddPrimary when button is disabled and clicked', () => {
      const wrapper = mountWithIntl(
        <ButtonList {...defaultProps} showAddPrimary={true} disabled={true} />
      );
      const addPrimaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-primary-button button-add-trigger'
      ).first();
      
      // Even if we try to call onClick, it should be disabled
      expect(addPrimaryButton.prop('disabled')).toBe(true);
    });
  });

  describe('Add Secondary Button', () => {
    it('should render add secondary button when showAddSecondary is true', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} showAddSecondary={true} />);
      const addSecondaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-secondary-button button-add-trigger'
      );
      
      expect(addSecondaryButton.exists()).toBe(true);
    });

    it('should not render add secondary button when showAddSecondary is false', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} showAddSecondary={false} />);
      const addSecondaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-secondary-button button-add-trigger'
      );
      
      expect(addSecondaryButton.exists()).toBe(false);
    });

    it('should call onAddSecondary when add secondary button is clicked', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} showAddSecondary={true} />);
      const addSecondaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-secondary-button button-add-trigger'
      ).first();
      
      addSecondaryButton.prop('onClick')();
      
      expect(mockOnAddSecondary).toHaveBeenCalledTimes(1);
    });

    it('should disable add secondary button when disabled prop is true', () => {
      const wrapper = mountWithIntl(
        <ButtonList {...defaultProps} showAddSecondary={true} disabled={true} />
      );
      const addSecondaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-secondary-button button-add-trigger'
      ).first();
      
      expect(addSecondaryButton.prop('disabled')).toBe(true);
    });

    it('should disable add secondary button when disableSecondaryButton prop is true', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          showAddSecondary={true}
          disableSecondaryButton={true}
        />
      );
      const addSecondaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-secondary-button button-add-trigger'
      ).first();
      
      expect(addSecondaryButton.prop('disabled')).toBe(true);
    });

    it('should disable add secondary button when both disabled and disableSecondaryButton are true', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          showAddSecondary={true}
          disabled={true}
          disableSecondaryButton={true}
        />
      );
      const addSecondaryButton = wrapper.find(CapButton).findWhere(
        (node) => node.prop('className') === 'add-secondary-button button-add-trigger'
      ).first();
      
      expect(addSecondaryButton.prop('disabled')).toBe(true);
    });
  });

  describe('Edit Button', () => {
    it('should call onEdit with correct index when edit is clicked on primary button', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      const firstButtonItem = wrapper.find(ButtonItem).first();
      
      firstButtonItem.prop('onEdit')(0);
      
      expect(mockOnEdit).toHaveBeenCalledWith(0);
      expect(mockOnEdit).toHaveBeenCalledTimes(1);
    });

    it('should call onEdit with correct index when edit is clicked on secondary button', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      const secondButtonItem = wrapper.find(ButtonItem).at(1);
      
      secondButtonItem.prop('onEdit')(1);
      
      expect(mockOnEdit).toHaveBeenCalledWith(1);
      expect(mockOnEdit).toHaveBeenCalledTimes(1);
    });

    it('should not call onEdit when button item is disabled', () => {
      const wrapper = mountWithIntl(
        <ButtonList {...defaultProps} disabled={true} />
      );
      const firstButtonItem = wrapper.find(ButtonItem).first();
      
      // ButtonItem should be disabled, so onEdit should not be called
      expect(firstButtonItem.prop('disabled')).toBe(true);
    });
  });

  describe('Delete Button', () => {
    it('should call onDelete with correct index when delete is clicked', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      const firstButtonItem = wrapper.find(ButtonItem).first();
      
      firstButtonItem.prop('onDelete')(0);
      
      expect(mockOnDelete).toHaveBeenCalledWith(0);
      expect(mockOnDelete).toHaveBeenCalledTimes(1);
    });

    it('should not call onDelete when button item is disabled', () => {
      const wrapper = mountWithIntl(
        <ButtonList {...defaultProps} disabled={true} />
      );
      const firstButtonItem = wrapper.find(ButtonItem).first();
      
      // ButtonItem should be disabled, so onDelete should not be called
      expect(firstButtonItem.prop('disabled')).toBe(true);
    });
  });

  describe('Reorder Buttons (Drag and Drop)', () => {
    it('should set draggedIndex on drag start', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      
      // Simulate drag start - call with index directly
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragStart')(0);
      });
      wrapper.update();
      
      // Get fresh references after state update
      act(() => {
        wrapper.find(ButtonItem).at(1).prop('onDragOver')(1);
      });
      wrapper.update();
      
      expect(mockOnReorder).toHaveBeenCalledWith(0, 1);
    });

    it('should call onReorder when dragging over a different button', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      
      // Start drag on first button - call with index directly
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragStart')(0);
      });
      wrapper.update();
      
      // Get fresh references after state update and drag over second button
      act(() => {
        wrapper.find(ButtonItem).at(1).prop('onDragOver')(1);
      });
      wrapper.update();
      
      expect(mockOnReorder).toHaveBeenCalledWith(0, 1);
    });

    it('should not call onReorder when dragging over the same button', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      
      // Start drag on first button - call with index directly
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragStart')(0);
      });
      wrapper.update();
      
      // Drag over the same button - call with index directly
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragOver')(0);
      });
      wrapper.update();
      
      expect(mockOnReorder).not.toHaveBeenCalled();
    });

    it('should not call onReorder when component is disabled', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} disabled={true} />);
      
      // Try to start drag (should be prevented) - call with index directly
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragStart')(0);
      });
      wrapper.update();
      
      // Try to drag over (should be prevented) - call with index directly
      act(() => {
        wrapper.find(ButtonItem).at(1).prop('onDragOver')(1);
      });
      wrapper.update();
      
      expect(mockOnReorder).not.toHaveBeenCalled();
    });

    it('should reset draggedIndex on drag end', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      
      // Start drag - call with index directly
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragStart')(0);
      });
      wrapper.update();
      
      // End drag
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragEnd')();
      });
      wrapper.update();
      
      // Try to drag over after drag end (should not call onReorder) - call with index directly
      mockOnReorder.mockClear();
      act(() => {
        wrapper.find(ButtonItem).at(1).prop('onDragOver')(1);
      });
      wrapper.update();
      
      expect(mockOnReorder).not.toHaveBeenCalled();
    });

    it('should handle multiple reorder operations', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      
      // First reorder: move button 0 to position 1 - call with index directly
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragStart')(0);
      });
      wrapper.update();
      
      act(() => {
        wrapper.find(ButtonItem).at(1).prop('onDragOver')(1);
      });
      wrapper.update();
      
      expect(mockOnReorder).toHaveBeenCalledWith(0, 1);
      
      // End drag
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragEnd')();
      });
      wrapper.update();
      
      // Second reorder: move button 1 to position 0 - call with index directly
      mockOnReorder.mockClear();
      act(() => {
        wrapper.find(ButtonItem).at(1).prop('onDragStart')(1);
      });
      wrapper.update();
      
      act(() => {
        wrapper.find(ButtonItem).first().prop('onDragOver')(0);
      });
      wrapper.update();
      
      expect(mockOnReorder).toHaveBeenCalledWith(1, 0);
    });
  });

  describe('Disabled States', () => {
    it('should disable all button items when disabled prop is true', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} disabled={true} />);
      const buttonItems = wrapper.find(ButtonItem);
      
      buttonItems.forEach((item) => {
        expect(item.prop('disabled')).toBe(true);
      });
    });

    it('should disable button items that are not being edited when inline form is visible', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          isInlineFormVisible={true}
          inlineFormIndex={0}
        />
      );
      const buttonItems = wrapper.find(ButtonItem);
      
      // First button (index 0) should not be disabled (being edited)
      expect(buttonItems.at(0).prop('disabled')).toBe(false);
      // Second button (index 1) should be disabled (not being edited)
      expect(buttonItems.at(1).prop('disabled')).toBe(true);
    });

    it('should disable button items when both disabled and inline form are active', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          disabled={true}
          isInlineFormVisible={true}
          inlineFormIndex={0}
        />
      );
      const buttonItems = wrapper.find(ButtonItem);
      
      // All buttons should be disabled when disabled prop is true
      buttonItems.forEach((item) => {
        expect(item.prop('disabled')).toBe(true);
      });
    });

    it('should not disable buttons when inline form is not visible', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          isInlineFormVisible={false}
          inlineFormIndex={null}
        />
      );
      const buttonItems = wrapper.find(ButtonItem);
      
      buttonItems.forEach((item) => {
        expect(item.prop('disabled')).toBe(false);
      });
    });
  });

  describe('Inline Form Rendering', () => {
    it('should render inline form when isInlineFormVisible is true and inlineFormIndex is set', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          isInlineFormVisible={true}
          inlineFormIndex={0}
          renderInlineForm={mockRenderInlineForm}
        />
      );
      
      const inlineForm = wrapper.find('.button-inline-form');
      expect(inlineForm.exists()).toBe(true);
      expect(mockRenderInlineForm).toHaveBeenCalled();
    });

    it('should not render inline form when isInlineFormVisible is false', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          isInlineFormVisible={false}
          inlineFormIndex={0}
          renderInlineForm={mockRenderInlineForm}
        />
      );
      
      const inlineForm = wrapper.find('.button-inline-form');
      expect(inlineForm.exists()).toBe(false);
    });

    it('should not render inline form when inlineFormIndex is null', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          isInlineFormVisible={true}
          inlineFormIndex={null}
          renderInlineForm={mockRenderInlineForm}
        />
      );
      
      const inlineForm = wrapper.find('.button-inline-form');
      expect(inlineForm.exists()).toBe(false);
    });

    it('should not render inline form when inlineFormIndex is negative', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          isInlineFormVisible={true}
          inlineFormIndex={-1}
          renderInlineForm={mockRenderInlineForm}
        />
      );
      
      const inlineForm = wrapper.find('.button-inline-form');
      expect(inlineForm.exists()).toBe(false);
    });

    it('should not render inline form when renderInlineForm is not a function', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          isInlineFormVisible={true}
          inlineFormIndex={0}
          renderInlineForm={null}
        />
      );
      
      const inlineForm = wrapper.find('.button-inline-form');
      expect(inlineForm.exists()).toBe(false);
    });

    it('should render inline form at the correct index', () => {
      const wrapper = mountWithIntl(
        <ButtonList
          {...defaultProps}
          isInlineFormVisible={true}
          inlineFormIndex={1}
          renderInlineForm={mockRenderInlineForm}
        />
      );
      
      const inlineForm = wrapper.find('.button-inline-form');
      expect(inlineForm.exists()).toBe(true);
      
      // The inline form should be rendered instead of the button at index 1
      const buttonItems = wrapper.find(ButtonItem);
      // We should still have button items, but the form should be in the list
      expect(inlineForm.exists()).toBe(true);
    });
  });

  describe('Button Item Props', () => {
    it('should pass correct button data to ButtonItem', () => {
      const customButtons = [
        { text: 'Custom Primary', url: 'https://custom1.com', type: 'primary' },
        { text: 'Custom Secondary', url: 'https://custom2.com', type: 'secondary' },
      ];
      const wrapper = mountWithIntl(
        <ButtonList {...defaultProps} buttons={customButtons} />
      );
      
      const firstButtonItem = wrapper.find(ButtonItem).first();
      expect(firstButtonItem.prop('button')).toEqual(customButtons[0]);
      
      const secondButtonItem = wrapper.find(ButtonItem).at(1);
      expect(secondButtonItem.prop('button')).toEqual(customButtons[1]);
    });

    it('should pass correct index to each ButtonItem', () => {
      const wrapper = mountWithIntl(<ButtonList {...defaultProps} />);
      const buttonItems = wrapper.find(ButtonItem);
      
      buttonItems.forEach((item, index) => {
        expect(item.prop('index')).toBe(index);
      });
    });
  });
});

