import { Test, TestingModule } from '@nestjs/testing';
import { ForbiddenException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { createHmac } from 'crypto';
import { TwitchWebhookController } from './twitch-webhook.controller';
import { TwitchEventService } from './twitch-event.service';

const SECRET = 'test-webhook-secret';

const makeEventService = () => ({
  handleCheer: jest.fn(),
  handleSubscription: jest.fn(),
});

const makeConfigService = () => ({
  get: jest.fn((key: string) => (key === 'TWITCH_WEBHOOK_SECRET' ? SECRET : undefined)),
});

function buildRequest(messageId: string, timestamp: string, bodyStr: string) {
  const rawBody = Buffer.from(bodyStr, 'utf8');
  const hmac = `sha256=${createHmac('sha256', SECRET)
    .update(`${messageId}${timestamp}${bodyStr}`)
    .digest('hex')}`;

  return { rawBody, signature: hmac };
}

describe('TwitchWebhookController — signature', () => {
  let controller: TwitchWebhookController;
  let eventService: ReturnType<typeof makeEventService>;

  beforeEach(async () => {
    eventService = makeEventService();

    const module: TestingModule = await Test.createTestingModule({
      controllers: [TwitchWebhookController],
      providers: [
        { provide: TwitchEventService, useValue: eventService },
        { provide: ConfigService, useValue: makeConfigService() },
      ],
    }).compile();

    controller = module.get<TwitchWebhookController>(TwitchWebhookController);
  });

  it('signature valide → traite la requête sans erreur', async () => {
    const msgId = 'valid-msg-id';
    const ts = new Date().toISOString();
    const bodyObj = {
      subscription: { type: 'channel.cheer' },
      event: { user_id: 'user1', bits_used: 100 },
    };
    const bodyStr = JSON.stringify(bodyObj);
    const { rawBody, signature } = buildRequest(msgId, ts, bodyStr);

    const req = { rawBody } as any;

    await expect(
      controller.handleWebhook(req, msgId, ts, signature, 'notification', bodyObj as any),
    ).resolves.toEqual({ status: 'ok' });
  });

  it('signature invalide → ForbiddenException (400/403)', async () => {
    const msgId = 'invalid-msg-id';
    const ts = new Date().toISOString();
    const bodyObj = {
      subscription: { type: 'channel.cheer' },
      event: { user_id: 'user1', bits_used: 100 },
    };
    const bodyStr = JSON.stringify(bodyObj);
    const rawBody = Buffer.from(bodyStr, 'utf8');
    const badSignature = 'sha256=invalidsignaturevalue00000000000000000000000000000000000000000000';

    const req = { rawBody } as any;

    await expect(
      controller.handleWebhook(req, msgId, ts, badSignature, 'notification', bodyObj as any),
    ).rejects.toThrow(ForbiddenException);

    expect(eventService.handleCheer).not.toHaveBeenCalled();
  });

  it('signature manquante → ForbiddenException', async () => {
    const msgId = 'no-sig-msg';
    const ts = new Date().toISOString();
    const bodyObj = { subscription: { type: 'channel.cheer' }, event: {} };
    const bodyStr = JSON.stringify(bodyObj);
    const rawBody = Buffer.from(bodyStr, 'utf8');

    const req = { rawBody } as any;

    // Pass empty string as signature — length mismatch → ForbiddenException
    await expect(
      controller.handleWebhook(req, msgId, ts, '', 'notification', bodyObj as any),
    ).rejects.toThrow(ForbiddenException);
  });
});
