import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
  NotificationHubsClient,
  createAppleNotificationBody,
  createAppleNotification,
  createTagExpression,
  createWindowsToastNotification,
  createWindowsInstallation,
  createAppleInstallation,
} from '@azure/notification-hubs';
import { TAG_MAX_COUNT } from '../../constants';
import { PNS } from '../../constants';
import { Context } from '../../common/log';
@Injectable()
export class NotificationhubService {
  private readonly logger = new Logger(NotificationhubService.name);
  private readonly client: NotificationHubsClient;
  constructor(private readonly configService: ConfigService) {
    this.client = new NotificationHubsClient(
      this.configService.get<string>('NOTIFICATION_HUB_CONNECT_STRING'),
      this.configService.get<string>('NOTIFICATION_HUB_NAME'),
    );
  }

  /**
   * Registers notificationhub service
   * @param pns
   * @param pnsHandler
   * @returns register
   */
  async register(
    context: Context,
    userId: number,
    pns: string,
    pnsHandler: string,
    installationId: string,
  ): Promise<void> {
    this.logger.log(
      `[IN] [${context.trackingId}] ${this.register.name} | params: { userId: ${userId}, pns: ${pns}, pnsHandler: ${pnsHandler}, installationId: ${installationId} }`,
    );

    const tag = `user_${userId}`;

    //登録情報作成
    const installation = {
      // XXX 登録の有効期限を設定したい場合
      //expirationTime: '',
      installationId: installationId,
      pushChannel: pnsHandler,
      tags: [tag],
    };

    try {
      switch (pns) {
        case PNS.WNS:
          await this.client.createOrUpdateInstallation(
            createWindowsInstallation(installation),
          );
          break;
        case PNS.APNS:
          await this.client.createOrUpdateInstallation(
            createAppleInstallation(installation),
          );
          break;
        default:
          throw new Error('invalid pns');
      }
    } catch (e) {
      this.logger.error(`error=${e.message}`);
      throw e;
    } finally {
      this.logger.log(`[OUT] [${context.trackingId}] ${this.register.name}`);
    }
  }

  /**
   * 指定したタグのユーザーに通知を送信する
   * @param tags
   * @param message
   * @returns notify
   */
  async notify(
    context: Context,
    tags: string[],
    message: string,
  ): Promise<void> {
    this.logger.log(
      `[IN] [${context.trackingId}] ${this.notify.name} | params: { tags: ${tags}, message: ${message} }`,
    );

    try {
      // OR条件によるtag指定は20個までなので分割して送信する
      const chunkTags = splitArrayInChunks(tags, TAG_MAX_COUNT);

      for (let index = 0; index < chunkTags.length; index++) {
        const currentTags = chunkTags[index];
        const tagExpression = createTagExpression(currentTags);

        // Windows
        try {
          const body = `<toast><visual><binding template="ToastText01"><text id="1">${message}</text></binding></visual></toast>`;
          const notification = createWindowsToastNotification({ body });
          const result = await this.client.sendNotification(notification, {
            tagExpression,
          });
          this.logger.log(result);
        } catch (e) {
          this.logger.error(`error=${e}`);
        }
        // Apple
        try {
          const body = createAppleNotificationBody({ aps: { alert: message } });
          const notification = createAppleNotification({ body });
          const result = await this.client.sendNotification(notification, {
            tagExpression,
          });
          this.logger.log(result);
        } catch (e) {
          this.logger.error(`error=${e}`);
        }
      }
    } catch (e) {
      throw e;
    } finally {
      this.logger.log(`[OUT] ${this.notify.name}`);
    }
  }
}

const splitArrayInChunks = (arr: string[], size: number): string[][] => {
  const result: string[][] = [];
  for (let i = 0; i < arr.length; i += size) {
    result.push(arr.slice(i, i + size));
  }
  return result;
};
