import { AccessToken } from '../../common/token';
import {
  CreateOrdersRequest,
  IssueCardLicensesRequest,
  IssueCardLicensesResponse,
  ActivateCardLicensesRequest,
} from './types/types';
import {
  makeDefaultAccountsRepositoryMockValue,
  makeDefaultLicensesRepositoryMockValue,
  makeDefaultUsersRepositoryMockValue,
  makeLicensesServiceMock,
} from './test/liscense.service.mock';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { HttpException, HttpStatus } from '@nestjs/common';
import {
  PoNumberAlreadyExistError,
  LicenseKeyAlreadyActivatedError,
  LicenseNotExistError,
} from '../../repositories/licenses/errors/types';
import { LicensesService } from './licenses.service';
import { makeTestingModule } from '../../common/test/modules';
import { DataSource } from 'typeorm';
import {
  createAccount,
  createUser,
  createCardLicense,
  createLicense,
  createCardLicenseIssue,
  selectCardLicensesCount,
  selectCardLicense,
  selectLicense,
} from './test/utility';

describe('LicensesService', () => {
  it('ライセンス注文が完了する', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new CreateOrdersRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.orderCount = 1000;
    body.poNumber = '1';
    expect(
      await service.licenseOrders(token, body.poNumber, body.orderCount),
    ).toEqual(undefined);
  });
  it('ユーザID取得できなかった場合、エラーとなる', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserByExternalId = new Error(
      'User not Found Error.',
    );
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new CreateOrdersRequest();
    const token: AccessToken = { userId: '', role: '', tier: 5 };
    body.orderCount = 1000;
    body.poNumber = '1';
    await expect(
      service.licenseOrders(token, body.poNumber, body.orderCount),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('親ユーザID取得できなかった場合、エラーとなる', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    usersRepositoryMockValue.findUserByExternalId = new Error(
      'Account not Found Error.',
    );
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new CreateOrdersRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.orderCount = 1000;
    body.poNumber = '1';
    await expect(
      service.licenseOrders(token, body.poNumber, body.orderCount),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('POナンバー重複時、エラーとなる', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.order = new PoNumberAlreadyExistError();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new CreateOrdersRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.orderCount = 1000;
    body.poNumber = '1';
    await expect(
      service.licenseOrders(token, body.poNumber, body.orderCount),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E010401'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('カードライセンス発行が完了する', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new IssueCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.createCount = 10;
    const issueCardLicensesResponse: IssueCardLicensesResponse = {
      cardLicenseKeys: [
        'WZCETXC0Z9PQZ9GKRGGY',
        'F0JD7EZEDBH4PQRQ83YF',
        'H0HXBP5K9RW7T7JSVDJV',
        'HKIWX54EESYL4X132223',
        '363E81JR460UBHXGFXFI',
        '70IKAPV9K6YMEVLTOXBY',
        '1RJY1TRRYYTGF1LL9WLU',
        'BXM0HKFO7IULTL0A1B36',
        'XYLEWNY2LR6Q657CZE41',
        'AEJWRFFSWRQYQQJ6WVLV',
      ],
    };
    expect(
      await service.issueCardLicenseKeys(token.userId, body.createCount),
    ).toEqual(issueCardLicensesResponse);
  });
  it('カードライセンス発行に失敗した場合、エラーになる', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.createCardLicenses = new Error('DB failed');
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new IssueCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.createCount = 1000;
    await expect(
      service.issueCardLicenseKeys(token.userId, body.createCount),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('カードライセンス取り込みが完了する', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new ActivateCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    expect(
      await service.activateCardLicenseKey(token.userId, body.cardLicenseKey),
    ).toEqual(undefined);
  });
  it('カードライセンス取り込みに失敗した場合、エラーになる（DBエラー）', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.activateCardLicense = new Error('DB failed');
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new ActivateCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    await expect(
      service.activateCardLicenseKey(token.userId, body.cardLicenseKey),
    ).rejects.toEqual(
      new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      ),
    );
  });
  it('カードライセンス取り込みに失敗した場合、エラーになる（ライセンスが存在しないエラー）', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.activateCardLicense =
      new LicenseNotExistError();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new ActivateCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    await expect(
      service.activateCardLicenseKey(token.userId, body.cardLicenseKey),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010801'), HttpStatus.BAD_REQUEST),
    );
  });
  it('カードライセンス取り込みに失敗した場合、エラーになる（ライセンスが既に取り込まれているエラー）', async () => {
    const lisencesRepositoryMockValue =
      makeDefaultLicensesRepositoryMockValue();
    lisencesRepositoryMockValue.activateCardLicense =
      new LicenseKeyAlreadyActivatedError();
    const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
    const accountsRepositoryMockValue =
      makeDefaultAccountsRepositoryMockValue();
    const service = await makeLicensesServiceMock(
      lisencesRepositoryMockValue,
      usersRepositoryMockValue,
      accountsRepositoryMockValue,
    );
    const body = new ActivateCardLicensesRequest();
    const token: AccessToken = { userId: '0001', role: '', tier: 5 };
    body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    await expect(
      service.activateCardLicenseKey(token.userId, body.cardLicenseKey),
    ).rejects.toEqual(
      new HttpException(makeErrorResponse('E010802'), HttpStatus.BAD_REQUEST),
    );
  });
});

describe('DBテスト', () => {
  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);

    const { accountId } = await createAccount(source);
    const { externalId } = await createUser(
      source,
      accountId,
      'userId',
      'admin',
    );

    const service = module.get<LicensesService>(LicensesService);
    const issueCount = 1000;
    await service.issueCardLicenseKeys(externalId, issueCount);
    const dbSelectResult = await selectCardLicensesCount(source);
    expect(dbSelectResult.count).toEqual(issueCount);
  });

  it('カードライセンス取り込みが完了する', async () => {
    const module = await makeTestingModule(source);

    const { accountId } = await createAccount(source);
    const { externalId } = await createUser(
      source,
      accountId,
      'userId',
      'admin',
    );

    const cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
    const defaultAccountId = 150;
    const licenseId = 50;
    const issueId = 100;

    await createLicense(source, licenseId, defaultAccountId);
    await createCardLicense(source, licenseId, issueId, cardLicenseKey);
    await createCardLicenseIssue(source, issueId);

    const service = module.get<LicensesService>(LicensesService);

    await service.activateCardLicenseKey(externalId, cardLicenseKey);
    const dbSelectResultFromCardLicense = await selectCardLicense(
      source,
      cardLicenseKey,
    );
    const dbSelectResultFromLicense = await selectLicense(source, licenseId);
    expect(
      dbSelectResultFromCardLicense.cardLicense.activated_at,
    ).toBeDefined();
    expect(dbSelectResultFromLicense.license.account_id).toEqual(accountId);
  });
});
