import React from 'react';
import {
  render, screen, waitFor, within,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { IntlProvider } from 'react-intl';
import SenderDetails, { parseSenderDetailsFromEntity } from '../SenderDetails';
import * as deliverySettingsConfig from '../deliverySettingsConfig';

const { parseEntityForDisplay } = deliverySettingsConfig;

/** Shared domainProperties entity shapes (see deliverySettingsConfig). */
const ENTITIES = {
  smsOk: {
    SMS: [
      {
        domainProperties: {
          id: 'sms-dom-1',
          domainName: 'SMS domain',
          contactInfo: [
            {
              type: 'gsm_sender_id',
              value: '+1002003001',
              valid: true,
              default: true,
            },
          ],
        },
      },
    ],
  },
  smsTwoDomains: {
    SMS: [
      {
        domainProperties: {
          id: 'sms-a',
          domainName: 'Gateway A',
          contactInfo: [
            {
              type: 'gsm_sender_id', value: '+111', valid: true, default: true,
            },
          ],
        },
      },
      {
        domainProperties: {
          id: 'sms-b',
          domainName: 'Gateway B',
          contactInfo: [
            {
              type: 'gsm_sender_id', value: '+222', valid: true, default: true,
            },
          ],
        },
      },
    ],
  },
  smsTwoSenders: {
    SMS: [
      {
        domainProperties: {
          id: 'sms-dom-1',
          domainName: 'SMS domain',
          contactInfo: [
            {
              type: 'gsm_sender_id', value: '+1002003001', valid: true, default: true,
            },
            {
              type: 'gsm_sender_id', value: '+1002003002', valid: true, default: false,
            },
          ],
        },
      },
    ],
  },
  emailTwoDomains: {
    EMAIL: [
      {
        domainProperties: {
          id: 'em-a',
          domainName: 'Mail Alpha',
          contactInfo: [
            {
              type: 'sender_id',
              value: 'alpha@brand.com',
              label: 'Alpha Name',
              valid: true,
              default: true,
            },
            {
              type: 'reply_to_id', value: 'reply-a@brand.com', valid: true, default: true,
            },
          ],
        },
      },
      {
        domainProperties: {
          id: 'em-b',
          domainName: 'Mail Beta',
          contactInfo: [
            {
              type: 'sender_id',
              value: 'beta@other.com',
              label: 'Beta Name',
              valid: true,
              default: true,
            },
            {
              type: 'reply_to_id', value: 'reply-b@other.com', valid: true, default: true,
            },
          ],
        },
      },
    ],
  },
  whatsappWaba: {
    WHATSAPP: [
      {
        domainProperties: {
          id: 'wa-dom-99',
          domainName: 'WA Business',
          connectionProperties: { sourceAccountIdentifier: 'waba-xyz' },
          contactInfo: [
            {
              type: 'gsm_sender_id', value: '+91900111222', valid: true, default: true,
            },
            {
              type: 'gsm_sender_id', value: '+91900111333', valid: true, default: false,
            },
          ],
        },
      },
    ],
  },
  rcsBrand: {
    RCS: [
      {
        domainProperties: {
          id: 'rcs-1',
          domainName: 'RCS Brand Co',
          contactInfo: [],
        },
      },
    ],
  },
};

function defaultSenderProps(override = {}) {
  const { onClose: overrideOnClose, onSave: overrideOnSave, ...rest } = override;
  return {
    show: true,
    channels: ['SMS'],
    preloadedDomainProperties: ENTITIES.smsOk,
    savedFieldValues: null,
    whatsappSourceAccountId: '',
    whatsappAccountName: '',
    ...rest,
    onClose: overrideOnClose ?? jest.fn(),
    onSave: overrideOnSave ?? jest.fn(),
  };
}

function renderSenderDetails(override = {}) {
  const props = defaultSenderProps(override);
  const { onClose, onSave } = props;
  const view = render(
    <IntlProvider locale="en" messages={{}} defaultLocale="en">
      <SenderDetails {...props} />
    </IntlProvider>,
  );
  return { ...view, onClose, onSave };
}

async function openSelectAndChoose(root, comboIndex, optionText) {
  const combos = within(root).getAllByRole('combobox');
  await userEvent.click(combos[comboIndex]);
  await waitFor(() => expect(screen.getByRole('listbox')).toBeInTheDocument());
  await userEvent.click(within(screen.getByRole('listbox')).getByText(optionText));
}

describe('SenderDetails', () => {
  it('renders SMS fields when domain properties are preloaded', async () => {
    renderSenderDetails();
    const root = document.querySelector('.sender-details');
    expect(root).toBeInTheDocument();
    await waitFor(() => {
      expect(within(root).getByText('Sender details')).toBeInTheDocument();
      expect(within(root).getByText('SMS Domain')).toBeInTheDocument();
      expect(within(root).getByText('Sender ID')).toBeInTheDocument();
      expect(within(root).getByText('Save changes')).toBeInTheDocument();
    });
  });

  it('calls onClose when the back control is used', async () => {
    const { onClose } = renderSenderDetails();
    const root = document.querySelector('.sender-details');
    await waitFor(() => {
      expect(within(root).getByLabelText('Back')).toBeInTheDocument();
    });
    await userEvent.click(within(root).getByLabelText('Back'));
    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it('shows domain gateway error when no SMS domains exist', async () => {
    renderSenderDetails({
      preloadedDomainProperties: { SMS: [] },
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => {
      expect(
        within(root).getByText(/Domain gateway id is not found for the selected channel/i),
      ).toBeInTheDocument();
    });
  });

  it('shows sender not configured when the domain has no sender options', async () => {
    renderSenderDetails({
      preloadedDomainProperties: {
        SMS: [
          {
            domainProperties: {
              id: 'sms-empty-senders',
              domainName: 'Gateway without senders',
              contactInfo: [],
            },
          },
        ],
      },
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => {
      expect(
        within(root).getByText(/Selected domain gateway id is not correct/i),
      ).toBeInTheDocument();
    });
  });

  it('stays empty of field labels when slidebox is open but preloaded data is missing', () => {
    renderSenderDetails({
      preloadedDomainProperties: null,
    });
    const root = document.querySelector('.sender-details');
    expect(root).toBeInTheDocument();
    expect(screen.queryByText('SMS Domain')).not.toBeInTheDocument();
  });

  it('applies savedFieldValues over API defaults for SMS sender', async () => {
    renderSenderDetails({
      preloadedDomainProperties: ENTITIES.smsTwoSenders,
      savedFieldValues: { smsSenderId: '+1002003002' },
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => {
      expect(within(root).getByText('+1002003002')).toBeInTheDocument();
    });
  });

  it('saves SMS after changing sender and invokes onSave then onClose', async () => {
    const { onSave, onClose } = renderSenderDetails({
      preloadedDomainProperties: ENTITIES.smsTwoSenders,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('Sender ID')).toBeInTheDocument());

    const combos = within(root).getAllByRole('combobox');
    await userEvent.click(combos[1]);
    await waitFor(() => expect(screen.getByRole('listbox')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('listbox')).getByText('+1002003002'));

    const saveBtn = within(root).getByRole('button', { name: /save changes/i });
    await waitFor(() => expect(saveBtn).not.toBeDisabled());
    await userEvent.click(saveBtn);

    expect(onSave).toHaveBeenCalledTimes(1);
    expect(onSave.mock.calls[0][0]).toMatchObject({
      smsSenderId: '+1002003002',
    });
    expect(onClose).toHaveBeenCalled();
  });

  it('resets SMS domain when multiple gateways exist', async () => {
    renderSenderDetails({
      preloadedDomainProperties: ENTITIES.smsTwoDomains,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('Gateway A')).toBeInTheDocument());

    await openSelectAndChoose(root, 0, 'Gateway B');
    await waitFor(() => expect(within(root).getByText('+222')).toBeInTheDocument());

    const resetLinks = within(root).getAllByTitle('Reset');
    await userEvent.click(resetLinks[0]);

    await waitFor(() => {
      expect(within(root).getByText('Gateway A')).toBeInTheDocument();
      expect(within(root).getByText('+111')).toBeInTheDocument();
    });
  });

  it('updates email sender fields when email domain changes', async () => {
    renderSenderDetails({
      channels: ['EMAIL'],
      preloadedDomainProperties: ENTITIES.emailTwoDomains,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('Mail Alpha')).toBeInTheDocument());

    await openSelectAndChoose(root, 0, 'Mail Beta');

    await waitFor(() => {
      expect(within(root).getByText('beta@other.com')).toBeInTheDocument();
    });
  });

  it('resets email domain row back to the first gateway', async () => {
    renderSenderDetails({
      channels: ['EMAIL'],
      preloadedDomainProperties: ENTITIES.emailTwoDomains,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('Mail Alpha')).toBeInTheDocument());

    await openSelectAndChoose(root, 0, 'Mail Beta');
    await waitFor(() => expect(within(root).getByText('beta@other.com')).toBeInTheDocument());

    const resetLinks = within(root).getAllByTitle('Reset');
    await userEvent.click(resetLinks[0]);

    await waitFor(() => {
      expect(within(root).getByText('alpha@brand.com')).toBeInTheDocument();
    });
  });

  it('resets SMS sender ID via generic reset branch', async () => {
    renderSenderDetails({
      preloadedDomainProperties: ENTITIES.smsTwoSenders,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('+1002003001')).toBeInTheDocument());

    const combos = within(root).getAllByRole('combobox');
    await userEvent.click(combos[1]);
    await waitFor(() => expect(screen.getByRole('listbox')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('listbox')).getByText('+1002003002'));

    const resetLinks = within(root).getAllByTitle('Reset');
    await userEvent.click(resetLinks[1]);

    await waitFor(() => expect(within(root).getByText('+1002003001')).toBeInTheDocument());
  });

  it('persists whatsappDomainId on save when WABA matches template source id', async () => {
    const { onSave } = renderSenderDetails({
      channels: ['WHATSAPP'],
      preloadedDomainProperties: ENTITIES.whatsappWaba,
      whatsappSourceAccountId: 'waba-xyz',
      whatsappAccountName: 'My Business',
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('My Business')).toBeInTheDocument());

    const combos = within(root).getAllByRole('combobox');
    await userEvent.click(combos[combos.length - 1]);
    await waitFor(() => expect(screen.getByRole('listbox')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('listbox')).getByText('+91900111333'));

    const saveBtn = within(root).getByRole('button', { name: /save changes/i });
    await waitFor(() => expect(saveBtn).not.toBeDisabled());
    await userEvent.click(saveBtn);

    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({
        whatsappDomainId: 'wa-dom-99',
        whatsappSenderNumber: '+91900111333',
      }),
    );
  });

  it('shows RCS account name from preloaded entity', async () => {
    renderSenderDetails({
      channels: ['RCS'],
      preloadedDomainProperties: ENTITIES.rcsBrand,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => {
      expect(within(root).getByText('RCS Brand Co')).toBeInTheDocument();
    });
  });

  it('exposes parseSenderDetailsFromEntity aligned with parseEntityForDisplay', () => {
    expect(parseSenderDetailsFromEntity(ENTITIES.smsOk)).toEqual(
      parseEntityForDisplay(ENTITIES.smsOk, {}),
    );
  });

  it('renders nothing substantive when channels list is empty', async () => {
    renderSenderDetails({
      channels: [],
      preloadedDomainProperties: ENTITIES.smsOk,
    });
    const root = document.querySelector('.sender-details');
    expect(root).toBeInTheDocument();
    await waitFor(() => {
      expect(within(root).queryByText('SMS Domain')).not.toBeInTheDocument();
    });
  });

  it('changing SMS domain auto-populates the sender ID (handleFieldChange cascade)', async () => {
    // Covers lines 102-104: fieldKey === 'smsDomain' && entity → auto-populate smsSenderId
    renderSenderDetails({
      channels: ['SMS'],
      preloadedDomainProperties: ENTITIES.smsTwoDomains,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('Gateway A')).toBeInTheDocument());

    const combos = within(root).getAllByRole('combobox');
    await userEvent.click(combos[0]);
    await waitFor(() => expect(screen.getByRole('listbox')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('listbox')).getByText('Gateway B'));

    // The sender ID should now auto-update to Gateway B's sender
    await waitFor(() => expect(within(root).getByText('+222')).toBeInTheDocument());
  });

  it('changing EMAIL domain cascades into sender ID, name, and reply-to fields', async () => {
    // Covers lines 106-110: fieldKey === 'emailDomain' && entity → cascade update
    renderSenderDetails({
      channels: ['EMAIL'],
      preloadedDomainProperties: ENTITIES.emailTwoDomains,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('Mail Alpha')).toBeInTheDocument());

    // Switch to Mail Beta domain
    const combos = within(root).getAllByRole('combobox');
    await userEvent.click(combos[0]);
    await waitFor(() => expect(screen.getByRole('listbox')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('listbox')).getByText('Mail Beta'));

    // Sender ID should auto-update to beta@other.com
    await waitFor(() => expect(within(root).getByText('beta@other.com')).toBeInTheDocument());
  });

  it('handles WhatsApp domain derivation in handleSave with sourceAccountIdentifier connectionProperties', async () => {
    // Covers lines 147-155: whatsappSourceAccountId && entity → derive whatsappDomainId
    const onSave = jest.fn();
    renderSenderDetails({
      channels: ['WHATSAPP'],
      preloadedDomainProperties: ENTITIES.whatsappWaba,
      whatsappSourceAccountId: 'waba-xyz',
      whatsappAccountName: 'WA Business',
      onSave,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('WA Business')).toBeInTheDocument());

    // Change sender to enable save
    const combos = within(root).getAllByRole('combobox');
    const senderCombo = combos[combos.length - 1];
    await userEvent.click(senderCombo);
    await waitFor(() => expect(screen.getByRole('listbox')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('listbox')).getByText('+91900111333'));

    const saveBtn = within(root).getByRole('button', { name: /save changes/i });
    await waitFor(() => expect(saveBtn).not.toBeDisabled());
    await userEvent.click(saveBtn);

    // Should have called onSave with whatsappDomainId derived from the matched WABA domain
    expect(onSave).toHaveBeenCalledWith(
      expect.objectContaining({ whatsappDomainId: 'wa-dom-99' }),
    );
  });

  it('resets viberSenderId via the else branch in handleReset (non-domain field)', async () => {
    // Covers lines 136-138: else branch in handleReset for non-smsDomain/emailDomain fields
    const viberEntity = {
      VIBER: [{
        id: 'vgw',
        domainProperties: {
          id: 0,
          domainName: 'Viber GW',
          contactInfo: [
            { type: 'gsm_sender_id', value: '+viber-def', valid: true, default: true },
            { type: 'gsm_sender_id', value: '+viber-alt', valid: true, default: false },
          ],
        },
      }],
    };
    renderSenderDetails({
      channels: ['VIBER'],
      preloadedDomainProperties: viberEntity,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('Sender ID')).toBeInTheDocument());

    // Change to an alternate sender
    const combos = within(root).getAllByRole('combobox');
    const senderCombo = combos[combos.length - 1];
    await userEvent.click(senderCombo);
    await waitFor(() => expect(screen.getByRole('listbox')).toBeInTheDocument());
    await userEvent.click(within(screen.getByRole('listbox')).getByText('+viber-alt'));

    await waitFor(() => expect(within(root).getByText('+viber-alt')).toBeInTheDocument());

    // Now click reset — for viberSenderId the else branch in handleReset is hit
    // (viberSenderId is not smsDomain/emailDomain, so getDefaultValueForField returns '')
    const resetLinks = within(root).getAllByTitle('Reset');
    await userEvent.click(resetLinks[resetLinks.length - 1]);
    // After reset, the field goes to the default value returned by getDefaultValueForField
    // For viberSenderId this is '' (no specific default handler)
    await waitFor(() => expect(within(root).queryByText('+viber-alt')).not.toBeInTheDocument());
  });

  it('renders a DISPLAY-type field as a read-only heading (no select or reset)', async () => {
    // Covers line 227-231: type === FIELD_TYPE.DISPLAY rendering path
    jest.spyOn(deliverySettingsConfig, 'getFieldsForChannels').mockReturnValue([
      {
        channel: 'EMAIL',
        fieldKey: 'emailDomainDisplay',
        messageKey: 'emailDomainLabel',
        type: deliverySettingsConfig.FIELD_TYPE.DISPLAY,
        getValue: () => 'Transactional domain',
        getOptions: () => [],
      },
    ]);
    renderSenderDetails({
      preloadedDomainProperties: ENTITIES.smsOk,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => {
      expect(within(root).getByText('Email Domain')).toBeInTheDocument();
    });
    // DISPLAY type should not show a combobox
    expect(within(root).queryByRole('combobox')).not.toBeInTheDocument();
  });

  it('returns null (renders nothing) for a field with an unknown type', async () => {
    // Covers line 271: return null fallthrough at end of renderField
    jest.spyOn(deliverySettingsConfig, 'getFieldsForChannels').mockReturnValue([
      {
        channel: 'SMS',
        fieldKey: 'unknownField',
        messageKey: 'smsDomain',
        type: 'not-a-real-type',
        getValue: () => '',
        getOptions: () => [],
      },
    ]);
    renderSenderDetails({
      preloadedDomainProperties: ENTITIES.smsOk,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('Sender details')).toBeInTheDocument());
    // No combobox should be rendered for an unknown field type
    expect(within(root).queryByRole('combobox')).not.toBeInTheDocument();
  });

  it('reset link click when only one option available does not call handleReset (isSingleOption guard)', async () => {
    // Covers line 259: onClick: if (!isSingleOption) handleReset(fieldKey) — single option branch
    // Ensure no active spies from previous tests interfere
    jest.restoreAllMocks();
    renderSenderDetails({
      preloadedDomainProperties: ENTITIES.smsOk,
    });
    const root = document.querySelector('.sender-details');
    await waitFor(() => expect(within(root).getByText('Sender ID')).toBeInTheDocument());

    // smsOk has exactly 1 sender option — clicking reset should be a no-op
    const resetLinks = within(root).getAllByTitle('Reset');
    const lastReset = resetLinks[resetLinks.length - 1];
    await userEvent.click(lastReset);
    // Value should remain the same (no state change)
    await waitFor(() => expect(within(root).getByText('+1002003001')).toBeInTheDocument());
  });
});
