import { Injectable, Inject } from '@angular/core';
import {
  HttpClient,
  HttpHeaders,
  HttpRequest,
  HttpResponse,
  HttpEventType
} from '@angular/common/http';

import { Observable } from 'rxjs';
import { map, filter } from 'rxjs/operators';

import { _MODULE_CONFIG } from '../config/token';
import { ModuleConfig } from '../config/module-config';
import { UrlConfig } from '@ucap-webmessenger/core';
import { Urls } from '../config/urls';
import {
  RetrieveRequest,
  RetrieveResponse,
  encodeRetrieve,
  decodeRetrieveSend,
  decodeRetrieveReceive,
  decodeRetrieveReservation,
  RetrieveSearchRequest,
  decodeRetrieveSearch
} from '../apis/retrieve';
import {
  SendRequest,
  SendResponse,
  encodeSend,
  decodeSend
} from '../apis/send';
import {
  DetailRequest,
  DetailResponse,
  encodeDetail,
  decodeDetail,
  ReadRequest,
  ReadResponse,
  encodeRead,
  decodeRead,
  ReadCheckResponse,
  decodeReadCheck,
  CancelRequest,
  CancelResponse,
  encodeCancel,
  decodeCancel,
  CancelReservationRequest,
  CancelReservationResponse,
  encodeCancelReservation,
  decodeCancelReservation,
  RetrieveResourceFileRequest,
  RetrieveResourceFileResponse,
  encodeRetrieveResourceFile,
  decodeRetrieveResourceFile
} from '../apis/detail';
import { DelRequest, DelResponse, decodeDel, encodeDel } from '../apis/del';
import {
  SaveMyRequest,
  SaveMyResponse,
  encodeSaveMy,
  decodeSaveMy,
  RetrieveMyRequest,
  RetrieveMyResponse,
  encodeRetrieveMy,
  decodeRetrieveMy,
  DelMyRequest,
  DelMyResponse,
  encodeDelMy,
  decodeDelMy,
  EditMyRequest,
  EditMyResponse,
  encodeEditMy,
  decodeEditMy
} from '../apis/my-message';
import {
  EditReservationRequest,
  EditReservationResponse,
  encodeEditReservation,
  decodeEditReservation
} from '../apis/edit-reservation-ex';
import {
  SendCopyRequest,
  SendCopyResponse,
  encodeSendCopy,
  decodeSendCopy
} from '../apis/send-copy';
import {
  UnreadCountRequest,
  UnreadCountResponse,
  encodeUnreadCount,
  decodeUnreadCount
} from '../apis/unread-count';
import {
  RetrieveNoticeRequest,
  RetrieveNoticeResponse,
  encodeRetrieveNotice,
  decodeRetrieveNotice
} from '../apis/notice';

@Injectable({
  providedIn: 'root'
})
export class MessageApiService {
  readonly urls: Urls;

  headers: HttpHeaders;

  constructor(
    @Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig,
    private httpClient: HttpClient
  ) {
    this.urls = UrlConfig.getUrls(
      this.moduleConfig.hostConfig,
      this.moduleConfig.urls
    );

    this.headers = new HttpHeaders({
      'Content-Type': 'application/json; charset=utf-8'
    });
  }

  /** retrieve */
  public retrieveSendMessage(
    req: RetrieveRequest
  ): Observable<RetrieveResponse> {
    return this.httpClient
      .post<any>(this.urls.retrieveSendMessageList, encodeRetrieve(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeRetrieveSend(res)));
  }
  public retrieveReceiveMessage(
    req: RetrieveRequest
  ): Observable<RetrieveResponse> {
    return this.httpClient
      .post<any>(this.urls.retrieveRecvMessageList, encodeRetrieve(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeRetrieveReceive(res)));
  }
  public retrieveReservationMessage(
    req: RetrieveRequest
  ): Observable<RetrieveResponse> {
    return this.httpClient
      .post<any>(
        this.urls.retrieveReservationMessageList,
        encodeRetrieve(req),
        {
          headers: this.headers
        }
      )
      .pipe(map(res => decodeRetrieveReservation(res)));
  }
  public retrieveSearchMessage(
    req: RetrieveSearchRequest
  ): Observable<RetrieveResponse> {
    return this.httpClient
      .post<any>(this.urls.retrieveSearchMessage, encodeRetrieve(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeRetrieveSearch(res)));
  }

  /** send */
  public sendMessage(req: SendRequest): Observable<SendResponse> {
    const httpReq = new HttpRequest(
      'POST',
      this.urls.sendNewMessage,
      encodeSend(req),
      { reportProgress: true }
    );

    const progress = req.fileUploadItem.uploadStart();

    return this.httpClient.request(httpReq).pipe(
      filter(event => {
        if (event instanceof HttpResponse) {
          return true;
        } else if (HttpEventType.UploadProgress === event.type) {
          progress.next(Math.round((100 * event.loaded) / event.total));
        }
        return false;
      }),
      map((event: HttpResponse<any>) => {
        req.fileUploadItem.uploadComplete();
        return decodeSend(event.body);
      })
    );
  }
  /** edit reservation */
  public editReservationMessageEx(
    req: EditReservationRequest
  ): Observable<EditReservationResponse> {
    const httpReq = new HttpRequest(
      'POST',
      this.urls.editReservationMessageEx,
      encodeEditReservation(req),
      { reportProgress: true }
    );

    const progress = req.fileUploadItem.uploadStart();

    return this.httpClient.request(httpReq).pipe(
      filter(event => {
        if (event instanceof HttpResponse) {
          return true;
        } else if (HttpEventType.UploadProgress === event.type) {
          progress.next(Math.round((100 * event.loaded) / event.total));
        }
        return false;
      }),
      map((event: HttpResponse<any>) => {
        req.fileUploadItem.uploadComplete();
        return decodeSend(event.body);
      })
    );
  }

  /** detail */
  public detailMessage(req: DetailRequest): Observable<DetailResponse> {
    return this.httpClient
      .post<any>(this.urls.retrieveMessageDetail, encodeDetail(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeDetail(res)));
  }
  public readMessage(req: ReadRequest): Observable<ReadResponse> {
    return this.httpClient
      .post<any>(this.urls.readMessage, encodeRead(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeRead(res)));
  }
  public retrieveReadCheck(req: ReadRequest): Observable<ReadCheckResponse> {
    return this.httpClient
      .post<any>(this.urls.retrieveReadCheck, encodeRead(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeReadCheck(res)));
  }
  public cancelMessage(req: CancelRequest): Observable<CancelResponse> {
    return this.httpClient
      .post<any>(this.urls.cancelMessage, encodeCancel(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeCancel(res)));
  }
  public cancelReservationMessage(
    req: CancelReservationRequest
  ): Observable<CancelReservationResponse> {
    return this.httpClient
      .post<any>(
        this.urls.cancelReservationMessage,
        encodeCancelReservation(req),
        {
          headers: this.headers
        }
      )
      .pipe(map(res => decodeCancelReservation(res)));
  }
  public retrieveResourceFile(
    req: RetrieveResourceFileRequest
  ): Observable<Blob> {
    const httpReq = new HttpRequest(
      'POST',
      this.urls.retrieveResourceFile,
      encodeRetrieveResourceFile(req),
      {
        headers: this.headers,
        reportProgress: true,
        responseType: 'blob'
      }
    );
    return this.httpClient.request(httpReq).pipe(
      filter(event => {
        if (event instanceof HttpResponse) {
          return true;
        } else if (HttpEventType.DownloadProgress === event.type) {
        }
        return false;
      }),
      map((event: HttpResponse<any>) => {
        return event.body;
      })
    );
  }

  /** del */
  public deleteMessage(req: DelRequest): Observable<DelResponse> {
    return this.httpClient
      .post<any>(this.urls.deleteMessage, encodeDel(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeDel(res)));
  }

  /** save my message */
  public saveMyMessage(req: SaveMyRequest): Observable<SaveMyResponse> {
    return this.httpClient
      .post<any>(this.urls.saveMyMessage, encodeSaveMy(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeSaveMy(res)));
  }
  public retrieveMyMessage(
    req: RetrieveMyRequest
  ): Observable<RetrieveMyResponse> {
    return this.httpClient
      .post<any>(this.urls.retrieveMyMessage, encodeRetrieveMy(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeRetrieveMy(res)));
  }
  public deleteMyMessage(req: DelMyRequest): Observable<DelMyResponse> {
    return this.httpClient
      .post<any>(this.urls.deleteMyMessage, encodeDelMy(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeDelMy(res)));
  }
  public editMyMessage(req: EditMyRequest): Observable<EditMyResponse> {
    return this.httpClient
      .post<any>(this.urls.editMyMessage, encodeEditMy(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeEditMy(res)));
  }

  /** send-copy(forward) */
  public sendCopyMessage(req: SendCopyRequest): Observable<SendCopyResponse> {
    return this.httpClient
      .post<any>(this.urls.sendCopyMessage, encodeSendCopy(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeSendCopy(res)));
  }

  /** unread count */
  public retrieveUnreadCount(
    req: UnreadCountRequest
  ): Observable<UnreadCountResponse> {
    return this.httpClient
      .post<any>(this.urls.retrieveUnreadCount, encodeUnreadCount(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeUnreadCount(res)));
  }

  /** NoticeList */
  public retrieveNotice(
    req: RetrieveNoticeRequest
  ): Observable<RetrieveNoticeResponse> {
    return this.httpClient
      .post<any>(this.urls.retrieveNoticeList, encodeRetrieveNotice(req), {
        headers: this.headers
      })
      .pipe(map(res => decodeRetrieveNotice(res)));
  }
}
