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

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

import { ProtocolService } from '@ucap-webmessenger/protocol';
import {
  SVC_TYPE_INFO,
  SSVC_TYPE_INFO_USER_REQ,
  SSVC_TYPE_INFO_STATE_REQ,
  SSVC_TYPE_INFO_USER_OPTION_REQ,
  SSVC_TYPE_INFO_USER_OPTION_UPD_REQ,
  SSVC_TYPE_INFO_USER_NOTI
} from '../types/service';
import {
  UserRequest,
  encodeUser,
  UserResponse,
  decodeUser,
  decodeUserNotification,
  UserNotification
} from '../protocols/user';
import {
  StatusRequest,
  StatusResponse,
  encodeStatus,
  decodeStatus
} from '../protocols/status';
import {
  UserOptionResponse,
  decodeUserOption,
  UserOptionUpdateRequest,
  encodeUserOptionUpdate,
  UserOptionUpdateResponse,
  decodeUserOptionUpdate
} from '../protocols/user-option';

type Notifications = UserNotification;

@Injectable({
  providedIn: 'root'
})
export class InfoProtocolService {
  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_INFO),
        tap(message => {
          switch (message.subServiceType) {
            case SSVC_TYPE_INFO_USER_NOTI:
              {
                this.notificationSubject.next(decodeUserNotification(message));
              }
              break;

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

  public user(req: UserRequest): Observable<UserResponse> {
    return this.protocolService
      .call(SVC_TYPE_INFO, SSVC_TYPE_INFO_USER_REQ, ...encodeUser(req))
      .pipe(
        take(1),
        map(res => decodeUser(res))
      );
  }

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

  public userOption(): Observable<UserOptionResponse> {
    return this.protocolService
      .call(SVC_TYPE_INFO, SSVC_TYPE_INFO_USER_OPTION_REQ)
      .pipe(
        take(1),
        map(res => decodeUserOption(res))
      );
  }

  public userOptionUpdate(
    req: UserOptionUpdateRequest
  ): Observable<UserOptionUpdateResponse> {
    return this.protocolService
      .call(
        SVC_TYPE_INFO,
        SSVC_TYPE_INFO_USER_OPTION_UPD_REQ,
        ...encodeUserOptionUpdate(req)
      )
      .pipe(
        take(1),
        map(res => decodeUserOptionUpdate(res))
      );
  }
}
