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

import { getCurrentUploadStateFromQueue, processedUploadQueueIdSelector } from '../../selectors';
import {
  errorAction,
  loadingProgressAction,
  uploadCcFilesAction,
  uploadThumbnailAction,
  uploadVideoAction,
  uploadVideoCancelledAction,
} from '../../slices';
import { requestEventAction, responseEventAction } from '@/modules/@common/request/redux/slices';

export default (action$, state$) =>
  action$.pipe(
    ofType(uploadVideoAction.type),
    switchMap((action) => {
      const processedUploadQueueId = processedUploadQueueIdSelector(state$.value);
      const { video, thumbnail, ccFiles } = getCurrentUploadStateFromQueue(state$.value);
      const {
        payload: { uploadUrl },
      } = action;
      const totalCcFilesSize = ccFiles.reduce((acc, value) => acc + value.fileObject.size, 0);

      const progressSubscriber = new Subject();

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

      const requestObservable = request.pipe(
        concatMap(() => {
          if (thumbnail) {
            return of(uploadThumbnailAction());
          }

          return of(uploadCcFilesAction());
        }),
      );

      const stream$ = merge(
        progressSubscriber.pipe(
          map((e) => ({ percentage: e.loaded / (e.total + totalCcFilesSize) })),
          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);
        }),
      );

      const actions = [];

      if (video) {
        actions.push(concat(stream$.pipe(startWith(requestEventAction())), of(responseEventAction())));
      } else if (thumbnail) {
        actions.push(of(uploadThumbnailAction()));
      } else {
        actions.push(of(uploadCcFilesAction()));
      }

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