import { ConfigModule } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { NotificationhubService } from '../../../gateways/notificationhub/notificationhub.service';
import { User } from '../../../repositories/users/entity/user.entity';
import { NotificationService } from '../notification.service';
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';

export type UsersRepositoryMockValue = {
  findUserByExternalId: User | Error;
};

export type NotificationHubMockValue = {
  register: undefined | Error;
};

export const makeNotificationServiceMock = async (
  usersRepositoryMockValue: UsersRepositoryMockValue,
  notificationHubMockValue: NotificationHubMockValue,
): Promise<NotificationService> => {
  const module: TestingModule = await Test.createTestingModule({
    providers: [NotificationService],
    imports: [
      ConfigModule.forRoot({
        envFilePath: ['.env.local', '.env'],
      }),
    ],
  })
    .useMocker((token) => {
      switch (token) {
        case UsersRepositoryService:
          return makeUsersRepositoryMock(usersRepositoryMockValue);
        case NotificationhubService:
          return makeNotificationHubMock(notificationHubMockValue);
      }
    })
    .compile();

  return module.get<NotificationService>(NotificationService);
};

export const makeNotificationHubMock = (value: NotificationHubMockValue) => {
  const { register } = value;

  return {
    register:
      register instanceof Error
        ? jest
            .fn<Promise<void>, [number, string, string, string]>()
            .mockRejectedValue(register)
        : jest
            .fn<Promise<void>, [number, string, string, string]>()
            .mockResolvedValue(register),
  };
};

export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
  const { findUserByExternalId } = value;

  return {
    findUserByExternalId:
      findUserByExternalId instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(findUserByExternalId)
        : jest.fn<Promise<User>, []>().mockResolvedValue(findUserByExternalId),
  };
};

export const makeDefaultNotificationHubMockValue =
  (): NotificationHubMockValue => {
    return { register: undefined };
  };

// 個別のテストケースに対応してそれぞれのMockを用意するのは無駄が多いのでテストケース内で個別の値を設定する
export const makeDefaultUsersRepositoryMockValue =
  (): UsersRepositoryMockValue => {
    const user = new User();
    user.id = 2;
    user.external_id = 'external_id';
    user.account_id = 123;
    user.role = 'none';
    user.author_id = undefined;
    user.accepted_terms_version = '1.0';
    user.email_verified = true;
    user.auto_renew = false;
    user.license_alert = false;
    user.notification = false;
    user.deleted_at = undefined;
    user.created_by = 'test';
    user.created_at = new Date();
    user.updated_by = 'test';
    user.updated_at = new Date();

    return {
      findUserByExternalId: user,
    };
  };
