import dayjs from 'dayjs';
import utcPlugin from 'dayjs/plugin/utc';
import { ofType } from 'redux-observable';
import { concat, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';

import { AUDIO_SOURCE_TYPE, EDITORIAL_SOURCE_TYPE } from '@/modules/@common/constants/file';
import { mapApiError } from '@/modules/@common/constants/mapApiError';
import { TYPE_ERROR } from '@/modules/@common/notify/constants';
import { ACCESS_LEVEL_ENUM, SEND_HUBS_NOTIFICATION } from '@/modules/editorial/shareAndAccess/constants';
import { UPLOAD_STATUS_COMPLETE, UPLOAD_STATUS_FAILED } from '@/modules/uploaderNew/constants';

import { checkFileIsAudio } from '../../helpers/audio';
import { getCurrentUploadStateFromQueue, processedUploadQueueIdSelector } from '../selectors';
import {
  createVideoAction,
  errorAction,
  loadingAction,
  loadingProgressAction,
  setProcessUploadStatusAction,
  startUploadFromQueueAction,
} from '../slices';
import { clearAction as clearCcFilesModuleBeforeUnmount } from '@/modules/@common/ccFiles/redux/slices';
import { gqlRequest } from '@/modules/@common/request';
import { showNotificationAction } from '@/modules/layout/redux/slices';

dayjs.extend(utcPlugin);

const getNotEmptyValue = (obj) =>
  Object.fromEntries(
    Object.entries(obj).filter(
      ([, value]) =>
        (typeof value === 'string' && value.trim() !== '') || typeof value === 'number' || typeof value === 'boolean',
    ),
  );

const queryGQL = `
  mutation videoCreateMutation($video: VideoInputType!) {
    videoCreate(video: $video) {
      uid
    }
  }
`;

const getErrorMessage = (errors, refId, uploadVideo, videoUrl) => {
  const {
    statusCode: status,
    response: { error: message },
  } = errors[0];

  if (status === 400 && uploadVideo && Object.keys(uploadVideo).length) {
    return 'Invalid file extension';
  }

  if (status === 400 && uploadVideo && videoUrl) {
    return 'The video URL is invalid or cannot be accessed. Please verify that you’re using a publicly available, valid URL.';
  }

  if (status === 409) {
    return `Video with the GUID ${refId} already exists. Try another GUID.`;
  }

  const apiError = mapApiError();

  return apiError([message]);
};

export default (action$, state$) =>
  action$.pipe(
    ofType(createVideoAction.type),
    switchMap(() => {
      const processedUploadQueueId = processedUploadQueueIdSelector(state$.value);
      const {
        title,
        refId,
        description,
        owner,
        feed,
        uploadVideo,
        date,
        language,
        isNotify,
        labels,
        keywords,
        iab,
        evergreen,
        notes,
        videoUrl,
        uploadCsv,
        accessLevel,
        sites,
        ccFiles,

        landingPageLink,
        primaryText,
        buttonLabel,
        secondaryText,
        timeToAppear,
        advertiserId,
        advertiserName,
        advertiserLogo,
        sponsored,

        sendHubsNotification,
      } = getCurrentUploadStateFromQueue(state$.value);

      const isAudio = checkFileIsAudio(uploadVideo.downloadUrl || videoUrl);

      const videoData = {
        type: 'SHORT_FORM',
        name: title,
        plot: description,
        videoUrl: uploadVideo.downloadUrl || videoUrl,
        contentOwner: owner.value,
        lang: [language.value],
        evergreen,
        landingPageLink,
        sponsored: getNotEmptyValue({
          primaryText,
          buttonLabel,
          secondaryText,
          timeToAppear: parseInt(timeToAppear, 10),
          advertiserId,
          advertiserName,
          advertiserLogo,
          sponsored,
        }),
        access: {
          level: accessLevel,
        },
        features: [],
      };

      if (videoUrl) {
        videoData.publisherLink = videoUrl;
      }

      if (accessLevel === ACCESS_LEVEL_ENUM.site) {
        videoData.access.sites = sites.map((site) => site.value.toString());

        if (sendHubsNotification) {
          videoData.features.push(SEND_HUBS_NOTIFICATION);
        }
      }

      if (uploadVideo?.ccFiles?.length) {
        videoData.ccFiles = uploadVideo.ccFiles.map((uploadedCcFile, index) => {
          const { fileObject, ...ccFile } = ccFiles[index];
          return {
            ...ccFile,
            file: uploadedCcFile.downloadUrl,
          };
        });
      }

      videoData.videoCreationDate = dayjs(date || new Date())
        .utc()
        .valueOf();

      if (uploadVideo.thumbnailDownloadUrl) {
        videoData.thumbnailUrl = uploadVideo.thumbnailDownloadUrl;
      }

      if (feed) {
        videoData.feedDescription = feed.feedDescription;
        videoData.feedSource = feed.feedSource;
        videoData.feedId = feed.value;
      }

      if (refId) {
        videoData.refId = refId;
      }

      if (labels.length) {
        videoData.label = labels;
      }

      if (keywords.length) {
        videoData.keywords = keywords;
      }

      if (iab.length) {
        videoData.iab = iab.map((o) => ({ id: o.id }));
      }

      if (notes.length) {
        videoData.notes = notes;
      }

      if (feed?.value) {
        videoData.features.push('ENABLE_VIDEO', 'TOP_PRIORITY');
      } else {
        videoData.features.push(
          'GENERATE_HLS',
          'TAGGING',
          'CLASSIFICATION',
          'SPEECH_RECOGNITION',
          'GENERATE_VIDEO_RESOLUTIONS',
          'ENABLE_VIDEO',
          'ENCODE',
          'TOP_PRIORITY',
        );
      }

      if (isNotify) {
        videoData.features.push('SEND_NOTIFICATION');
      }

      videoData.origin = !isAudio ? EDITORIAL_SOURCE_TYPE : AUDIO_SOURCE_TYPE;

      const stream$ = gqlRequest(
        {
          query: queryGQL,
          variables: { video: videoData },
        },
        {
          showNotificationMessage: false,
        },
      ).pipe(
        switchMap(({ errors }) => {
          const actions = [];

          if (errors.length) {
            actions.push(
              of(
                showNotificationAction({
                  type: TYPE_ERROR,
                  message: getErrorMessage(errors, refId, uploadVideo, videoUrl, uploadCsv),
                }),
              ),
              of(
                setProcessUploadStatusAction({
                  status: UPLOAD_STATUS_FAILED,
                  processedUploadQueueId,
                }),
              ),
              of(startUploadFromQueueAction()),
            );
          } else {
            actions.push(
              of(
                loadingProgressAction({
                  percentage: 1,
                  processedUploadQueueId,
                }),
              ),
              of(clearCcFilesModuleBeforeUnmount()),
              of(
                setProcessUploadStatusAction({
                  status: UPLOAD_STATUS_COMPLETE,
                  processedUploadQueueId,
                }),
              ),
              of(startUploadFromQueueAction()),
            );
          }

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

      return concat(stream$, of(loadingAction(false)), of(errorAction({})));
    }),
  );
