import { HttpException, HttpStatus } from '@nestjs/common';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import {
  makeAccountsServiceMock,
  makeDefaultAccountsRepositoryMockValue,
  makeDefaultAdB2cMockValue,
  makeDefaultLicensesRepositoryMockValue,
  makeDefaultSendGridlValue,
  makeDefaultUserGroupsRepositoryMockValue,
  makeDefaultUsersRepositoryMockValue,
} from './test/accounts.service.mock';
import { makeDefaultConfigValue } from '../users/test/users.service.mock';
import {
  createAccount,
  createLicense,
  createLicenseOrder,
} from './test/utility';
import { DataSource } from 'typeorm';
import { makeTestingModule } from '../../common/test/modules';
import { AccountsService } from './accounts.service';

describe('AccountsService', () => {
  it('アカウントに紐づくライセンス情報を取得する', async () => {
    const accountId = 1;
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );
    expect(await service.getLicenseSummary(accountId)).toEqual(
      expectedAccountLisenceCounts,
    );
  });

  it('ライセンス情報が取得できない場合、エラーとなる', async () => {
    const accountId = 1;
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    accountsRepositoryMockValue.getLicenseSummaryInfo = null;
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );
    await expect(service.getLicenseSummary(accountId)).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  it('アカウントに紐づくタイピストユーザーを取得できる', async () => {
    const externalId = 'externalId';
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );
    expect(await service.getTypists(externalId)).toEqual([
      { id: 1, name: 'Typist1' },
      { id: 2, name: 'Typist2' },
      { id: 3, name: 'Typist3' },
    ]);
  });
  it('ユーザー取得に失敗した場合、エラーとなる', async () => {
    const externalId = 'externalId';
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findTypistUsers = new Error();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );
    await expect(service.getTypists(externalId)).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('ADB2Cからのユーザーの取得に失敗した場合、エラーとなる', async () => {
    const externalId = 'externalId';
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    adb2cParam.getUsers = new Error();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );
    await expect(service.getTypists(externalId)).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });

  it('アカウントに紐づくユーザーグループを取得できる', async () => {
    const externalId = 'externalId';
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );

    expect(await service.getTypistGroups(externalId)).toEqual([
      { id: 1, name: 'GroupA' },
      { id: 2, name: 'GroupB' },
    ]);
  });
  it('ユーザーの取得に失敗した場合、エラーとなること', async () => {
    const externalId = 'externalId';
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserByExternalId = new Error('DB failed');
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );

    await expect(service.getTypistGroups(externalId)).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('ユーザーグループの取得に失敗した場合、エラーとなること', async () => {
    const externalId = 'externalId';
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    userGroupsRepositoryMockValue.getUserGroups = new Error('DB failed');
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );

    await expect(service.getTypistGroups(externalId)).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('パートナーを追加できる', async () => {
    const companyName = 'TEST_COMPANY';
    const country = 'US';
    const email = 'xxx@example.com';
    const adminName = 'ADMIN';
    const userId = '100';
    const tier = 3;
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );
    expect(
      await service.createPartnerAccount(
        companyName,
        country,
        email,
        adminName,
        userId,
        tier + 1,
      ),
    ).toEqual(undefined);
  });
  it('アカウントの追加に失敗した場合、エラーとなる', async () => {
    const companyName = 'TEST_COMPANY';
    const country = 'US';
    const email = 'xxx@example.com';
    const adminName = 'ADMIN';
    const userId = '100';
    const tier = 3;
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    accountsRepositoryMockValue.createAccount = new Error('DB failed');
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );
    await expect(
      service.createPartnerAccount(
        companyName,
        country,
        email,
        adminName,
        userId,
        tier + 1,
      ),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
});

const expectedAccountLisenceCounts = {
  totalLicense: 1,
  allocatedLicense: 2,
  reusableLicense: 3,
  freeLicense: 4,
  expiringWithin14daysLicense: 5,
  issueRequesting: 6,
  numberOfRequesting: 7,
  shortage: 0,
  storageSize: 0,
  usedSize: 0,
  isStorageAvailable: false,
};

describe('createPartnerAccount', () => {
  let source: DataSource = null;
  beforeEach(async () => {
    source = new DataSource({
      type: 'sqlite',
      database: ':memory:',
      logging: false,
      entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
      synchronize: true, // trueにすると自動的にmigrationが行われるため注意
    });
    return source.initialize();
  });

  afterEach(async () => {
    await source.destroy();
    source = null;
  });

  it('パラメータのアカウント自身と子アカウントに紐つくライセンス情報を取得する', async () => {
    const module = await makeTestingModule(source);

    // 親アカウントと子アカウント2つ作成
    const { accountId: parentAccountId } = await createAccount(
      source,
      0,
      1,
      'PARENTCORP',
    );
    const { accountId: childAccountId1 } = await createAccount(
      source,
      parentAccountId,
      2,
      'CHILDCORP1',
    );
    const { accountId: childAccountId2 } = await createAccount(
      source,
      parentAccountId,
      2,
      'CHILDCORP2',
    );

    // 所有ライセンスを追加（親：3、子1：1、子2：2）
    await createLicense(source, parentAccountId);
    await createLicense(source, parentAccountId);
    await createLicense(source, parentAccountId);

    await createLicense(source, childAccountId1);

    await createLicense(source, childAccountId2);
    await createLicense(source, childAccountId2);

    // ライセンス注文を追加（子1→親：10ライセンス、子2→親：5ライセンス）
    await createLicenseOrder(
      source,
      childAccountId1,
      parentAccountId,
      10,
      'TEST222',
    );
    await createLicenseOrder(source, childAccountId2, parentAccountId, 5);

    const service = module.get<AccountsService>(AccountsService);
    const accountId = parentAccountId;
    const offset = 0;
    const limit = 20;

    const responce = await service.getPartnerLicenses(limit, offset, accountId);

    expect(responce.total).toBe(2);

    expect(responce.ownPartnerLicense.companyName).toBe('PARENTCORP');
    expect(responce.ownPartnerLicense.tier).toBe(1);
    expect(responce.ownPartnerLicense.stockLicense).toBe(3);
    expect(responce.ownPartnerLicense.issuedRequested).toBe(15);
    expect(responce.ownPartnerLicense.shortage).toBe(12);

    expect(responce.childrenPartnerLicenses[0].companyName).toBe('CHILDCORP1');
    expect(responce.childrenPartnerLicenses[0].tier).toBe(2);
    expect(responce.childrenPartnerLicenses[0].stockLicense).toBe(1);
    expect(responce.childrenPartnerLicenses[0].issueRequesting).toBe(10);

    expect(responce.childrenPartnerLicenses[1].companyName).toBe('CHILDCORP2');
    expect(responce.childrenPartnerLicenses[1].tier).toBe(2);
    expect(responce.childrenPartnerLicenses[1].stockLicense).toBe(2);
    expect(responce.childrenPartnerLicenses[1].issueRequesting).toBe(5);
  });
});

describe('getOrderHistories', () => {
  let source: DataSource = null;
  beforeEach(async () => {
    source = new DataSource({
      type: 'sqlite',
      database: ':memory:',
      logging: false,
      entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
      synchronize: true, // trueにすると自動的にmigrationが行われるため注意
    });
    return source.initialize();
  });

  afterEach(async () => {
    await source.destroy();
    source = null;
  });

  it('指定したアカウントIDの注文履歴情報を取得できる', async () => {
    const module = await makeTestingModule(source);

    const targetAccountId = 10;
    const targetParentAccountId = 14;
    const now = new Date(); // 現在の時刻を取得

    // 注文履歴情報の作成
    await createLicenseOrder(
      source,
      targetAccountId,
      targetParentAccountId,
      10,
      'TEST001',
      new Date(now.getTime() + 60 * 60 * 1000),
    );
    await createLicenseOrder(
      source,
      targetAccountId,
      targetParentAccountId,
      10,
      'TEST002',
      new Date(now.getTime() + 60 * 60 * 1000 * 2),
    );
    await createLicenseOrder(
      source,
      targetAccountId,
      targetParentAccountId,
      10,
      'TEST003',
      new Date(now.getTime() + 60 * 60 * 1000 * 3),
    );
    await createLicenseOrder(
      source,
      targetAccountId,
      targetParentAccountId,
      10,
      'TEST004',
      new Date(now.getTime() + 60 * 60 * 1000 * 4),
    );
    await createLicenseOrder(
      source,
      targetAccountId,
      targetParentAccountId,
      10,
      'TEST005',
      new Date(now.getTime() + 60 * 60 * 1000 * 5),
    );

    const service = module.get<AccountsService>(AccountsService);
    const accountId = targetAccountId;
    const offset = 1;
    const limit = 2;

    const responce = await service.getOrderHistories(limit, offset, accountId);

    expect(responce.total).toBe(5);

    expect(responce.orderHistories[0].poNumber).toBe('TEST004');
    expect(responce.orderHistories[1].poNumber).toBe('TEST003');
  });
  it('注文履歴情報の取得に失敗した場合、エラーとなる', async () => {
    const limit = 0;
    const offset = 0;
    const accountId = 0;
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findTypistUsers = new Error();
    const userGroupsRepositoryMockValue =
      makeDefaultUserGroupsRepositoryMockValue();
    const adb2cParam = makeDefaultAdB2cMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const configMockValue = makeDefaultConfigValue();
    const sendGridMockValue = makeDefaultSendGridlValue();
    const licensesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    licensesRepositoryMockValue.getLicenseOrderHistoryInfo = new Error(
      'DB failed',
    );
    const service = await makeAccountsServiceMock(
      accountsRepositoryMockValue,
      usersRepositoryMockValue,
      userGroupsRepositoryMockValue,
      adb2cParam,
      configMockValue,
      sendGridMockValue,
      licensesRepositoryMockValue,
    );
    await expect(
      service.getOrderHistories(limit, offset, accountId),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
});
