import * as grpc from '@grpc/grpc-js';
import { kordProto } from '@kord/grpc-contracts';

// In-Memory Test Service Definition
class MockBotDashboardService {
  public Ping(call: any, callback: any) {
    if (!call.request.message) {
      return callback({ code: grpc.status.INVALID_ARGUMENT, details: 'Message is required' });
    }
    callback(null, { reply: `Pong to ${call.request.message}` });
  }

  public GetGuildChannels(call: any, callback: any) {
    const guildId = call.request.guildId;
    if (guildId === '123') {
      callback(null, {
        channels: [
          { id: '1', name: 'general', type: '0' },
          { id: '2', name: 'voice', type: '2' },
        ],
      });
    } else {
      callback({ code: grpc.status.NOT_FOUND, details: 'Guild not found' });
    }
  }
}

describe('gRPC Integration: BotDashboardService', () => {
  let server: grpc.Server;
  let client: any;

  beforeAll((done) => {
    // 1. Setup gRPC In-Memory Server for Tests
    server = new grpc.Server();
    const serviceImpl = new MockBotDashboardService();
    
    server.addService((kordProto as any).BotDashboardService.service, {
      Ping: serviceImpl.Ping.bind(serviceImpl),
      GetGuildChannels: serviceImpl.GetGuildChannels.bind(serviceImpl),
    });

    server.bindAsync('0.0.0.0:50052', grpc.ServerCredentials.createInsecure(), (err, port) => {
      if (err) return done(err);
      
      // 2. Setup Client pointing to the test server
      client = new (kordProto as any).BotDashboardService(
        `localhost:${port}`,
        grpc.credentials.createInsecure()
      );
      done();
    });
  });

  afterAll(() => {
    server.forceShutdown();
    client.close();
  });

  describe('When pinging the gRPC layer', () => {
    it('Then it should echo back with Pong', (done) => {
      client.Ping({ message: 'BDD_Test' }, (err: any, response: any) => {
        expect(err).toBeNull();
        expect(response.reply).toBe('Pong to BDD_Test');
        done();
      });
    });

    it('Then it should throw an INVALID_ARGUMENT error if message is missing', (done) => {
      client.Ping({ message: '' }, (err: any, response: any) => {
        expect(err).not.toBeNull();
        expect(err.code).toBe(grpc.status.INVALID_ARGUMENT);
        expect(response).toBeUndefined();
        done();
      });
    });
  });

  describe('When fetching Guild Channels', () => {
    it('Then it should return channel boundaries if guild exists', (done) => {
      client.GetGuildChannels({ guildId: '123' }, (err: any, response: any) => {
        expect(err).toBeNull();
        expect(response.channels).toHaveLength(2);
        expect(response.channels[0].name).toBe('general');
        done();
      });
    });

    it('Then it should return NOT_FOUND if guild does not exist', (done) => {
      client.GetGuildChannels({ guildId: '999' }, (err: any, response: any) => {
        expect(err).not.toBeNull();
        expect(err.code).toBe(grpc.status.NOT_FOUND);
        done();
      });
    });
  });
});
