import { Injectable } from '@angular/core';

import { Observable, Subject } from 'rxjs';
import { map, take, takeWhile, share, filter, tap } from 'rxjs/operators';

import { ProtocolService } from '@ucap-webmessenger/protocol';
import {
  StatusRequest,
  encodeStatus,
  decodeStatus,
  StatusResponse,
  decodeStatusNotification,
  StatusNotification
} from '../protocols/status';
import {
  SVC_TYPE_STATUS,
  SSVC_TYPE_STATUS_REQ,
  SSVC_TYPE_STATUS_BULK_INFO_REQ,
  SSVC_TYPE_STATUS_BULK_INFO_RES,
  SSVC_TYPE_STATUS_BULK_INFO_DATA,
  SSVC_TYPE_STATUS_BUDDY_REQ,
  SSVC_TYPE_STATUS_MSG_UPD_REQ,
  SSVC_TYPE_STATUS_SUBSCRIPT_REQ,
  SSVC_TYPE_STATUS_UNSUBSCRIPT_REQ,
  SSVC_TYPE_STATUS_NOTI
} from '../types/service';
import {
  BulkInfoRequest,
  BulkInfoResponse,
  BulkInfoData,
  encodeBulkInfo,
  decodeBulkInfo,
  decodeBulkInfoData
} from '../protocols/bulk-info';
import { BuddyResponse, decodeBuddy } from '../protocols/buddy';
import {
  MessageUpdateRequest,
  MessageUpdateResponse,
  encodeMessageUpdate,
  decodeMessageUpdate
} from '../protocols/message-update';
import {
  SubscribeRequest,
  SubscribeResponse,
  decodeSubscribe,
  encodeSubscribe
} from '../protocols/subscribe';
import {
  UnSubscribeRequest,
  encodeUnSubscribe
} from '../protocols/unsubscribe';

type Notifications = StatusNotification;

@Injectable({
  providedIn: 'root'
})
export class StatusProtocolService {
  private notificationSubject: Subject<Notifications>;
  public notification$: Observable<Notifications>;

  constructor(private protocolService: ProtocolService) {
    this.notificationSubject = new Subject();
    this.notification$ = this.notificationSubject.asObservable().pipe(share());

    this.protocolService.serverMessage
      .pipe(
        filter(message => message.serviceType === SVC_TYPE_STATUS),
        tap(message => {
          switch (message.subServiceType) {
            case SSVC_TYPE_STATUS_NOTI:
              {
                this.notificationSubject.next(
                  decodeStatusNotification(message)
                );
              }
              break;

            default:
              break;
          }
        })
      )
      .subscribe();
  }

  public status(req: StatusRequest): Observable<StatusResponse> {
    return this.protocolService
      .call(SVC_TYPE_STATUS, SSVC_TYPE_STATUS_REQ, ...encodeStatus(req))
      .pipe(
        take(1),
        map(res => {
          return decodeStatus(res);
        })
      );
  }

  public buddy(): Observable<BuddyResponse> {
    return this.protocolService
      .call(SVC_TYPE_STATUS, SSVC_TYPE_STATUS_BUDDY_REQ)
      .pipe(
        take(1),
        map(res => {
          return decodeBuddy(res);
        })
      );
  }

  public bulkInfo(
    req: BulkInfoRequest
  ): Observable<BulkInfoResponse | BulkInfoData> {
    return this.protocolService
      .call(
        SVC_TYPE_STATUS,
        SSVC_TYPE_STATUS_BULK_INFO_REQ,
        ...encodeBulkInfo(req)
      )
      .pipe(
        takeWhile(
          res => SSVC_TYPE_STATUS_BULK_INFO_RES !== res.subServiceType,
          true
        ),
        map(res => {
          if (SSVC_TYPE_STATUS_BULK_INFO_DATA === res.subServiceType) {
            return decodeBulkInfoData(res);
          }
          return decodeBulkInfo(res);
        })
      );
  }

  public messageUpdate(
    req: MessageUpdateRequest
  ): Observable<MessageUpdateResponse> {
    return this.protocolService
      .call(
        SVC_TYPE_STATUS,
        SSVC_TYPE_STATUS_MSG_UPD_REQ,
        ...encodeMessageUpdate(req)
      )
      .pipe(
        take(1),
        map(res => {
          return decodeMessageUpdate(res);
        })
      );
  }

  /**
   * Only Use 1:1 Chat Room and must be using unsubscribe method.
   * @param req SubscribeRequest
   */
  public subscribe(req: SubscribeRequest): Observable<SubscribeResponse> {
    return this.protocolService
      .call(
        SVC_TYPE_STATUS,
        SSVC_TYPE_STATUS_SUBSCRIPT_REQ,
        ...encodeSubscribe(req)
      )
      .pipe(
        take(1),
        map(res => {
          return decodeSubscribe(res);
        })
      );
  }

  /**
   * Only Use 1:1 Chat Room and must be using subscribe method.
   * @param req UnSubscribeRequest
   */
  public unSubscribe(req: UnSubscribeRequest): void {
    return this.protocolService.send(
      SVC_TYPE_STATUS,
      SSVC_TYPE_STATUS_UNSUBSCRIPT_REQ,
      ...encodeUnSubscribe(req)
    );
  }
}
