import { ofType } from 'redux-observable';
import { concat, of } from 'rxjs';
import { mergeMap, switchMap } from 'rxjs/operators';

import { dataSelector } from '../selectors';
import { dataAction, endAction, repeatAction, runAction } from '../slices';
import { hasUnfinishedJobs } from '@/modules/@common/monitoring/helpers/monitoring';
import { gqlRequest } from '@/modules/@common/request';

const queryGQL = `
  query MonitoringQuery($videoId: String!, $size: Float!) {
    monitoring(videoId: $videoId, size: $size) {
      jobs {
        videoId
        childId
        childType
        type
        state
        message
        startTime
        updateTime
        progress
        properties {
          key
          value
        }
      }
    }
  }
`;

const jobTypes = [
  'VIDEO_PROCESSING',
  'VIDEO_CHILD_PROCESSING',
  'TAGGING',
  'SCENE_RECOGNITION',
  'VIDEO_ENCODING',
  'CLOSED_CAPTIONS',
  'THUMBNAIL_PROCESSING',
];

const getFilteredJobs = (jobs) => jobs.filter((job) => jobTypes.includes(job.type));

const monitoringEpic = (action$, state$) =>
  action$.pipe(
    ofType(runAction.type),
    mergeMap(({ payload }) => {
      const monitoringData = dataSelector(state$.value);
      const videoId = payload;

      const stream$ = gqlRequest({
        query: queryGQL,
        variables: {
          videoId,
          size: 1000,
        },
      }).pipe(
        switchMap(({ data, errors }) => {
          if (errors.length) {
            throw new Error(errors[0]);
          }

          const actions = [];

          if (!errors.length) {
            const filteredJobs = getFilteredJobs(data.monitoring.jobs);
            const isFinishedMonitoring = !hasUnfinishedJobs(filteredJobs);
            const updatedJobData = { ...monitoringData, ...{ [videoId]: filteredJobs } };

            actions.push(
              of(dataAction(updatedJobData)),
              of(
                isFinishedMonitoring
                  ? endAction(videoId)
                  : repeatAction({
                      videoId,
                      jobs: filteredJobs,
                    }),
              ),
            );
          }

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

      return concat(stream$);
    }),
  );

export default monitoringEpic;
