import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { AccessToken } from '../../common/token';
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
import {
  PoNumberAlreadyExistError,
  LicenseNotExistError,
  LicenseKeyAlreadyActivatedError,
} from '../../repositories/licenses/errors/types';
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
import { UserNotFoundError } from '../../repositories/users/errors/types';
import { IssueCardLicensesResponse } from './types/types';

@Injectable()
export class LicensesService {
  constructor(
    private readonly usersRepository: UsersRepositoryService,
    private readonly accountsRepository: AccountsRepositoryService,
    private readonly licensesRepository: LicensesRepositoryService,
  ) {}
  private readonly logger = new Logger(LicensesService.name);

  /**
   * license Orders
   * @param token
   * @param body
   */
  async licenseOrders(
    accessToken: AccessToken,
    poNumber: string,
    orderCount: number,
  ): Promise<void> {
    //アクセストークンからユーザーIDを取得する
    this.logger.log(`[IN] ${this.licenseOrders.name}`);
    const userId = accessToken.userId;
    let myAccountId: number;
    let parentAccountId: number;

    // ユーザIDからアカウントIDを取得する
    try {
      myAccountId = (await this.usersRepository.findUserByExternalId(userId))
        .account_id;
    } catch (e) {
      this.logger.error(`error=${e}`);
      switch (e.constructor) {
        case UserNotFoundError:
          throw new HttpException(
            makeErrorResponse('E010204'),
            HttpStatus.BAD_REQUEST,
          );
        default:
          throw new HttpException(
            makeErrorResponse('E009999'),
            HttpStatus.INTERNAL_SERVER_ERROR,
          );
      }
    }

    // 親アカウントIDを取得
    try {
      parentAccountId = (
        await this.accountsRepository.findAccountById(myAccountId)
      ).parent_account_id;
    } catch (e) {
      this.logger.error(`error=${e}`);
      switch (e.constructor) {
        case AccountNotFoundError:
          throw new HttpException(
            makeErrorResponse('E010501'),
            HttpStatus.BAD_REQUEST,
          );
        default:
          throw new HttpException(
            makeErrorResponse('E009999'),
            HttpStatus.INTERNAL_SERVER_ERROR,
          );
      }
    }

    try {
      await this.licensesRepository.order(
        poNumber,
        myAccountId,
        parentAccountId,
        orderCount,
      );
    } catch (e) {
      switch (e.constructor) {
        case PoNumberAlreadyExistError:
          throw new HttpException(
            makeErrorResponse('E010401'),
            HttpStatus.BAD_REQUEST,
          );
        default:
          throw new HttpException(
            makeErrorResponse('E009999'),
            HttpStatus.INTERNAL_SERVER_ERROR,
          );
      }
    }
  }
  async issueCardLicenseKeys(
    externalId: string,
    createCount: number,
  ): Promise<IssueCardLicensesResponse> {
    const issueCardLicensesResponse = new IssueCardLicensesResponse();
    let myAccountId: number;

    // ユーザIDからアカウントIDを取得する
    try {
      myAccountId = (
        await this.usersRepository.findUserByExternalId(externalId)
      ).account_id;
    } catch (e) {
      this.logger.error(`error=${e}`);
      switch (e.constructor) {
        case UserNotFoundError:
          throw new HttpException(
            makeErrorResponse('E010204'),
            HttpStatus.BAD_REQUEST,
          );
        default:
          throw new HttpException(
            makeErrorResponse('E009999'),
            HttpStatus.INTERNAL_SERVER_ERROR,
          );
      }
    }
    try {
      const licenseKeys = await this.licensesRepository.createCardLicenses(
        myAccountId,
        createCount,
      );
      issueCardLicensesResponse.cardLicenseKeys = licenseKeys;
      return issueCardLicensesResponse;
    } catch (e) {
      this.logger.error(`error=${e}`);
      this.logger.error('get cardlicensekeys failed');
      throw new HttpException(
        makeErrorResponse('E009999'),
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  /**
   * card license activate
   * @param externalId
   * @param cardLicenseKey
   */
  async activateCardLicenseKey(
    externalId: string,
    cardLicenseKey: string,
  ): Promise<void> {
    this.logger.log(
      `[IN] ${this.activateCardLicenseKey.name}, argCardLicenseKey: ${cardLicenseKey}`,
    );
    let myAccountId: number;

    // ユーザIDからアカウントIDを取得する
    try {
      myAccountId = (
        await this.usersRepository.findUserByExternalId(externalId)
      ).account_id;
    } catch (e) {
      this.logger.error(`error=${e}`);
      switch (e.constructor) {
        case UserNotFoundError:
          throw new HttpException(
            makeErrorResponse('E010204'),
            HttpStatus.BAD_REQUEST,
          );
        default:
          throw new HttpException(
            makeErrorResponse('E009999'),
            HttpStatus.INTERNAL_SERVER_ERROR,
          );
      }
    }

    // カードライセンスを取り込む
    try {
      await this.licensesRepository.activateCardLicense(
        myAccountId,
        cardLicenseKey,
      );
    } catch (e) {
      this.logger.error(`error=${e}`);
      this.logger.error('cardLicenseKey activate failed');

      switch (e.constructor) {
        case LicenseNotExistError:
          throw new HttpException(
            makeErrorResponse('E010801'),
            HttpStatus.BAD_REQUEST,
          );
        case LicenseKeyAlreadyActivatedError:
          throw new HttpException(
            makeErrorResponse('E010802'),
            HttpStatus.BAD_REQUEST,
          );
        default:
          throw new HttpException(
            makeErrorResponse('E009999'),
            HttpStatus.INTERNAL_SERVER_ERROR,
          );
      }
    }
    this.logger.log(`[OUT] ${this.activateCardLicenseKey.name}`);
    return;
  }
}
