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

import { TeamsAdapter } from './adapter.js';
import type { InboundMessage } from '../types.js';

const mockFetch = vi.fn();

function jsonResponse(body: unknown, status = 200): Response {
  return {
    ok: status >= 200 && status < 300,
    status,
    json: async () => body,
    text: async () => JSON.stringify(body),
  } as Response;
}

describe('TeamsAdapter', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.stubGlobal('fetch', mockFetch);
  });

  it('has name teams and starts disconnected', () => {
    const adapter = new TeamsAdapter({ appId: 'id', appPassword: 'secret' });
    expect(adapter.name).toBe('teams');
    expect(adapter.status).toBe('disconnected');
  });

  it('send fetches bot token and posts activity', async () => {
    const adapter = new TeamsAdapter({ appId: 'id', appPassword: 'secret' });
    await adapter.connect();

    mockFetch.mockImplementation(async (url: string) => {
      if (url.includes('/oauth2/v2.0/token')) {
        return jsonResponse({ access_token: 'token123', expires_in: 3600 });
      }
      if (url.includes('/v3/conversations/conv1/activities')) {
        return jsonResponse({ id: 'activity1' });
      }
      throw new Error(`Unexpected fetch URL: ${url}`);
    });

    const peer = `teams:${Buffer.from(JSON.stringify({ conversationId: 'conv1', serviceUrl: 'https://smba.trafficmanager.net/amer' }), 'utf8').toString('base64url')}`;
    await adapter.send(peer, { text: 'Hello Teams' });

    expect(mockFetch).toHaveBeenCalledTimes(2);
    expect(mockFetch.mock.calls[1]?.[0]).toContain('/v3/conversations/conv1/activities');
  });

  it('handleActivity emits inbound message with replyPeerId', async () => {
    const adapter = new TeamsAdapter({ appId: 'id', appPassword: 'secret', requireMention: true });
    const inbound: InboundMessage[] = [];
    adapter.onMessage((msg) => inbound.push(msg));

    await adapter.handleActivity({
      type: 'message',
      id: 'm1',
      text: '<at>Flynn</at> run diagnostics',
      serviceUrl: 'https://smba.trafficmanager.net/amer',
      conversation: { id: 'conv1', conversationType: 'channel' },
      from: { id: 'user1', name: 'Alice' },
      recipient: { id: 'bot1', name: 'Flynn' },
      entities: [{ type: 'mention', mentioned: { id: 'bot1', name: 'Flynn' } }],
      timestamp: '2026-02-16T00:00:00.000Z',
    });

    expect(inbound).toHaveLength(1);
    expect(inbound[0]?.senderId).toBe('user1');
    expect(inbound[0]?.text).toBe('run diagnostics');
    const metadata = inbound[0]?.metadata as Record<string, unknown>;
    expect(typeof metadata?.replyPeerId).toBe('string');
  });

  it('drops group messages without mention when require_mention=true', async () => {
    const adapter = new TeamsAdapter({ appId: 'id', appPassword: 'secret', requireMention: true });
    const handler = vi.fn();
    adapter.onMessage(handler);

    await adapter.handleActivity({
      type: 'message',
      id: 'm2',
      text: 'hello bot',
      serviceUrl: 'https://smba.trafficmanager.net/amer',
      conversation: { id: 'conv1', conversationType: 'channel' },
      from: { id: 'user1', name: 'Alice' },
      recipient: { id: 'bot1', name: 'Flynn' },
      entities: [],
    });

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