import { describe, it, expect, vi, beforeAll, beforeEach } from 'vitest';

const getAccessTokenMock = vi.fn(async () => ({ token: 'gchat-token' }));
const getClientMock = vi.fn(async () => ({ getAccessToken: getAccessTokenMock }));
const googleAuthCtorMock = vi.fn(() => ({ getClient: getClientMock }));

vi.mock('googleapis', () => ({
  google: {
    auth: {
      GoogleAuth: googleAuthCtorMock,
    },
  },
}));

const mockFetch = vi.fn();
vi.stubGlobal('fetch', mockFetch);

describe('GoogleChatAdapter', () => {
  let GoogleChatAdapter: typeof import('./adapter.js').GoogleChatAdapter;

  beforeAll(async () => {
    ({ GoogleChatAdapter } = await import('./adapter.js'));
  });

  beforeEach(() => {
    vi.clearAllMocks();
    mockFetch.mockReset();
  });

  it('has name google_chat and starts disconnected', () => {
    const adapter = new GoogleChatAdapter({ serviceAccountJson: '{}' });
    expect(adapter.name).toBe('google_chat');
    expect(adapter.status).toBe('disconnected');
  });

  it('send posts to Google Chat API with bearer token', async () => {
    const adapter = new GoogleChatAdapter({ serviceAccountJson: '{"type":"service_account"}' });
    await adapter.connect();

    mockFetch.mockResolvedValue({
      ok: true,
      status: 200,
      text: async () => '',
    } as Response);

    const peer = `gchat:${Buffer.from(JSON.stringify({ spaceName: 'spaces/AAA', threadName: 'spaces/AAA/threads/BBB' }), 'utf8').toString('base64url')}`;
    await adapter.send(peer, { text: 'hello chat' });

    expect(googleAuthCtorMock).toHaveBeenCalled();
    expect(mockFetch).toHaveBeenCalledTimes(1);
    expect(mockFetch.mock.calls[0]?.[0]).toBe('https://chat.googleapis.com/v1/spaces/AAA/messages');
    const init = mockFetch.mock.calls[0]?.[1] as RequestInit;
    expect((init.headers as Record<string, string>).Authorization).toBe('Bearer gchat-token');
  });

  it('handleEvent forwards MESSAGE event with reply metadata', async () => {
    const adapter = new GoogleChatAdapter({ serviceAccountJson: '{}', requireMention: true });
    const handler = vi.fn();
    adapter.onMessage(handler);

    await adapter.handleEvent({
      type: 'MESSAGE',
      message: {
        name: 'spaces/AAA/messages/123',
        argumentText: 'status check',
        text: '<users/1> status check',
        sender: { name: 'users/1', displayName: 'Alice' },
        space: { name: 'spaces/AAA', type: 'ROOM' },
        thread: { name: 'spaces/AAA/threads/BBB' },
      },
    });

    expect(handler).toHaveBeenCalledTimes(1);
    const msg = handler.mock.calls[0]?.[0] as { channel: string; text: string; metadata?: Record<string, unknown> };
    expect(msg.channel).toBe('google_chat');
    expect(msg.text).toBe('status check');
    expect(typeof msg.metadata?.replyPeerId).toBe('string');
  });

  it('drops non-DM room message when mention is required and argumentText is missing', async () => {
    const adapter = new GoogleChatAdapter({ serviceAccountJson: '{}', requireMention: true });
    const handler = vi.fn();
    adapter.onMessage(handler);

    await adapter.handleEvent({
      type: 'MESSAGE',
      message: {
        name: 'spaces/AAA/messages/123',
        text: 'no mention',
        sender: { name: 'users/1', displayName: 'Alice' },
        space: { name: 'spaces/AAA', type: 'ROOM' },
      },
    });

    expect(handler).not.toHaveBeenCalled();
  });
});
