import { Test, TestingModule } from '@nestjs/testing';
import { TasksService } from '../tasks.service';
import { TasksRepositoryService } from '../../../repositories/tasks/tasks.repository.service';
import { User } from '../../../repositories/users/entity/user.entity';
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';
import { Task } from '../../../repositories/tasks/entity/task.entity';
import { CheckoutPermission } from '../../../repositories/checkout_permissions/entity/checkout_permission.entity';
import {
  SortDirection,
  TaskListSortableAttribute,
} from '../../../common/types/sort';
import { AdB2cService } from '../../../gateways/adb2c/adb2c.service';
import { AdB2cUser } from '../../../gateways/adb2c/types/types';
import { Assignee } from '../types/types';
import { UserGroupMember } from '../../../repositories/user_groups/entity/user_group_member.entity';
import { NotificationhubService } from '../../../gateways/notificationhub/notificationhub.service';
import { UserGroupsRepositoryService } from '../../../repositories/user_groups/user_groups.repository.service';

export type TasksRepositoryMockValue = {
  getTasksFromAccountId:
    | {
        tasks: Task[];
        permissions: CheckoutPermission[];
        count: number;
      }
    | Error;
  getTasksFromAuthorIdAndAccountId:
    | {
        tasks: Task[];
        permissions: CheckoutPermission[];
        count: number;
      }
    | Error;
  getTasksFromTypistRelations:
    | {
        tasks: Task[];
        permissions: CheckoutPermission[];
        count: number;
      }
    | Error;
  changeCheckoutPermission: void | Error;
};

export type AdB2CServiceMockValue = {
  getUsers: AdB2cUser[] | Error;
};

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

export type UserGroupsRepositoryMockValue = {
  getGroupMembersFromGroupIds: UserGroupMember[] | Error;
};

export type NotificationhubServiceMockValue = {
  notify: undefined | Error;
};

export const makeTasksServiceMock = async (
  tasksRepositoryMockValue: TasksRepositoryMockValue,
  usersRepositoryMockValue: UsersRepositoryMockValue,
  userGroupsRepositoryMockValue: UserGroupsRepositoryMockValue,
  adB2CServiceMockValue: AdB2CServiceMockValue,
  notificationhubServiceMockValue: NotificationhubServiceMockValue,
): Promise<{
  tasksService: TasksService;
  taskRepoService: TasksRepositoryService;
}> => {
  const module: TestingModule = await Test.createTestingModule({
    providers: [TasksService],
  })
    .useMocker((token) => {
      switch (token) {
        case TasksRepositoryService:
          return makeTasksRepositoryMock(tasksRepositoryMockValue);
        case UsersRepositoryService:
          return makeUsersRepositoryMock(usersRepositoryMockValue);
        case UserGroupsRepositoryService:
          return makeUserGroupsRepositoryMock(userGroupsRepositoryMockValue);
        case AdB2cService:
          return makeAdb2cServiceMock(adB2CServiceMockValue);
        case NotificationhubService:
          return makeNotificationhubServiceMock(
            notificationhubServiceMockValue,
          );
      }
    })
    .compile();

  return {
    tasksService: module.get<TasksService>(TasksService),
    taskRepoService: module.get(TasksRepositoryService),
  };
};

export const makeTasksRepositoryMock = (value: TasksRepositoryMockValue) => {
  const {
    getTasksFromAccountId,
    getTasksFromAuthorIdAndAccountId,
    getTasksFromTypistRelations,
    changeCheckoutPermission,
  } = value;
  return {
    getTasksFromAccountId:
      getTasksFromAccountId instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(getTasksFromAccountId)
        : jest
            .fn<
              Promise<{
                tasks: Task[];
                permissions: CheckoutPermission[];
                count: number;
              }>,
              []
            >()
            .mockResolvedValue(getTasksFromAccountId),
    getTasksFromAuthorIdAndAccountId:
      getTasksFromAuthorIdAndAccountId instanceof Error
        ? jest
            .fn<
              Promise<void>,
              [
                string,
                number,
                number,
                number,
                TaskListSortableAttribute,
                SortDirection,
                string[],
              ]
            >()
            .mockRejectedValue(getTasksFromAuthorIdAndAccountId)
        : jest
            .fn<
              Promise<{
                tasks: Task[];
                permissions: CheckoutPermission[];
                count: number;
              }>,
              []
            >()
            .mockResolvedValue(getTasksFromAuthorIdAndAccountId),
    getTasksFromTypistRelations:
      getTasksFromTypistRelations instanceof Error
        ? jest
            .fn<
              Promise<void>,
              [
                string,
                number,
                number,
                number,
                TaskListSortableAttribute,
                SortDirection,
                string[],
              ]
            >()
            .mockRejectedValue(getTasksFromTypistRelations)
        : jest
            .fn<
              Promise<{
                tasks: Task[];
                permissions: CheckoutPermission[];
                count: number;
              }>,
              []
            >()
            .mockResolvedValue(getTasksFromTypistRelations),
    changeCheckoutPermission:
      changeCheckoutPermission instanceof Error
        ? jest
            .fn<Promise<void>, []>()
            .mockRejectedValue(changeCheckoutPermission)
        : jest
            .fn<Promise<void>, [number, Assignee[], number]>()
            .mockResolvedValue(changeCheckoutPermission),
  };
};

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 makeDefaultTasksRepositoryMockValue =
  (): TasksRepositoryMockValue => {
    return {
      getTasksFromAccountId: defaultTasksRepositoryMockValue,
      getTasksFromAuthorIdAndAccountId: defaultTasksRepositoryMockValue,
      getTasksFromTypistRelations: defaultTasksRepositoryMockValue,
      changeCheckoutPermission: undefined,
    };
  };

export const makeAdb2cServiceMock = (value: AdB2CServiceMockValue) => {
  const { getUsers } = value;

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

export const makeDefaultAdb2cServiceMockValue = (): AdB2CServiceMockValue => {
  return {
    getUsers: [{ id: 'userId', displayName: 'XXXX XXX' }],
  };
};

export const makeNotificationhubServiceMock = (
  value: NotificationhubServiceMockValue,
) => {
  const { notify } = value;

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

export const makeDefaultNotificationhubServiceMockValue =
  (): NotificationhubServiceMockValue => {
    return {
      notify: undefined,
    };
  };

export const makeUserGroupsRepositoryMock = (
  value: UserGroupsRepositoryMockValue,
) => {
  const { getGroupMembersFromGroupIds } = value;

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

export const makeDefaultUserGroupsRepositoryMockValue =
  (): UserGroupsRepositoryMockValue => {
    return {
      getGroupMembersFromGroupIds: [
        {
          id: 1,
          user_group_id: 1,
          user_id: 1,
          created_by: 'test',
          updated_by: 'test',
        },
        {
          id: 2,
          user_group_id: 1,
          user_id: 2,
          created_by: 'test',
          updated_by: 'test',
        },
        {
          id: 3,
          user_group_id: 2,
          user_id: 1,
          created_by: 'test',
          updated_by: 'test',
        },
        {
          id: 4,
          user_group_id: 3,
          user_id: 1,
          created_by: 'test',
          updated_by: 'test',
        },
        {
          id: 5,
          user_group_id: 3,
          user_id: 3,
          created_by: 'test',
          updated_by: 'test',
        },
      ],
    };
  };

export const makeDefaultUsersRepositoryMockValue =
  (): UsersRepositoryMockValue => {
    const user1 = new User();
    user1.id = 1;
    user1.external_id = 'userId';
    user1.account_id = 1;
    user1.role = 'admin';
    user1.email_verified = true;
    user1.auto_renew = false;
    user1.license_alert = false;
    user1.notification = false;
    user1.deleted_at = undefined;
    user1.created_by = 'test';
    user1.created_at = new Date();
    user1.author_id = 'abcdef';

    return {
      findUserByExternalId: user1,
    };
  };
const defaultTasksRepositoryMockValue: {
  tasks: Task[];
  permissions: CheckoutPermission[];
  count: number;
} = {
  tasks: [
    {
      id: 1,
      job_number: '00000001',
      account_id: 1,
      is_job_number_enabled: true,
      audio_file_id: 1,
      status: 'Uploaded',
      priority: '00',
      created_at: new Date('2023-01-01T01:01:01.000'),
      option_items: [
        {
          id: 1,
          audio_file_id: 1,
          label: 'label01',
          value: 'value01',
        },
        {
          id: 2,
          audio_file_id: 1,
          label: 'label02',
          value: 'value02',
        },
        {
          id: 3,
          audio_file_id: 1,
          label: 'label03',
          value: 'value03',
        },
        {
          id: 4,
          audio_file_id: 1,
          label: 'label04',
          value: 'value04',
        },
        {
          id: 5,
          audio_file_id: 1,
          label: 'label05',
          value: 'value05',
        },
        {
          id: 6,
          audio_file_id: 1,
          label: 'label06',
          value: 'value06',
        },
        {
          id: 7,
          audio_file_id: 1,
          label: 'label07',
          value: 'value07',
        },
        {
          id: 8,
          audio_file_id: 1,
          label: 'label08',
          value: 'value08',
        },
        {
          id: 9,
          audio_file_id: 1,
          label: 'label09',
          value: 'value09',
        },
        {
          id: 10,
          audio_file_id: 1,
          label: 'label10',
          value: 'value10',
        },
      ],
      file: {
        id: 1,
        account_id: 1,
        owner_user_id: 1,
        url: 'test/test.zip',
        file_name: 'test.zip',
        author_id: 'AUTHOR',
        work_type_id: 'WorkType',
        started_at: new Date('2023-01-01T01:01:01.000'),
        duration: '123000',
        finished_at: new Date('2023-01-01T01:01:01.000'),
        uploaded_at: new Date('2023-01-01T01:01:01.000'),
        file_size: 123000,
        priority: '00',
        audio_format: 'DS',
        comment: 'comment',
        is_encrypted: true,
      },
    },
  ],
  permissions: [
    {
      id: 1,
      task_id: 1,
      user_id: 1,
      user: {
        id: 1,
        account_id: 1,
        external_id: 'userId',
        role: 'typist',
        accepted_terms_version: '',
        email_verified: true,
        auto_renew: true,
        license_alert: true,
        notification: true,
        encryption: false,
        prompt: false,
        created_by: 'test',
        created_at: new Date(),
        updated_by: 'test',
        updated_at: new Date(),
      },
    },
  ],
  count: 1,
};
