import { describe, it, expect } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MessagingProvider } from '../provider';
import { MockAdapter } from '../adapters/mock';
import { ConversationSettings } from './conversation-settings';

function mount(adapter = new MockAdapter()) {
  render(
    <MessagingProvider adapter={adapter}>
      <ConversationSettings threadId="th_mock_2" title="Storm crew" membership="group" onClose={() => {}} />
    </MessagingProvider>,
  );
  return adapter;
}

describe('MockAdapter member management', () => {
  it('adds and removes a member, and renames', async () => {
    const a = new MockAdapter();
    await a.addMember('th_mock_2', 'pp_sofia');
    expect((await a.listMembers('th_mock_2')).some((m) => m.id === 'pp_sofia')).toBe(true);
    await a.removeMember('th_mock_2', 'pp_sofia');
    expect((await a.listMembers('th_mock_2')).some((m) => m.id === 'pp_sofia')).toBe(false);
    await a.renameConversation('th_mock_2', 'Renamed');
    expect((await a.listConversations()).find((c) => c.threadId === 'th_mock_2')?.title).toBe('Renamed');
  });
});

describe('<ConversationSettings />', () => {
  it('lists members and adds one from the directory', async () => {
    const a = mount();
    // A current member is shown.
    await screen.findByText('Dan Whitaker');
    // Add an addable person from the directory.
    const sofia = await screen.findByText('Sofia Ramirez');
    fireEvent.click(sofia);
    await waitFor(async () => {
      expect((await a.listMembers('th_mock_2')).some((m) => m.id === 'pp_sofia')).toBe(true);
    });
  });

  it('“#” switches the picker to channels you belong to, excluding this one', async () => {
    mount();
    await screen.findByText('Dan Whitaker');
    fireEvent.change(screen.getByLabelText('Search people'), { target: { value: '#' } });
    // Channels the mock user is in show up as import sources…
    await screen.findByText('#general');
    // …and the conversation being edited is never offered as its own source.
    expect(screen.queryByText('#Storm crew')).toBeNull();
  });

  it('imports a channel roster only after confirmation, and reports what landed', async () => {
    const a = mount();
    await screen.findByText('Dan Whitaker');
    const before = (await a.listMembers('th_mock_2')).length;

    fireEvent.change(screen.getByLabelText('Search people'), { target: { value: '#' } });
    fireEvent.click(await screen.findByText('#general'));

    // Staged, NOT applied — picking a channel must not mutate membership on its own.
    const confirm = await screen.findByRole('button', { name: /^Add \d+$/ });
    expect((await a.listMembers('th_mock_2')).length).toBe(before);

    fireEvent.click(confirm);
    await waitFor(async () => {
      expect((await a.listMembers('th_mock_2')).length).toBeGreaterThan(before);
    });
    await screen.findByText(/Added \d+/);
  });

  it('cancelling a staged import leaves membership untouched', async () => {
    const a = mount();
    await screen.findByText('Dan Whitaker');
    const before = (await a.listMembers('th_mock_2')).length;

    fireEvent.change(screen.getByLabelText('Search people'), { target: { value: '#' } });
    fireEvent.click(await screen.findByText('#general'));
    fireEvent.click(await screen.findByRole('button', { name: 'Cancel' }));

    await screen.findByLabelText('Search people'); // back to the picker
    expect((await a.listMembers('th_mock_2')).length).toBe(before);
  });

  it('hides the channel-import affordance when the adapter cannot bulk-add', async () => {
    const a = new MockAdapter();
    // A host that implements single add but not addMembers => no import path offered.
    (a as { addMembers?: unknown }).addMembers = undefined;
    render(
      <MessagingProvider adapter={a}>
        <ConversationSettings threadId="th_mock_2" title="Storm crew" membership="group" onClose={() => {}} />
      </MessagingProvider>,
    );
    await screen.findByText('Dan Whitaker');
    expect(screen.getByLabelText('Search people').getAttribute('placeholder')).toBe('Search people…');
    fireEvent.change(screen.getByLabelText('Search people'), { target: { value: '#' } });
    expect(screen.queryByText('#general')).toBeNull(); // '#' is just a search string here
  });
});
