import { describe, it, expect } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { InboxProvider } from './provider';
import { Inbox } from './inbox';
import { MockInboxAdapter } from '../adapters/mock-inbox';

function mount() {
  return render(
    <InboxProvider adapter={new MockInboxAdapter()}>
      <Inbox />
    </InboxProvider>,
  );
}

describe('mock inbox adapter', () => {
  it('unifies work items + mail in the Open view; other states drop mail', async () => {
    const a = new MockInboxAdapter();
    const open = await a.listInbox('OPEN');
    expect(open.some((i) => i.kind === 'MAIL')).toBe(true);
    expect(open.some((i) => i.kind === 'MENTION')).toBe(true);
    const done = await a.listInbox('DONE');
    expect(done.some((i) => i.kind === 'MAIL')).toBe(false);
  });

  it('transition moves a work item out of Open', async () => {
    const a = new MockInboxAdapter();
    await a.transition('in_3', 'DONE');
    expect((await a.listInbox('OPEN')).some((i) => i.id === 'in_3')).toBe(false);
    expect((await a.listInbox('DONE')).some((i) => i.id === 'in_3')).toBe(true);
  });

  it('mail reply appends to the thread', async () => {
    const a = new MockInboxAdapter();
    await a.mailReply('mt_welcome', 'thanks!');
    expect((await a.mailHistory('mt_welcome')).some((m) => m.text === 'thanks!')).toBe(true);
  });

  it('reply carries an uploaded attachment', async () => {
    const a = new MockInboxAdapter();
    const ref = await a.uploadAttachment(new File(['x'], 'plan.pdf', { type: 'application/pdf' }));
    expect(ref).toMatchObject({ filename: 'plan.pdf', mimeType: 'application/pdf' });
    await a.mailReply('mt_welcome', '', ref);
    const last = (await a.mailHistory('mt_welcome')).at(-1)!;
    expect(last.attachment?.filename).toBe('plan.pdf');
  });

  it('composeInternal carries a first attachment onto the new thread', async () => {
    const a = new MockInboxAdapter();
    const ref = await a.uploadAttachment(new File(['x'], 'quote.png', { type: 'image/png' }));
    await a.composeInternal('pp_sofia', 'Quote', 'see attached', [ref]);
    const open = await a.listInbox('OPEN');
    const row = open.find((i) => i.title === 'Quote')!;
    expect((await a.mailHistory(row.threadId!)).at(-1)?.attachment?.filename).toBe('quote.png');
  });
});

describe('<Inbox /> (rendered)', () => {
  it('lists items and opens a mail thread on click', async () => {
    mount();
    // A folded mail row is present.
    const welcome = await screen.findByText('Welcome to the Founders Club');
    fireEvent.click(welcome);
    // The reader opens with a reply box.
    expect(await screen.findByLabelText('Reply')).toBeTruthy();
  });

  it('replies into a mail thread', async () => {
    mount();
    fireEvent.click(await screen.findByText('Welcome to the Founders Club'));
    const input = (await screen.findByLabelText('Reply')) as HTMLInputElement;
    fireEvent.change(input, { target: { value: 'got it' } });
    fireEvent.click(screen.getByText('Reply'));
    await waitFor(() => expect(screen.getByText('got it')).toBeTruthy());
  });

  it('attaches a file into a mail reply', async () => {
    mount();
    fireEvent.click(await screen.findByText('Welcome to the Founders Club'));
    const file = new File(['data'], 'roof.pdf', { type: 'application/pdf' });
    fireEvent.change(await screen.findByLabelText('Attach file'), { target: { files: [file] } });
    // The pending chip shows the file, then Reply sends it.
    await screen.findByText(/roof\.pdf/);
    fireEvent.click(screen.getByText('Reply'));
    await waitFor(() => expect(screen.getAllByText(/roof\.pdf/).length).toBeGreaterThan(0));
  });

  it('composes an in-app message and it shows in the inbox', async () => {
    mount();
    fireEvent.click(await screen.findByText('New message'));
    // pick a recipient
    fireEvent.click(await screen.findByText('Sofia Ramirez'));
    fireEvent.change(screen.getByLabelText('Subject'), { target: { value: 'Quick q' } });
    fireEvent.change(screen.getByLabelText('Message body'), { target: { value: 'ping' } });
    fireEvent.click(screen.getByText('Send'));
    await waitFor(() => expect(screen.getByText('Quick q')).toBeTruthy());
  });
});
