import { Test, TestingModule } from '@nestjs/testing';
import { BlobstorageService } from '../../../gateways/blobstorage/blobstorage.service';
import { User } from '../../../repositories/users/entity/user.entity';
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';
import { FilesService } from '../files.service';
import { TasksRepositoryService } from '../../../repositories/tasks/tasks.repository.service';
import { Task } from '../../../repositories/tasks/entity/task.entity';

export type BlobstorageServiceMockValue = {
  createContainer: void | Error;
  containerExists: boolean | Error;
  fileExists: boolean | Error;
  publishUploadSas: string | Error;
  publishDownloadSas: string | Error;
};

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

export type TasksRepositoryMockValue = {
  create: Task | Error;
  getTasksFromAccountId: { tasks: Task[]; count: number } | Error;
};

export const makeFilesServiceMock = async (
  blobStorageService: BlobstorageServiceMockValue,
  usersRepositoryMockValue: UsersRepositoryMockValue,
  tasksRepositoryMockValue: TasksRepositoryMockValue,
): Promise<FilesService> => {
  const module: TestingModule = await Test.createTestingModule({
    providers: [FilesService],
  })
    .useMocker((token) => {
      switch (token) {
        case BlobstorageService:
          return makeBlobstorageServiceMock(blobStorageService);
        case UsersRepositoryService:
          return makeUsersRepositoryMock(usersRepositoryMockValue);
        case TasksRepositoryService:
          return makeTasksRepositoryMock(tasksRepositoryMockValue);
      }
    })
    .compile();

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

export const makeBlobstorageServiceMock = (
  value: BlobstorageServiceMockValue,
) => {
  const {
    containerExists,
    fileExists,
    createContainer,
    publishUploadSas,
    publishDownloadSas,
  } = value;

  return {
    containerExists:
      containerExists instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(containerExists)
        : jest.fn<Promise<boolean>, []>().mockResolvedValue(containerExists),
    fileExists:
      fileExists instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(fileExists)
        : jest.fn<Promise<boolean>, []>().mockResolvedValue(fileExists),
    createContainer:
      createContainer instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(createContainer)
        : jest.fn<Promise<void>, []>().mockResolvedValue(createContainer),
    publishUploadSas:
      publishUploadSas instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(publishUploadSas)
        : jest.fn<Promise<string>, []>().mockResolvedValue(publishUploadSas),
    publishDownloadSas:
      publishDownloadSas instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(publishDownloadSas)
        : jest.fn<Promise<string>, []>().mockResolvedValue(publishDownloadSas),
  };
};

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 makeBlobstorageServiceMockValue =
  (): BlobstorageServiceMockValue => {
    return {
      containerExists: true,
      fileExists: true,
      publishUploadSas: 'https://blob-storage?sas-token',
      publishDownloadSas: 'https://blob-storage?sas-token',
      createContainer: undefined,
    };
  };

export const makeTasksRepositoryMock = (value: TasksRepositoryMockValue) => {
  const { create } = value;

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

// 個別のテストケースに対応してそれぞれのMockを用意するのは無駄が多いのでテストケース内で個別の値を設定する
export const makeDefaultUsersRepositoryMockValue =
  (): UsersRepositoryMockValue => {
    return {
      findUserByExternalId: {
        id: 2,
        external_id: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx',
        account_id: 1234567890123456,
        role: 'none',
        author_id: '',
        accepted_terms_version: '1.0',
        email_verified: true,
        deleted_at: null,
        created_by: 'test',
        created_at: new Date(),
        updated_by: null,
        updated_at: null,
        auto_renew: true,
        license_alert: true,
        notification: true,
        encryption: false,
        prompt: false,
        account: {
          id: 2,
          parent_account_id: 2,
          tier: 5,
          country: '',
          delegation_permission: true,
          locked: false,
          company_name: '',
          verified: true,
          primary_admin_user_id: 2,
          deleted_at: null,
          created_by: '',
          created_at: new Date(),
          updated_by: '',
          updated_at: null,
        },
      },
    };
  };

export const makeDefaultTasksRepositoryMockValue =
  (): TasksRepositoryMockValue => {
    return {
      create: {
        id: 1,
        job_number: '00000001',
        account_id: 1,
        is_job_number_enabled: true,
        audio_file_id: 1,
        status: 'Uploaded',
        priority: '01',
        created_at: new Date(),
      },
      getTasksFromAccountId: {
        tasks: [],
        count: 0,
      },
    };
  };
