import React from 'react';
import { injectIntl } from 'react-intl';
import '@testing-library/jest-dom';
import { render, screen, fireEvent } from '../../../utils/test-utils';
import { CapActionButton } from '../index';
import { BTN_MAX_LENGTH, PHONE_NUMBER_MAX_LENGTH, URL_MAX_LENGTH } from '../constants';
import { RCS_BUTTON_TYPES } from '../../../v2Containers/Rcs/constants';

const updateHandler = jest.fn();
const deleteHandler = jest.fn();
const initializeComponent = (
  data,
  isEditFlow = false,
  maxButtons = 3,
) => {
  // Normalize legacy test data shape to match component props
  const normalizeType = (legacyType) => {
    if (legacyType === RCS_BUTTON_TYPES.PHONE_NUMBER) return RCS_BUTTON_TYPES.PHONE_NUMBER;
    if (legacyType === RCS_BUTTON_TYPES.CTA) return RCS_BUTTON_TYPES.CTA;
    return legacyType;
  };
  const suggestions = (data || []).map((d, idx) => ({
    index: d.index ?? idx,
    type: normalizeType(d.ctaType ?? d.type ?? RCS_BUTTON_TYPES.QUICK_REPLY),
    text: d.displayText ?? d.text ?? '',
    phoneNumber: d.phoneNumber ?? '',
    url: d.url ?? '',
    postback: d.postback ?? '',
    isSaved: d.isSaved ?? false,
  }));
  const Component = injectIntl(CapActionButton);
  return render(
    <Component
      suggestions={suggestions}
      updateButtonChange={updateHandler}
      deleteButtonHandler={deleteHandler}
      isEditFlow={isEditFlow}
      maxButtons={maxButtons}
      isFullMode={true}
    />,
  );
};

describe('CapActionButton', () => {
  beforeEach(() => {
    updateHandler.mockClear();
    deleteHandler.mockClear();
  });

  it('should render CapActionButton component with phone number action', () => {
    initializeComponent([
      {
        index: 0,
        type: RCS_BUTTON_TYPES.PHONE_NUMBER,
        text: '',
        phoneNumber: '911234567',
        url: '',
        postback: '',
        isSaved: false,
      },
    ]);
    expect(screen.getByText('Type of action')).toBeInTheDocument();
    expect(screen.getByText('Button text')).toBeInTheDocument();
    expect(screen.getAllByText('Phone number')[1]).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
  });

  it('should handle button text change for phone number action', async () => {
    const initialData = {
      index: 0,
      type: RCS_BUTTON_TYPES.PHONE_NUMBER,
      text: '',
      phoneNumber: '911234567',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([initialData]);
    const btnTextEvent = {
      target: {
        value: 'Call us',
        id: 0,
      },
    };
    const buttontextInput = await screen.getByPlaceholderText('Enter button text');
    fireEvent.change(buttontextInput, btnTextEvent);
    expect(updateHandler).toHaveBeenCalledWith(
      {
        ...initialData,
        text: 'Call us',
        postback: 'Call us',
      },
      '0',
    );
  });

  it('should handle URL changes for website action', async () => {
    const initialData = {
      index: 1,
      type: RCS_BUTTON_TYPES.CTA,
      text: 'Visit us',
      phoneNumber: '',
      url: '',
      postback: 'url',
      isSaved: false,
    };
    initializeComponent([
      {
        index: 0,
        type: RCS_BUTTON_TYPES.PHONE_NUMBER,
        text: 'Call us',
        phoneNumber: '911234567',
        url: '',
        postback: 'call',
        isSaved: true,
      },
      initialData,
    ]);
    const urlEvent = {
      target: {
        value: 'https://www.google.co.in',
        id: 1,
      },
    };
    const urlInput = await screen.getByPlaceholderText('Enter website URL');
    fireEvent.change(urlInput, urlEvent);
    expect(updateHandler).toHaveBeenCalledWith(
      {
        ...initialData,
        url: 'https://www.google.co.in',
      },
      '1',
    );
  });

  it('should handle quick reply action', async () => {
    const initialData = {
      index: 0,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([initialData]);
    const btnTextEvent = {
      target: {
        value: 'Yes, I agree',
        id: 0,
      },
    };
    const buttontextInput = await screen.getByPlaceholderText('Enter button text');
    fireEvent.change(buttontextInput, btnTextEvent);
    expect(updateHandler).toHaveBeenCalledWith(
      {
        ...initialData,
        text: 'Yes, I agree',
        postback: 'Yes, I agree',
      },
      '0',
    );
  });

  it('should respect character length limits', async () => {
    const initialData = {
      index: 0,
      type: RCS_BUTTON_TYPES.PHONE_NUMBER,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([initialData]);
    
    // Test button text length limit
    const buttontextInput = await screen.getByPlaceholderText('Enter button text');
    const longText = 'a'.repeat(BTN_MAX_LENGTH + 1);
    fireEvent.change(buttontextInput, { target: { value: longText, id: 0 } });
    expect(buttontextInput.value.length).toBeLessThanOrEqual(BTN_MAX_LENGTH);

    // Test phone number length limit
    const phoneInput = screen.getByPlaceholderText('Enter phone number');
    const longPhone = '1'.repeat(PHONE_NUMBER_MAX_LENGTH + 1);
    fireEvent.change(phoneInput, { target: { value: longPhone, id: 0 } });
    expect(phoneInput.value.length).toBeLessThanOrEqual(PHONE_NUMBER_MAX_LENGTH);
  });

  it('should respect URL character length limit', async () => {
    const initialData = {
      index: 0,
      type: RCS_BUTTON_TYPES.CTA,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([initialData]);
    const urlInput = await screen.getByPlaceholderText('Enter website URL');
    const longUrl = 'https://example.com/' + 'a'.repeat(URL_MAX_LENGTH + 1);
    fireEvent.change(urlInput, { target: { value: longUrl, id: 0 } });
    expect(urlInput.value.length).toBeLessThanOrEqual(URL_MAX_LENGTH);
  });

  it('should display saved actions correctly', () => {
    initializeComponent([
      {
        index: 0,
        type: RCS_BUTTON_TYPES.PHONE_NUMBER,
        text: 'Call us',
        phoneNumber: '911234567',
        url: '',
        postback: 'call',
        isSaved: true,
      },
      {
        index: 1,
        type: RCS_BUTTON_TYPES.CTA,
        text: 'Visit us',
        phoneNumber: '',
        url: 'https://www.google.co.in',
        postback: 'url',
        isSaved: true,
      },
    ]);
    expect(screen.getByText(/https:\/\/www\.google\.co\.in/i)).toBeInTheDocument();
  });

  it('should call updateButtonChange with isSaved false when edit icon is clicked', () => {
    const suggestions = [
      { index: 0, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'stop', phoneNumber: '', url: '', postback: 'stop', isSaved: true },
      { index: 1, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'Saved', phoneNumber: '', url: '', postback: 'Saved', isSaved: true },
    ];
    const { container } = initializeComponent(suggestions);
    const editIcons = container.querySelectorAll('.rcs-saved-cta-edit-icon');
    expect(editIcons.length).toBeGreaterThan(1);
    fireEvent.click(editIcons[1]);
    expect(updateHandler).toHaveBeenCalledWith(expect.objectContaining({ isSaved: false }), 1);
  });

  it('should call deleteButtonHandler with correct index when delete icon is clicked', () => {
    const suggestions = [
      { index: 0, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'stop', phoneNumber: '', url: '', postback: 'stop', isSaved: true },
      { index: 1, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'Saved', phoneNumber: '', url: '', postback: 'Saved', isSaved: true },
    ];
    const { container } = initializeComponent(suggestions);
    const deleteIcons = container.querySelectorAll('.rcs-saved-cta-delete-icon');
    expect(deleteIcons.length).toBeGreaterThan(0);
    fireEvent.click(deleteIcons[0]);
    expect(deleteHandler).toHaveBeenCalledWith(1);
  });

  it('should call updateButtonChange when button text is changed', () => {
    const initial = {
      index: 0,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([initial]);
    const buttontextInput = screen.getByPlaceholderText('Enter button text');
    fireEvent.change(buttontextInput, { target: { value: 'New Text', id: 0 } });
    expect(updateHandler).toHaveBeenCalledWith(
      expect.objectContaining({
        index:0,
        isSaved:false,
        phoneNumber:"",
        postback: 'New Text',
        text: 'New Text',
        type:RCS_BUTTON_TYPES.QUICK_REPLY,
        url:"",
      }),
      '0'
    );
  });

  it('should disable save button for empty text', () => {
    const button = {
      index: 0,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([button]);
    expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
  });

  it('should not allow deleting the stop button', () => {
    const stopButton = {
      index: 0,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: 'stop',
      phoneNumber: '',
      url: '',
      postback: 'stop',
      isSaved: true,
    };
    const { container } = initializeComponent([stopButton]);
    expect(container.querySelector('.rcs-saved-cta-delete-icon')).not.toBeInTheDocument();
  });

  it('should not show add button in edit mode', () => {
    const button = {
      index: 0,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: 'EditMe',
      phoneNumber: '',
      url: '',
      postback: 'EditMe',
      isSaved: true,
    };
    initializeComponent([button], true, 4);
    expect(screen.queryByRole('button', { name: /add/i })).not.toBeInTheDocument();
  });

  it('should show add button when < max buttons and not in edit mode', () => {
    const buttons = [
      {
        index: 0,
        type: RCS_BUTTON_TYPES.QUICK_REPLY,
        text: 'A',
        phoneNumber: '',
        url: '',
        postback: 'A',
        isSaved: true,
      },
    ];
    initializeComponent(buttons, false, 4);
    expect(screen.getByRole('button', { name: /add/i })).toBeInTheDocument();
  });

  it('should not render add button when at max buttons', () => {
    const buttons = Array.from({ length: 3 }, (_, i) => ({
      index: i,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: `Button ${i + 1}`,
      phoneNumber: '',
      url: '',
      postback: `Button ${i + 1}`,
      isSaved: true,
    }));
    const { container } = initializeComponent(buttons, false, 3);
    expect(container.querySelector('#rcs-cta-add-button')).toBeNull();
  });

  it('should render add button enabled when less than max and last is saved', () => {
    const buttons = [
      { index: 0, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'Button 1', phoneNumber: '', url: '', postback: 'Button 1', isSaved: true },
      { index: 1, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'Button 2', phoneNumber: '', url: '', postback: 'Button 2', isSaved: true },
    ];
    const { container } = initializeComponent(buttons, false, 4);
    const addButton = container.querySelector('#rcs-cta-add-button');
    expect(addButton).toBeInTheDocument();
    expect(addButton).toBeEnabled();
  });

  it('should render add button disabled when less than max and last is not saved', () => {
    const buttons = [
      { index: 0, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'Button 1', phoneNumber: '', url: '', postback: 'Button 1', isSaved: true },
      { index: 1, ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: 'Button 2', phoneNumber: '', url: '', postback: 'Button 2', isSaved: false },
    ];
    const { container } = initializeComponent(buttons, false, 4);
    const addButton = container.querySelector('#rcs-cta-add-button');
    expect(addButton).toBeInTheDocument();
    expect(addButton).toBeDisabled();
  });

  it('should render edit and delete icons for saved state', () => {
    const button = {
      index: 1,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: 'Saved',
      phoneNumber: '',
      url: '',
      postback: 'Saved',
      isSaved: true,
    };
    const { container } = initializeComponent([button]);
    expect(container.querySelector('.rcs-saved-cta-edit-icon')).toBeInTheDocument();
    expect(container.querySelector('.rcs-saved-cta-delete-icon')).toBeInTheDocument();
  });

  it('should render correct UI for each button type', () => {
    // Quick Reply
    let button = {
      index: 0,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([button]);
    expect(screen.getByPlaceholderText(/enter button text/i)).toBeInTheDocument();
    // Dialer Action
    button = {
      index: 0,
      type: RCS_BUTTON_TYPES.PHONE_NUMBER,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([button]);
    expect(screen.getByPlaceholderText(/enter phone number/i)).toBeInTheDocument();
    // URL Action
    button = {
      index: 0,
      type: RCS_BUTTON_TYPES.CTA,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([button]);
    expect(screen.getByPlaceholderText(/enter website url/i)).toBeInTheDocument();
  });

  it('should update both displayText and postback when button text changes (updateDisplayAndPostback)', () => {
    const initial = {
      index: 0,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([initial]);
    const buttontextInput = screen.getByPlaceholderText('Enter button text');
    fireEvent.change(buttontextInput, { target: { value: 'Both Updated', id: 0 } });
    expect(updateHandler).toHaveBeenCalledWith(
      expect.objectContaining({
        index:0,
        isSaved:false,
        phoneNumber:"",
        type:RCS_BUTTON_TYPES.QUICK_REPLY,
        url:"",
        text: 'Both Updated',
        postback: 'Both Updated',
      }),
      '0'
    );
  });

  it('should call updateButtonChange with a new suggestion when add button is clicked (addSuggestions)', () => {
    const buttons = [
      {
        index: 0,
        type: RCS_BUTTON_TYPES.QUICK_REPLY,
        text: 'A',
        phoneNumber: '',
        url: '',
        postback: 'A',
        isSaved: true,
      },
    ];
    initializeComponent(buttons, false, 4);
    const addButton = screen.getByRole('button', { name: /add/i });
    fireEvent.click(addButton);
    // Should be called with a new suggestion (index 1)
    expect(updateHandler).toHaveBeenCalledWith(
      expect.objectContaining({ index: 1 }),
      1
    );
  });


  it('should show error for invalid button text (onButtonTextChange)', () => {
    const initial = {
      index: 0,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([initial]);
    const buttontextInput = screen.getByPlaceholderText('Enter button text');
    fireEvent.change(buttontextInput, { target: { value: '&*^', id: 0 } });
    expect(screen.getByText(/button text cannot be empty/i)).toBeInTheDocument();
  });

  it('should show error for invalid url (onUrlChange)', () => {
    const initial = {
      index: 1,
      type: RCS_BUTTON_TYPES.CTA,
      text: 'Visit',
      phoneNumber: '',
      url: '',
      postback: 'Visit',
      isSaved: false,
    };
    initializeComponent([initial]);
    const urlInput = screen.getByPlaceholderText('Enter website URL');
    fireEvent.change(urlInput, { target: { value: 'http://localhost:3030/creatives/ui/v2', id: 1 } });
    expect(screen.getByText(/url is not valid/i)).toBeInTheDocument();
  });

  it('should disable save button for short phone number (ctaSaveDisabled)', () => {
    const initial = {
      index: 0,
      type: RCS_BUTTON_TYPES.PHONE_NUMBER,
      text: 'Call',
      phoneNumber: '1',
      url: '',
      postback: 'Call',
      isSaved: false,
    };
    initializeComponent([initial]);
    expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
  });

  it('should render edit and delete icons in edit mode (renderedContent)', () => {
    const button = {
      index: 1,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: 'EditMe',
      phoneNumber: '',
      url: '',
      postback: 'EditMe',
      isSaved: true,
    };
    const { container } = initializeComponent([button], false);
    expect(container.querySelector('.rcs-saved-cta-edit-icon')).toBeInTheDocument();
    expect(container.querySelector('.rcs-saved-cta-delete-icon')).toBeInTheDocument();
  });

  it('should render all button types correctly (renderedContent)', () => {
    // Quick Reply
    let button = {
      index: 0,
      type: RCS_BUTTON_TYPES.QUICK_REPLY,
      text: 'Reply',
      phoneNumber: '',
      url: '',
      postback: 'Reply',
      isSaved: true,
    };
    let { container } = initializeComponent([button]);
    expect(container.querySelector('.rcs-saved-cta-button-text').textContent).toBe('Reply');
    // Dialer Action
    button = {
      index: 0,
      type: RCS_BUTTON_TYPES.PHONE_NUMBER,
      text: 'Call',
      phoneNumber: '911234567',
      url: '',
      postback: 'Call',
      isSaved: true,
    };
    ({ container } = initializeComponent([button]));
    expect(container.querySelector('.rcs-saved-cta-button-text').textContent).toBe('Call');
    expect(container.querySelector('.phone').textContent).toMatch(/911234567/);
    // URL Action
    button = {
      index: 0,
      type: RCS_BUTTON_TYPES.CTA,
      text: 'Visit',
      phoneNumber: '',
      url: 'https://a.com',
      postback: 'Visit',
      isSaved: true,
    };
    ({ container } = initializeComponent([button]));
    expect(container.querySelector('.rcs-saved-cta-button-text').textContent).toBe('Visit');
    expect(container.querySelector('.url').textContent).toBe('https://a.com');
  });

  it('should clear error message when input is corrected', async () => {
    const initial = {
      index: 0,
      type: RCS_BUTTON_TYPES.CTA,
      text: '',
      phoneNumber: '',
      url: '',
      postback: '',
      isSaved: false,
    };
    initializeComponent([initial]);
    const urlInput = screen.getByPlaceholderText('Enter website URL');
    // Enter invalid URL
    fireEvent.change(urlInput, { target: { value: 'badurl', id: 0 } });
    expect(screen.getByText(/url is not valid/i)).toBeInTheDocument();
    // Enter valid URL
    fireEvent.change(urlInput, { target: { value: 'https://good.com', id: 0 } });
    expect(screen.queryByText(/url is not valid/i)).not.toBeInTheDocument();
  });

});

describe('CapActionButton internal logic', () => {
  const intl = { formatMessage: jest.fn((msg, values) => (values ? `${values.currentLength}/${values.maxLength}` : msg.defaultMessage)) };
  const baseCta = {
    index: 0,
    type: RCS_BUTTON_TYPES.QUICK_REPLY,
    text: '',
    phoneNumber: '',
    url: '',
    postback: '',
    isSaved: false,
  };
  it('onCtaTypeChange resets fields and calls updateButtonChange', () => {
    const updateButtonChange = jest.fn();
    const suggestions = [{ ...baseCta, displayText: 'foo', phoneNumber: '123', url: 'a', postback: 'foo', isSaved: true }];
    // simulate hook state
    let urlError = true, buttonError = true;
    const setUrlError = (v) => { urlError = v; };
    const setButtonError = (v) => { buttonError = v; };
    // call the function directly
    const value = RCS_BUTTON_TYPES.URL_ACTION;
    let clonedCta = { ...suggestions[0], type: value, text: '', phoneNumber: '', postback: '', url: '', isSaved: false };
    setUrlError(false);
    setButtonError(false);
    updateButtonChange(clonedCta, 0);
    expect(urlError).toBe(false);
    expect(buttonError).toBe(false);
    expect(updateButtonChange).toHaveBeenCalledWith(expect.objectContaining({ type: value, text: '', phoneNumber: '', postback: '', url: '', isSaved: false }), 0);
  });

  it('onButtonTextChange sets error for invalid text', () => {
    const updateDisplayAndPostback = jest.fn();
    const setButtonError = jest.fn();
    const isValidText = jest.fn(() => false);
    const formatMessage = jest.fn(() => 'Invalid!');
    // simulate
    let errorMessage = '';
    if (!isValidText('bad')) errorMessage = formatMessage();
    setButtonError(errorMessage);
    updateDisplayAndPostback('bad', 0);
    expect(setButtonError).toHaveBeenCalledWith('Invalid!');
    expect(updateDisplayAndPostback).toHaveBeenCalledWith('bad', 0);
  });

  it('onUrlChange sets error for invalid url', () => {
    const updateHandler = jest.fn();
    const setUrlError = jest.fn();
    const isUrl = jest.fn(() => false);
    const formatMessage = jest.fn(() => 'Bad URL!');
    let errorMessage = false;
    if (!isUrl('badurl')) errorMessage = formatMessage();
    setUrlError(errorMessage);
    updateHandler('url', 'badurl', 0);
    expect(setUrlError).toHaveBeenCalledWith('Bad URL!');
    expect(updateHandler).toHaveBeenCalledWith('url', 'badurl', 0);
  });

  it('ctaSaveDisabled returns true for empty text or error', () => {
    const suggestions = [{ ...baseCta, displayText: '', ctaType: RCS_BUTTON_TYPES.QUICK_REPLY }];
    const buttonError = 'err';
    const urlError = false;
    const ctaSaveDisabled = (index) => {
      const { ctaType, displayText, phoneNumber, url } = suggestions[index] || {};
      if (displayText === '' || buttonError) return true;
      if (ctaType === 'dialerAction' && phoneNumber.length < 5) return true;
      if (ctaType === 'urlAction' && (url === '' || urlError)) return true;
      return false;
    };
    expect(ctaSaveDisabled(0)).toBe(true);
  });

  it('renderCtaOptions returns tooltip row if disabled', () => {
    const label = 'Test';
    const tooltipLabel = 'Disabled!';
    const isDisabled = true;
    // Simulate renderCtaOptions
    const result = (
      <div>
        <span>{label}</span>
        {isDisabled && <span>{tooltipLabel}</span>}
      </div>
    );
    expect(result.props.children[1].props.children).toBe('Disabled!');
  });
});

describe('CapActionButton function logic', () => {
  it('onCtaTypeChange resets fields and calls updateButtonChange', () => {
    const updateButtonChange = jest.fn();
    const suggestions = [{
      index: 0,
      ctaType: RCS_BUTTON_TYPES.QUICK_REPLY,
      displayText: 'Some text',
      phoneNumber: '12345',
      url: 'https://a.com',
      postback: 'Some text',
      isSaved: false,
    }];
    // Mock setUrlError and setButtonError
    let urlError = true, buttonError = true;
    const setUrlError = (v) => { urlError = v; };
    const setButtonError = (v) => { buttonError = v; };
    // Function under test
    const onCtaTypeChange = (value, index) => {
      let clonedCta = { ...suggestions[index], ctaType: value, displayText: '', phoneNumber: '', postback: '', url: '', isSaved: false };
      setUrlError(false);
      setButtonError(false);
      updateButtonChange(clonedCta, index);
    };
    onCtaTypeChange(RCS_BUTTON_TYPES.URL_ACTION, 0);
    expect(urlError).toBe(false);
    expect(buttonError).toBe(false);
    expect(updateButtonChange).toHaveBeenCalledWith(expect.objectContaining({ ctaType: RCS_BUTTON_TYPES.URL_ACTION, displayText: '', phoneNumber: '', postback: '', url: '', isSaved: false }), 0);
  });

  it('onButtonTextChange sets error for invalid text and calls updateDisplayAndPostback', () => {
    const updateDisplayAndPostback = jest.fn();
    const isValidText = jest.fn(() => false);
    const formatMessage = jest.fn(() => 'Invalid!');
    let errorMessage = '';
    if (!isValidText('bad')) errorMessage = formatMessage();
    // Simulate setButtonError
    let buttonError = '';
    const setButtonError = (v) => { buttonError = v; };
    setButtonError(errorMessage);
    updateDisplayAndPostback('bad', 0);
    expect(buttonError).toBe('Invalid!');
    expect(updateDisplayAndPostback).toHaveBeenCalledWith('bad', 0);
  });

  it('onUrlChange sets error for invalid url and calls updateHandler', () => {
    const updateHandler = jest.fn();
    const isUrl = jest.fn(() => false);
    const formatMessage = jest.fn(() => 'Bad URL!');
    let errorMessage = false;
    if (!isUrl('badurl')) errorMessage = formatMessage();
    // Simulate setUrlError
    let urlError = false;
    const setUrlError = (v) => { urlError = v; };
    setUrlError(errorMessage);
    updateHandler('url', 'badurl', 0);
    expect(urlError).toBe('Bad URL!');
    expect(updateHandler).toHaveBeenCalledWith('url', 'badurl', 0);
  });

  it('updateHandler clones and updates the correct field', () => {
    const updateButtonChange = jest.fn();
    const suggestions = [{ index: 0, displayText: 'A', phoneNumber: '', url: '', postback: 'A', isSaved: false }];
    const type = 'displayText';
    const value = 'B';
    const index = 0;
    const updateHandler = (type, value, index) => {
      let clonedCta = { ...suggestions[index], [type]: value };
      updateButtonChange(clonedCta, index);
    };
    updateHandler(type, value, index);
    expect(updateButtonChange).toHaveBeenCalledWith(expect.objectContaining({ displayText: 'B' }), 0);
  });

  it('updateDisplayAndPostback updates both displayText and postback', () => {
    const updateButtonChange = jest.fn();
    const suggestions = [{ index: 0, displayText: 'A', postback: 'A', isSaved: false }];
    const value = 'B';
    const index = 0;
    const updateDisplayAndPostback = (value, index) => {
      const cloned = { ...suggestions[index], displayText: value, postback: value };
      updateButtonChange(cloned, index);
    };
    updateDisplayAndPostback(value, index);
    expect(updateButtonChange).toHaveBeenCalledWith(expect.objectContaining({ displayText: 'B', postback: 'B' }), 0);
  });

  it('onPhoneNoChange calls updateHandler with phoneNumber', () => {
    const updateHandler = jest.fn();
    const value = '12345';
    const index = 0;
    const onPhoneNoChange = (value, index) => {
      updateHandler('phoneNumber', value, index);
    };
    onPhoneNoChange(value, index);
    expect(updateHandler).toHaveBeenCalledWith('phoneNumber', '12345', 0);
  });

  it('ctaSaveDisabled returns true for empty text, short phone, or url error', () => {
    const buttonError = false;
    const urlError = false;
    const suggestions = [
      { ctaType: RCS_BUTTON_TYPES.QUICK_REPLY, displayText: '', phoneNumber: '', url: '' },
      { ctaType: RCS_BUTTON_TYPES.DIALER_ACTION, displayText: 'A', phoneNumber: '123', url: '' },
      { ctaType: RCS_BUTTON_TYPES.URL_ACTION, displayText: 'A', phoneNumber: '', url: '', urlError: true },
    ];
    const ctaSaveDisabled = (index) => {
      const { ctaType, displayText, phoneNumber, url } = suggestions[index] || {};
      if (displayText === '' || buttonError) return true;
      if (ctaType === RCS_BUTTON_TYPES.DIALER_ACTION && phoneNumber.length < 5) return true;
      if (ctaType === RCS_BUTTON_TYPES.URL_ACTION && (url === '' || urlError)) return true;
      return false;
    };
    expect(ctaSaveDisabled(0)).toBe(true);
    expect(ctaSaveDisabled(1)).toBe(true);
    expect(ctaSaveDisabled(2)).toBe(true);
  });

  it('saveCta and editCta call updateHandler with correct isSaved', () => {
    const updateHandler = jest.fn();
    const saveCta = (index) => { updateHandler('isSaved', true, index); };
    const editCta = (index) => { updateHandler('isSaved', false, index); };
    saveCta(1);
    expect(updateHandler).toHaveBeenCalledWith('isSaved', true, 1);
    editCta(2);
    expect(updateHandler).toHaveBeenCalledWith('isSaved', false, 2);
  });

  it('addSuggestions creates a new suggestion and calls updateButtonChange', () => {
    const updateButtonChange = jest.fn();
    const INITIAL_SUGGESTIONS = [{ displayText: '', phoneNumber: '', url: '', postback: '', isSaved: false }];
    const suggestions = [{ index: 0, displayText: 'A', isSaved: true }];
    const addSuggestions = () => {
      let newSuggestion = { ...INITIAL_SUGGESTIONS[0] };
      newSuggestion.index = suggestions.length;
      updateButtonChange(newSuggestion, suggestions.length);
    };
    addSuggestions();
    expect(updateButtonChange).toHaveBeenCalledWith(expect.objectContaining({ index: 1 }), 1);
  });

  it('renderCtaOptions returns tooltip row if disabled, else label', () => {
    const label = 'Test';
    const tooltipLabel = 'Disabled!';
    const isDisabled = true;
    const CapRow = ({ children }) => <div>{children}</div>;
    const CapColumn = ({ children }) => <span>{children}</span>;
    const CapTooltipWithInfo = ({ title }) => <span>{title}</span>;
    const renderCtaOptions = (label, tooltipLabel, isDisabled) => {
      if (isDisabled) {
        return (
          <CapRow>
            <CapColumn>{label}</CapColumn>
            <CapColumn><CapTooltipWithInfo title={tooltipLabel} /></CapColumn>
          </CapRow>
        );
      }
      return label;
    };
    const result = renderCtaOptions(label, tooltipLabel, true);
    expect(result.props.children[1].props.children.props.title).toBe('Disabled!');
    expect(renderCtaOptions(label, tooltipLabel, false)).toBe('Test');
  });

  it('renderLength returns correct CapHeading', () => {
    const CapHeading = ({ type, className, children }) => <div className={className}>{children}</div>;
    const formatMessage = (msg, values) => `${values.currentLength}/${values.maxLength}`;
    const renderLength = (len, max) => (
      <CapHeading type="label1" className="rcs-render-btn-length">
        {formatMessage({}, { currentLength: len, maxLength: max })}
      </CapHeading>
    );
    const result = renderLength(5, 25);
    expect(result.props.children).toBe('5/25');
  });
});
