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

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

import {
  FileProfileSaveRequest,
  FileProfileSaveResponse,
  encodeFileProfileSave,
  decodeFileProfileSave
} from '../apis/file-profile-save';
import {
  FileTalkDownloadRequest,
  encodeFileTalkDownload,
  encodeFormDataFileTalkDownload
} from '../apis/file-talk-download';
import {
  FileTalkSaveRequest,
  FileTalkSaveResponse,
  encodeFileTalkSave,
  decodeFileTalkSave
} from '../apis/file-talk-save';
import {
  FileTalkSaveMultiRequest,
  FileTalkSaveMultiResponse,
  encodeFileTalkSaveMulti,
  decodeFileTalkSaveMulti
} from '../apis/file-talk-save-multi';
import {
  FileTalkShareRequest,
  FileTalkShareResponse,
  encodeFileTalkShare,
  decodeFileTalkShare
} from '../apis/file-talk-share';
import {
  MassTalkDownloadRequest,
  MassTalkDownloadResponse,
  encodeMassTalkDownload,
  decodeMassTalkDownload
} from '../apis/mass-talk-download';
import {
  MassTalkSaveRequest,
  MassTalkSaveResponse,
  encodeMassTalkSave,
  decodeMassTalkSave
} from '../apis/mass-talk-save';
import {
  TransMassTalkDownloadRequest,
  TransMassTalkDownloadResponse,
  encodeTransMassTalkDownload,
  decodeTransMassTalkDownload
} from '../apis/trans-mass-talk-download';
import {
  TransMassTalkSaveRequest,
  TransMassTalkSaveResponse,
  encodeTransMassTalkSave,
  decodeTransMassTalkSave
} from '../apis/trans-mass-talk-save';
import {
  TranslationReqRequest,
  TranslationReqResponse,
  encodeTranslationReq,
  decodeTranslationReq
} from '../apis/translation-req';
import {
  TranslationSaveRequest,
  TranslationSaveResponse,
  encodeTranslationSave,
  decodeTranslationSave
} from '../apis/translation-save';

import { _MODULE_CONFIG } from '../config/token';
import { ModuleConfig } from '../config/module-config';
import { Urls } from '../config/urls';
import { UrlConfig } from '@ucap-webmessenger/core';
import { FileDownloadItem } from '@ucap-webmessenger/api';

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

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

  public fileProfileSave(
    req: FileProfileSaveRequest,
    fileProfileSaveUrl?: string
  ): Observable<FileProfileSaveResponse> {
    const httpReq = new HttpRequest(
      'POST',
      !!fileProfileSaveUrl ? fileProfileSaveUrl : this.urls.fileProfileSave,
      encodeFileProfileSave(req),
      { reportProgress: true, responseType: 'text' as 'json' }
    );

    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 decodeFileProfileSave(event.body);
      })
    );
  }

  public urlForFileTalkDownload(
    req: FileTalkDownloadRequest,
    fileTalkDownloadUrl?: string
  ): string {
    const httpReq = new HttpRequest(
      'GET',
      !!fileTalkDownloadUrl ? fileTalkDownloadUrl : this.urls.fileTalkDownload,
      {},
      {
        params: encodeFileTalkDownload(req)
      }
    );

    return httpReq.urlWithParams;
  }

  public fileTalkDownload(
    req: FileTalkDownloadRequest,
    fileTalkDownloadUrl?: string
  ): Observable<Blob> {
    const httpReq = new HttpRequest(
      'POST',
      !!fileTalkDownloadUrl ? fileTalkDownloadUrl : this.urls.fileTalkDownload,
      encodeFormDataFileTalkDownload(req),
      { reportProgress: true, responseType: 'blob' }
    );

    let progress: Subject<number>;
    if (!!req.fileDownloadItem) {
      progress = req.fileDownloadItem.downloadStart();
    }

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

  public fileTalkSave(
    req: FileTalkSaveRequest,
    fileTalkSaveUrl?: string
  ): Observable<FileTalkSaveResponse> {
    const httpReq = new HttpRequest(
      'POST',
      !!fileTalkSaveUrl ? fileTalkSaveUrl : this.urls.fileTalkSave,
      encodeFileTalkSave(req),
      { reportProgress: true, responseType: 'text' as 'json' }
    );

    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 decodeFileTalkSave(event.body);
      })
    );
  }

  public fileTalkSaveMulti(
    req: FileTalkSaveMultiRequest,
    fileTalkSaveMultiUrl?: string
  ): Observable<FileTalkSaveMultiResponse> {
    const httpReq = new HttpRequest(
      'POST',
      !!fileTalkSaveMultiUrl
        ? fileTalkSaveMultiUrl
        : this.urls.fileTalkSaveMulti,
      encodeFileTalkSaveMulti(req),
      { reportProgress: true, responseType: 'text' as 'json' }
    );

    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 decodeFileTalkSaveMulti(event.body);
      })
    );
  }

  public acceptableExtensionForFileTalk(
    extensions: string[]
  ): { accept: boolean; reject: string[] } {
    let accept = true;
    const reject: string[] = [];
    for (const extension of extensions) {
      if (
        -1 ===
        this.moduleConfig.acceptableFileExtensions.indexOf(
          extension.toLowerCase()
        )
      ) {
        reject.push(extension);
        accept = false;
      }
    }
    return {
      accept,
      reject
    };
  }

  public fileTalkShare(
    req: FileTalkShareRequest
  ): Observable<FileTalkShareResponse> {
    return this.httpClient
      .post<any>(
        this.urls.fileTalkShare,
        {},
        {
          params: encodeFileTalkShare(req)
        }
      )
      .pipe(map(res => decodeFileTalkShare(res)));
  }

  public massTalkDownload(
    req: MassTalkDownloadRequest
  ): Observable<MassTalkDownloadResponse> {
    return this.httpClient
      .post<any>(
        this.urls.massTalkDownload,
        {},
        {
          params: encodeMassTalkDownload(req),
          responseType: 'text' as 'json'
        }
      )
      .pipe(map(res => decodeMassTalkDownload(res)));
  }

  public massTalkSave(
    req: MassTalkSaveRequest
  ): Observable<MassTalkSaveResponse> {
    const httpReq = new HttpRequest(
      'POST',
      this.urls.massTalkSave,
      encodeMassTalkSave(req),
      { reportProgress: true, responseType: 'text' as 'json' }
    );

    return this.httpClient.request(httpReq).pipe(
      filter(event => {
        if (event instanceof HttpResponse) {
          return true;
        }
        return false;
      }),
      map(res => decodeMassTalkSave((res as HttpResponse<any>).body))
    );
  }

  public transMassTalkDownload(
    req: TransMassTalkDownloadRequest
  ): Observable<TransMassTalkDownloadResponse> {
    return this.httpClient
      .post<any>(
        this.urls.transMassTalkDownload,
        {},
        {
          params: encodeTransMassTalkDownload(req)
        }
      )
      .pipe(map(res => decodeTransMassTalkDownload(res)));
  }

  public transMassTalkSave(
    req: TransMassTalkSaveRequest
  ): Observable<TransMassTalkSaveResponse> {
    return this.httpClient
      .post<any>(
        this.urls.transMassTalkSave,
        {},
        {
          params: encodeTransMassTalkSave(req)
        }
      )
      .pipe(map(res => decodeTransMassTalkSave(res)));
  }

  public translationReq(
    req: TranslationReqRequest
  ): Observable<TranslationReqResponse> {
    return this.httpClient
      .post<any>(
        this.urls.translationReq,
        {},
        {
          params: encodeTranslationReq(req)
        }
      )
      .pipe(map(res => decodeTranslationReq(res)));
  }

  public translationSave(
    req: TranslationSaveRequest
  ): Observable<TranslationSaveResponse> {
    const httpReq = new HttpRequest(
      'POST',
      this.urls.translationSave,
      encodeTranslationSave(req),
      { reportProgress: true }
    );

    return this.httpClient.request(httpReq).pipe(
      filter(event => {
        if (event instanceof HttpResponse) {
          return true;
        }
        return false;
      }),
      map(res => decodeTranslationSave((res as HttpResponse<any>).body))
    );
  }

  public fileDownload(
    fileDownloadUrl: string,
    fileDownloadItem: FileDownloadItem
  ): Observable<Blob> {
    const httpReq = new HttpRequest('GET', fileDownloadUrl, null, {
      reportProgress: true,
      responseType: 'blob'
    });

    let progress: Subject<number>;
    if (!!fileDownloadItem) {
      progress = fileDownloadItem.downloadStart();
    }

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