import { ofType } from 'redux-observable';
import { concat, merge, of, Subject } from 'rxjs';
import { ajax } from 'rxjs/ajax';
import { catchError, concatMap, filter, map, switchMap, takeUntil } from 'rxjs/operators';

import { getCurrentUploadStateFromQueue, processedUploadQueueIdSelector } from '../../selectors';
import {
  createVideoFromCsvAction,
  errorAction,
  loadingProgressAction,
  uploadCsvAction,
  uploadVideoCancelledAction,
} from '../../slices';

export default (action$, state$) =>
  action$.pipe(
    ofType(uploadCsvAction.type),
    switchMap((action) => {
      const processedUploadQueueId = processedUploadQueueIdSelector(state$.value);
      const { csvFile } = getCurrentUploadStateFromQueue(state$.value);
      const {
        payload: { uploadUrl },
      } = action;

      const progressSubscriber = new Subject();

      const request = ajax({
        method: 'PUT',
        url: uploadUrl,
        headers: {
          'Content-Type': 'application/octet-stream',
        },
        crossDomain: true,
        withCredentials: true,
        body: csvFile,
        progressSubscriber,
      });

      const requestObservable = request.pipe(concatMap(() => of(createVideoFromCsvAction())));

      const stream$ = merge(
        progressSubscriber.pipe(
          map((e) => ({ percentage: e.loaded / e.total })),
          map((data) =>
            loadingProgressAction({
              percentage: data.percentage,
              processedUploadQueueId,
            }),
          ),
        ),
        requestObservable,
      ).pipe(
        takeUntil(
          action$.pipe(
            ofType(uploadVideoCancelledAction.type),
            filter(({ payload }) => payload.processedUploadQueueId === processedUploadQueueId),
          ),
        ),
        catchError(() => {
          const actions = [
            of(
              errorAction({
                reason: 'error',
                message: 'Network connection error',
              }),
            ),
          ];

          return concat(...actions);
        }),
      );

      return stream$;
    }),
  );
