import { ConfigModule, ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { User } from '../../../repositories/users/entity/user.entity';
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';
import { AccountsService } from '../accounts.service';
import { AccountsRepositoryService } from '../../../repositories/accounts/accounts.repository.service';
import {
  AdB2cService,
  ConflictError,
} from '../../../gateways/adb2c/adb2c.service';
import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service';
import { Account, LicenseOrder, LicenseSummaryInfo } from '../types/types';
import { UserGroup } from '../../../repositories/user_groups/entity/user_group.entity';
import { UserGroupsRepositoryService } from '../../../repositories/user_groups/user_groups.repository.service';
import { AdB2cUser } from '../../../gateways/adb2c/types/types';
import { LicensesRepositoryService } from '../../../repositories/licenses/licenses.repository.service';

export type LicensesRepositoryMockValue = {
  getLicenseOrderHistoryInfo:
    | {
        total: number;
        orderHistories: LicenseOrder[];
      }
    | Error;
};
export type UsersRepositoryMockValue = {
  findUserById: User | Error;
  findUserByExternalId: User | Error;
  findTypistUsers: User[] | Error;
};
export type UserGroupsRepositoryMockValue = {
  getUserGroups: UserGroup[] | Error;
};
export type AdB2cMockValue = {
  createUser: string | ConflictError | Error;
  getUsers: AdB2cUser[] | Error;
};
export type SendGridMockValue = {
  createMailContentFromEmailConfirm: {
    subject: string;
    text: string;
    html: string;
  };
  createMailContentFromEmailConfirmForNormalUser: {
    subject: string;
    text: string;
    html: string;
  };
  sendMail: undefined | Error;
};
export type ConfigMockValue = {
  get: string | Error;
};
export type AccountsRepositoryMockValue = {
  getLicenseSummaryInfo: {
    licenseSummary: LicenseSummaryInfo;
    isStorageAvailable: boolean;
  };
  createAccount: { newAccount: Account; adminUser: User } | Error;
};
export const makeAccountsServiceMock = async (
  accountsRepositoryMockValue: AccountsRepositoryMockValue,
  usersRepositoryMockValue: UsersRepositoryMockValue,
  userGroupsRepositoryMockValue: UserGroupsRepositoryMockValue,
  adB2cMockValue: AdB2cMockValue,
  configMockValue: ConfigMockValue,
  sendGridMockValue: SendGridMockValue,
  licensesRepositoryMockValue: LicensesRepositoryMockValue,
): Promise<AccountsService> => {
  const module: TestingModule = await Test.createTestingModule({
    providers: [AccountsService],
    imports: [
      ConfigModule.forRoot({
        ignoreEnvFile: true,
        ignoreEnvVars: true,
      }),
    ],
  })
    .useMocker((token) => {
      switch (token) {
        case AccountsRepositoryService:
          return makeAccountsRepositoryMock(accountsRepositoryMockValue);
        case UsersRepositoryService:
          return makeUsersRepositoryMock(usersRepositoryMockValue);
        case UserGroupsRepositoryService:
          return makeUserGroupsRepositoryMock(userGroupsRepositoryMockValue);
        case AdB2cService:
          return makeAdB2cServiceMock(adB2cMockValue);
        case ConfigService:
          return makeConfigMock(configMockValue);
        case SendGridService:
          return makeSendGridServiceMock(sendGridMockValue);
        case LicensesRepositoryService:
          return makeLicensesRepositoryMock(licensesRepositoryMockValue);
      }
    })
    .compile();

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

export const makeAccountsRepositoryMock = (
  value: AccountsRepositoryMockValue,
) => {
  const { getLicenseSummaryInfo, createAccount } = value;
  return {
    getLicenseSummaryInfo:
      getLicenseSummaryInfo instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(getLicenseSummaryInfo)
        : jest
            .fn<
              Promise<{
                licenseSummary: LicenseSummaryInfo;
                isStorageAvailable: boolean;
              }>,
              []
            >()
            .mockResolvedValue(getLicenseSummaryInfo),
    createAccount:
      createAccount instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(createAccount)
        : jest
            .fn<Promise<{ newAccount: Account; adminUser: User }>, []>()
            .mockResolvedValue(createAccount),
  };
};
export const makeLicensesRepositoryMock = (
  value: LicensesRepositoryMockValue,
) => {
  const { getLicenseOrderHistoryInfo } = value;

  return {
    findUserById:
      getLicenseOrderHistoryInfo instanceof Error
        ? jest
            .fn<Promise<void>, []>()
            .mockRejectedValue(getLicenseOrderHistoryInfo)
        : jest
            .fn<
              Promise<{ total: number; orderHistories: LicenseOrder[] }>,
              []
            >()
            .mockResolvedValue(getLicenseOrderHistoryInfo),
  };
};
export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
  const { findUserById, findUserByExternalId, findTypistUsers } = value;

  return {
    findUserById:
      findUserById instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(findUserById)
        : jest.fn<Promise<User>, []>().mockResolvedValue(findUserById),
    findUserByExternalId:
      findUserByExternalId instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(findUserByExternalId)
        : jest.fn<Promise<User>, []>().mockResolvedValue(findUserByExternalId),
    findTypistUsers:
      findTypistUsers instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(findTypistUsers)
        : jest.fn<Promise<User[]>, []>().mockResolvedValue(findTypistUsers),
  };
};
export const makeUserGroupsRepositoryMock = (
  value: UserGroupsRepositoryMockValue,
) => {
  const { getUserGroups } = value;

  return {
    getUserGroups:
      getUserGroups instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(getUserGroups)
        : jest.fn<Promise<UserGroup[]>, []>().mockResolvedValue(getUserGroups),
  };
};
export const makeAdB2cServiceMock = (value: AdB2cMockValue) => {
  const { createUser, getUsers } = value;

  return {
    createUser:
      createUser instanceof Error
        ? jest.fn<Promise<ConflictError>, []>().mockRejectedValue(createUser)
        : jest
            .fn<Promise<string | ConflictError>, []>()
            .mockResolvedValue(createUser),
    getUsers:
      getUsers instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(getUsers)
        : jest.fn<Promise<AdB2cUser[]>, []>().mockResolvedValue(getUsers),
  };
};
export const makeConfigMock = (value: ConfigMockValue) => {
  const { get } = value;

  return {
    get:
      get instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(get)
        : jest.fn<Promise<string>, []>().mockResolvedValue(get),
  };
};
export const makeSendGridServiceMock = (value: SendGridMockValue) => {
  const {
    createMailContentFromEmailConfirm,
    createMailContentFromEmailConfirmForNormalUser,
    sendMail,
  } = value;
  return {
    createMailContentFromEmailConfirm:
      createMailContentFromEmailConfirm instanceof Error
        ? jest
            .fn<Promise<void>, []>()
            .mockRejectedValue(createMailContentFromEmailConfirm)
        : jest
            .fn<Promise<{ subject: string; text: string; html: string }>, []>()
            .mockResolvedValue(createMailContentFromEmailConfirm),
    createMailContentFromEmailConfirmForNormalUser:
      createMailContentFromEmailConfirmForNormalUser instanceof Error
        ? jest
            .fn<Promise<void>, []>()
            .mockRejectedValue(createMailContentFromEmailConfirmForNormalUser)
        : jest
            .fn<Promise<{ subject: string; text: string; html: string }>, []>()
            .mockResolvedValue(createMailContentFromEmailConfirmForNormalUser),
    sendMail:
      sendMail instanceof Error
        ? jest.fn<Promise<void>, []>().mockRejectedValue(sendMail)
        : jest.fn<Promise<void>, []>().mockResolvedValue(sendMail),
  };
};
// 個別のテストケースに対応してそれぞれのMockを用意するのは無駄が多いのでテストケース内で個別の値を設定する
export const makeDefaultAccountsRepositoryMockValue =
  (): AccountsRepositoryMockValue => {
    let licenseSummaryInfo = new LicenseSummaryInfo();
    licenseSummaryInfo = {
      totalLicense: 1,
      allocatedLicense: 2,
      reusableLicense: 3,
      freeLicense: 4,
      expiringSoonLicense: 5,
      issueRequesting: 6,
      numberOfRequesting: 7,
      allocatableLicenseWithMargin: 8,
    };
    const account = new Account();
    account.accountId = 1;
    const user = new User();
    user.id = 1;
    user.external_id = 'ede66c43-9b9d-4222-93ed-5f11c96e08e2';
    user.account_id = 1234567890123456;
    user.role = 'none admin';
    user.author_id = '6cce347f-0cf1-a15e-19ab-d00988b643f9';
    user.accepted_terms_version = '1.0';
    user.email_verified = true;
    user.auto_renew = false;
    user.license_alert = false;
    user.notification = false;
    user.deleted_at = null;
    user.created_by = 'test';
    user.created_at = new Date();
    user.updated_by = null;
    user.updated_at = null;
    return {
      getLicenseSummaryInfo: {
        licenseSummary: licenseSummaryInfo,
        isStorageAvailable: false,
      },
      createAccount: {
        newAccount: account,
        adminUser: user,
      },
    };
  };
export const makeDefaultUsersRepositoryMockValue =
  (): UsersRepositoryMockValue => {
    const user = new User();
    user.id = 1;
    user.external_id = 'ede66c43-9b9d-4222-93ed-5f11c96e08e2';
    user.account_id = 1234567890123456;
    user.role = 'none admin';
    user.author_id = '6cce347f-0cf1-a15e-19ab-d00988b643f9';
    user.accepted_terms_version = '1.0';
    user.email_verified = true;
    user.auto_renew = false;
    user.license_alert = false;
    user.notification = false;
    user.deleted_at = null;
    user.created_by = 'test';
    user.created_at = new Date();
    user.updated_by = null;
    user.updated_at = null;

    const typists: User[] = [];
    typists.push(
      {
        ...user,
        id: 1,
        external_id: 'typist1',
        role: 'typist',
      },
      {
        ...user,
        id: 2,
        external_id: 'typist2',
        role: 'typist',
      },
      {
        ...user,
        id: 3,
        external_id: 'typist3',
        role: 'typist',
      },
    );

    return {
      findUserById: user,
      findUserByExternalId: user,
      findTypistUsers: typists,
    };
  };

export const makeDefaultUserGroupsRepositoryMockValue =
  (): UserGroupsRepositoryMockValue => {
    const user = new User();
    user.id = 1;
    user.external_id = 'ede66c43-9b9d-4222-93ed-5f11c96e08e2';
    user.account_id = 1234567890123456;
    user.role = 'none admin';
    user.author_id = '6cce347f-0cf1-a15e-19ab-d00988b643f9';
    user.accepted_terms_version = '1.0';
    user.email_verified = true;
    user.auto_renew = false;
    user.license_alert = false;
    user.notification = false;
    user.deleted_at = null;
    user.created_by = 'test';
    user.created_at = new Date();
    user.updated_by = null;
    user.updated_at = null;

    return {
      getUserGroups: [
        {
          id: 1,
          account_id: 1,
          name: 'GroupA',
          created_by: 'test',
          updated_by: 'test',
        },
        {
          id: 2,
          account_id: 1,
          name: 'GroupB',
          created_by: 'test',
          updated_by: 'test',
        },
      ],
    };
  };
export const makeDefaultAdB2cMockValue = (): AdB2cMockValue => {
  return {
    createUser: '001',
    getUsers: [
      {
        id: 'typist1',
        displayName: 'Typist1',
      },
      {
        id: 'typist2',
        displayName: 'Typist2',
      },
      {
        id: 'typist3',
        displayName: 'Typist3',
      },
    ],
  };
};
export const makeDefaultSendGridlValue = (): SendGridMockValue => {
  return {
    sendMail: undefined,
    createMailContentFromEmailConfirm: { subject: '', text: '', html: '' },
    createMailContentFromEmailConfirmForNormalUser: {
      subject: 'Verify your new account',
      text: `The verification URL.`,
      html: `<p>The verification URL.<p>`,
    },
  };
};
export const makeDefaultLicensesRepositoryMockValue =
  (): LicensesRepositoryMockValue => {
    return {
      getLicenseOrderHistoryInfo: {
        total: 100,
        orderHistories: [
          {
            orderDate: '2023/04/01',
            issueDate: '2023/04/01',
            numberOfOrder: 10,
            poNumber: 'PO001',
            status: 'Issued',
          },
        ],
      },
    };
  };
