import { HttpModule } from '@nestjs/common';
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigurationModule } from '../configuration/configuration.module';
import { MailerService } from '../mailer/mailer.service';
import { NewsletterSubscription } from '../newsletter/newsletter-subscription.schema';
import { NewsletterService } from '../newsletter/newsletter.service';
import { SearchModule } from '../search/search.module';
import { Structure } from '../structures/schemas/structure.schema';
import { StructuresService } from '../structures/services/structures.service';
import { StructuresSearchService } from '../structures/services/structures-search.service';
import { User } from '../users/schemas/user.schema';
import { UsersService } from '../users/users.service';
import { AdminController } from './admin.controller';
import { mockJwtAuthGuard } from '../../test/mock/guards/jwt-auth.mock.guard';
import { mockRoleGuard } from '../../test/mock/guards/role.mock.guard';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../users/guards/roles.guard';
import { UsersServiceMock } from '../../test/mock/services/user.mock.service';
import { StructuresServiceMock } from '../../test/mock/services/structures.mock.service';
import { NewsletterServiceMock } from '../../test/mock/services/newsletter.mock.service';

describe('AdminController', () => {
  let controller: AdminController;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [ConfigurationModule, HttpModule, SearchModule],
      providers: [
        {
          provide: UsersService,
          useClass: UsersServiceMock,
        },
        {
          provide: StructuresService,
          useClass: StructuresServiceMock,
        },
        {
          provide: NewsletterService,
          useClass: NewsletterServiceMock,
        },
        StructuresSearchService,
        MailerService,
        {
          provide: getModelToken('User'),
          useValue: User,
        },
        {
          provide: getModelToken('NewsletterSubscription'),
          useValue: NewsletterSubscription,
        },
        {
          provide: getModelToken('Structure'),
          useValue: Structure,
        },
      ],
      controllers: [AdminController],
    })
      .overrideGuard(JwtAuthGuard)
      .useValue(mockJwtAuthGuard)
      .overrideGuard(RolesGuard)
      .useValue(mockRoleGuard)
      .compile();

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

  it('should be defined', () => {
    expect(controller).toBeDefined();
  });

  it('should get pending attachments', async () => {
    expect((await controller.getPendingAttachments()).length).toBe(2);
    expect(Object.keys((await controller.getPendingAttachments())[0]).length).toBe(3);
  });

  describe('Pending structures validation', () => {
    it('should validate pending structure', async () => {
      const pendingStructureTest = {
        structureId: '6093ba0e2ab5775cfc01ed3e',
        structureName: 'test',
        userEmail: 'jean.paul@mii.com',
      };
      expect((await controller.validatePendingStructure(pendingStructureTest)).length).toBe(2);
      expect(Object.keys((await controller.validatePendingStructure(pendingStructureTest))[0]).length).toBe(3);
    });

    it('should get structure does not exist', async () => {
      const pendingStructureTest = {
        structureId: '1093ba0e2ab5775cfc01z2ki',
        structureName: 'test',
        userEmail: 'jean.paul@mii.com',
      };
      try {
        await controller.validatePendingStructure(pendingStructureTest);
      } catch (e) {
        expect(e.message).toBe('Structure does not exist');
        expect(e.status).toBe(404);
      }
    });
  });

  describe('Pending structures cancel', () => {
    it('should refuse pending structure', async () => {
      const pendingStructureTest = {
        structureId: '6093ba0e2ab5775cfc01ed3e',
        structureName: 'test',
        userEmail: 'jean.paul@mii.com',
      };
      expect((await controller.refusePendingStructure(pendingStructureTest)).length).toBe(2);
      expect(Object.keys((await controller.refusePendingStructure(pendingStructureTest))[0]).length).toBe(3);
    });

    it('should get structure does not exist', async () => {
      const pendingStructureTest = {
        structureId: '1093ba0e2ab5775cfc01z2ki',
        structureName: 'test',
        userEmail: 'jean.paul@mii.com',
      };
      try {
        await controller.refusePendingStructure(pendingStructureTest);
      } catch (e) {
        expect(e.message).toBe('Structure does not exist');
        expect(e.status).toBe(404);
      }
    });
  });

  describe('Delete user', () => {
    it('should delete user', async () => {
      expect((await controller.deleteUser({ id: 'tsfsf6296' })).email).toBe('pauline.dupont@mii.com');
    });
    it('should return unexisting user', async () => {
      try {
        await controller.deleteUser({ id: 'userdoesnotexist' });
      } catch (e) {
        expect(e.message).toBe('Invalid user id');
        expect(e.status).toBe(400);
      }
    });
  });

  describe('Search user', () => {
    it('should return all users, empty string', async () => {
      expect((await controller.searchUsers({ searchString: '' })).length).toBe(2);
    });
    it('should return all users, null input', async () => {
      expect((await controller.searchUsers({ searchString: null })).length).toBe(2);
    });
    it('should one user', async () => {
      expect((await controller.searchUsers({ searchString: 'a@a.com' })).length).toBe(1);
      expect((await controller.searchUsers({ searchString: 'a@a.com' }))[0].email).toBe('a@a.com');
    });
    it('should no user', async () => {
      expect((await controller.searchUsers({ searchString: 'dfqfqsfqfqfa@a.com' })).length).toBe(0);
    });
  });

  describe('Search user newleetter subscription', () => {
    it('should return all subscribed users, empty string', async () => {
      expect((await controller.getNewsletterSubscriptions({ searchString: '' })).length).toBe(3);
    });
    it('should return all subscribed users, null input', async () => {
      expect((await controller.getNewsletterSubscriptions({ searchString: null })).length).toBe(3);
    });
    it('should find one user', async () => {
      expect((await controller.getNewsletterSubscriptions({ searchString: 'a@a.com' })).length).toBe(1);
      expect((await controller.getNewsletterSubscriptions({ searchString: 'a@a.com' }))[0].email).toBe('a@a.com');
    });
    it('should find no user', async () => {
      expect((await controller.getNewsletterSubscriptions({ searchString: 'adgdgsdg@a.com' })).length).toBe(0);
    });
  });

  it('should count user subscribed to newsletter', async () => {
    expect(await controller.countNewsletterSubscriptions()).toBe(246);
  });

  describe('Search delete a user subscription', () => {
    it('should return a deleted object', async () => {
      expect((await controller.unsubscribeUserFromNewsletter('a@a.com')).email).toBe('a@a.com');
      expect(Object.keys(await controller.unsubscribeUserFromNewsletter('a@a.com')).length).toBe(4);
    });
    it('should throw an error', async () => {
      try {
        await controller.unsubscribeUserFromNewsletter('test@test.com');
      } catch (e) {
        expect(e.message).toBe('Invalid  email');
        expect(e.status).toBe(401);
      }
    });
  });

  it('should get pending structure list for admin', async () => {
    expect((await controller.getAdminStructuresList()).inClaim.length).toBe(2);
    expect((await controller.getAdminStructuresList()).toClaim.length).toEqual(2);
    expect((await controller.getAdminStructuresList()).claimed.length).toEqual(0);
    expect((await controller.getAdminStructuresList()).incomplete.length).toEqual(2);
  });
});