import { paths } from 'app/routing/paths';
import { Id, RequestApprovalRequest } from 'backend-api/models';
import { SpreadsheetContainer } from 'media-plan-v2/containers/spreadsheet';
import { LoadingState } from 'common/types';
import { useProjectAccessRestricted, useQuery } from 'common/hooks';
import {
  changeMediaPlanMode,
  clearMediaPlan,
  closeMediaPlanUpdatedWarningModal,
  getAllMediaPlans,
  getApprovalRequests,
  getMediaPlan,
  getMediaPlanMetrics,
  getMediaPlanReviewers,
  openMediaPlanUpdatedWarningModal,
  requestApproval,
  resetCampaignSelection,
  resetCampaignsForApproval,
  setMediaPlanMode,
  setMediaPlanNotFoundModalOpened,
  submitApproval,
} from 'media-plan-v2/actions';
import { ConfirmReviewFooter, RequestApprovalFooter } from 'media-plan-v2/components';
import { SpreadsheetRef } from 'media-plan-v2/components/spreadsheet';
import {
  approvalRequestsCampaignsSelector,
  approvalRequestsLoadingStateSelector,
  isMediaPlanNotFoundModalOpenedSelector,
  isMediaPlanUpdatedModalOpenedSelector,
  mediaPlanModeSelector,
  mediaPlanSelector,
  mediaPlansSelector,
  projectReviewersSelector,
  isRequestApprovalLoadingSelector,
  reviewedCampaignsSelector,
  selectedCampaignsIdsSelector,
  errorSelector,
} from 'media-plan-v2/selectors';
import { isLoadingStateAllowUILoading, splitApprovedDisapprovedCampaigns } from 'media-plan-v2/transducers';
import { MediaPlanMode, MediaPlanReferrerType } from 'media-plan-v2/types';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { RouteComponentProps, useHistory, useLocation } from 'react-router-dom';
import { AnalyticsEvents, trackEvent } from 'utils/analytic';
import { bem } from 'utils/bem';
import { getSearchParamsFromLocation, navigateTo, replaceTo } from 'utils/navigation';
import { BEM_CLASS, SMediaPlanContainer, SMediaPlanContainerFooter } from './s-media-plan-container';
import { includesRole } from 'common/transducers';
import { MediaPlanWarningModal } from 'media-plan-v2/components/media-plan-not-exist-modal';
import { useRedirectToFirstMediaPlan } from 'media-plan-v2/hooks/use-redirect-to-first-media-plan';
import { MediaPlanHeaderContainer } from '../media-plan-header';
import { ROLES } from 'common-v2/constants';
import { Head } from 'app/components/head';
import { getProjectName } from 'common-v2/transducers';
import { THEME, useManageToasts } from 'gdb-web-shared-components';
import { ProjectRoutingProps } from 'app/routing/types';
import { Loading } from 'common-v2/types';
import { usePrevious } from 'react-use';
import { createPortal } from 'react-dom';

const classes = bem(BEM_CLASS);

export const MediaPlanContainer = React.memo(
  ({
    projectId,
    projectLoading,
    projectDetails,
    match,
  }: RouteComponentProps<{ mediaPlanId: string }> & ProjectRoutingProps) => {
    const dispatch = useDispatch();

    const mediaPlanId = match.params.mediaPlanId === undefined ? undefined : Number(match.params.mediaPlanId);
    const previousMediaPlanId = usePrevious(mediaPlanId);

    const { query } = useQuery();
    const { openToast } = useManageToasts(THEME.light);
    const location = useLocation();
    const history = useHistory();
    const params = getSearchParamsFromLocation(location);

    const spreadsheetRef = useRef<SpreadsheetRef>(null);

    const mediaPlans = useSelector(mediaPlansSelector);
    const mediaPlan = useSelector(mediaPlanSelector);
    const mediaPlanMode = useSelector(mediaPlanModeSelector);
    const previousMediaPlanMode = usePrevious(mediaPlanMode);
    const selectedCampaignIds = useSelector(selectedCampaignsIdsSelector);
    const projectReviewers = useSelector(projectReviewersSelector);
    const reviewedCampaigns = useSelector(reviewedCampaignsSelector);
    const approvalRequestsCampaigns = useSelector(approvalRequestsCampaignsSelector);
    const approvalRequestsLoading = useSelector(approvalRequestsLoadingStateSelector);
    const isMediaPlanNotFoundModalOpened = useSelector(isMediaPlanNotFoundModalOpenedSelector);
    const isMediaPlanUpdatedModalOpened = useSelector(isMediaPlanUpdatedModalOpenedSelector);
    const isRequestApprovalLoading = useSelector(isRequestApprovalLoadingSelector);

    useProjectAccessRestricted(errorSelector);
    useRedirectToFirstMediaPlan(mediaPlanId, projectId);

    const isSpreadsheetLoading =
      isLoadingStateAllowUILoading(projectLoading) ||
      isLoadingStateAllowUILoading(mediaPlan.loading) ||
      isLoadingStateAllowUILoading(approvalRequestsLoading);

    useEffect(() => {
      if (!!projectDetails && !projectDetails.isClaimed) {
        navigateTo(paths.outdatedNotFound());
      }
    }, [projectDetails]);

    useEffect(() => {
      return () => {
        dispatch(clearMediaPlan());
      };
    }, [dispatch]);

    const [isFullScreen, setIsFullScreen] = useState(false);

    const hasNothingToApprove =
      approvalRequestsLoading === LoadingState.Finished && approvalRequestsCampaigns.length === 0;

    const isApprovalModeEmpty = mediaPlanMode === MediaPlanMode.APPROVAL && hasNothingToApprove;

    useEffect(() => {
      if (approvalRequestsLoading !== LoadingState.Finished) return;

      if (!isApprovalModeEmpty) {
        return;
      }

      if (params.referrer === MediaPlanReferrerType.EMAIL) {
        dispatch(openMediaPlanUpdatedWarningModal());
      } else {
        replaceTo(paths.mediaPlan(projectId, mediaPlanId), { query: { ...query, mode: undefined } });
      }
    }, [dispatch, isApprovalModeEmpty, projectId, mediaPlanId, params.referrer, approvalRequestsLoading, query]);

    const submitRequestApproval = useCallback(
      (reviewers: Id[], message?: string) => {
        if (!mediaPlanId) return;

        const payload: RequestApprovalRequest = { campaigns: selectedCampaignIds, reviewers, message };

        trackEvent(AnalyticsEvents.APPROVAL_REQUEST_CONFIRMED, {
          projectId,
          campaignCount: selectedCampaignIds.length,
        });

        dispatch(
          requestApproval.request({
            projectId,
            mediaPlanId,
            payload,
            openToast,
          })
        );
      },
      [mediaPlanId, selectedCampaignIds, projectId, dispatch, openToast]
    );

    const cancelRequestApproval = useCallback(() => {
      dispatch(changeMediaPlanMode(MediaPlanMode.DEFAULT));
      dispatch(resetCampaignSelection());
    }, [dispatch]);

    const toggleFullScreen = useCallback(() => {
      setIsFullScreen(!isFullScreen);
    }, [isFullScreen]);

    const fetchMediaPlan = useCallback(
      (mediaPlanId: number) => {
        dispatch(getApprovalRequests.request({ projectId, mediaPlanId }));
        dispatch(getMediaPlan.request({ projectId, mediaPlanId }));
      },
      [dispatch, projectId]
    );

    useEffect(() => {
      const isUserHasApprovalPermission =
        !!projectDetails?.userRoles && includesRole(projectDetails.userRoles, ROLES.APPROVER);
      const isApprovalMode =
        isUserHasApprovalPermission && params.mode === MediaPlanMode.APPROVAL && !hasNothingToApprove;

      if (isApprovalMode) {
        trackEvent(AnalyticsEvents.APPROVAL_VIEW_OPENED_LINK, { projectId, mediaPlanId });
        dispatch(setMediaPlanMode(MediaPlanMode.APPROVAL));
      } else {
        dispatch(setMediaPlanMode(MediaPlanMode.DEFAULT));
      }
    }, [dispatch, hasNothingToApprove, mediaPlanId, params.mode, projectDetails?.userRoles, projectId]);

    const refetchAllMediaPlanData = useCallback(
      (mediaPlanId: number) => {
        if (mediaPlans.loading === LoadingState.Failed) {
          dispatch(getAllMediaPlans.request(projectId));
        }

        fetchMediaPlan(mediaPlanId);
      },
      [dispatch, fetchMediaPlan, mediaPlans.loading, projectId]
    );

    useEffect(() => trackEvent(AnalyticsEvents.MEDIA_PLAN_VIEWED, { projectId }), [projectId]);

    useEffect(() => {
      document.body.style.overflow = isFullScreen ? 'hidden' : 'visible';
    }, [isFullScreen]);

    const onExportClick = useCallback(() => spreadsheetRef.current?.export(), []);

    useEffect(() => {
      if (!mediaPlanId) return;

      dispatch(getMediaPlanReviewers.request({ projectId, mediaPlanId }));
    }, [dispatch, mediaPlanId, projectId]);

    const onSubmitSuccess = useCallback(() => {
      dispatch(resetCampaignsForApproval());

      replaceTo(paths.mediaPlan(projectId, mediaPlanId), { query: { mode: MediaPlanMode.APPROVAL } });
    }, [dispatch, mediaPlanId, projectId]);

    const confirmCampaignsReview = useCallback(() => {
      if (!mediaPlanId) return;

      const { approved, disapproved } = splitApprovedDisapprovedCampaigns(reviewedCampaigns);

      trackEvent(AnalyticsEvents.APPROVER_CONFIRM_REVIEW, {
        projectId,
        approvedCampaignsCount: approved.length,
        disapprovedCampaignsCount: disapproved.length,
      });

      dispatch(
        submitApproval.request({
          projectId,
          mediaPlanId,
          payload: { campaigns: reviewedCampaigns },
          onSubmitSuccess,
          openToast,
        })
      );
    }, [mediaPlanId, reviewedCampaigns, projectId, dispatch, onSubmitSuccess, openToast]);

    const renderFooter = (isFullScreen: boolean) => {
      const root = document.getElementById('root');

      if (mediaPlanId === undefined || root === null) return null;

      let footer;

      switch (mediaPlanMode) {
        case MediaPlanMode.REQUEST_APPROVAL:
          footer = (
            <SMediaPlanContainerFooter>
              <div className={classes('footer')}>
                <RequestApprovalFooter
                  count={selectedCampaignIds.length}
                  reviewers={projectReviewers.data}
                  onSubmit={submitRequestApproval}
                  onCancel={cancelRequestApproval}
                  approvalRequestLoading={isRequestApprovalLoading}
                  isConfidentialProject={projectDetails?.isConfidential}
                />
              </div>
            </SMediaPlanContainerFooter>
          );
          break;
        case MediaPlanMode.APPROVAL:
          footer =
            approvalRequestsLoading === LoadingState.Finished ? (
              <SMediaPlanContainerFooter>
                <div className={classes('footer')}>
                  <ConfirmReviewFooter
                    selectedCount={reviewedCampaigns.length}
                    allCount={approvalRequestsCampaigns.length}
                    onConfirm={confirmCampaignsReview}
                    approvalRequestLoading={isRequestApprovalLoading}
                  />
                </div>

                {isRequestApprovalLoading === LoadingState.Started && <div className={classes('overlay')} />}
              </SMediaPlanContainerFooter>
            ) : null;
          break;
        default:
          footer = null;
      }

      return isFullScreen ? footer : createPortal(footer, root);
    };

    useEffect(() => {
      if (projectLoading !== Loading.Finished) return;

      if (mediaPlans.loading === LoadingState.Idle || mediaPlanId === undefined) {
        dispatch(getAllMediaPlans.request(projectId));
        return;
      }

      const isMediaPlanModeChanged = mediaPlanMode !== previousMediaPlanMode;
      const isMediaPlanModeRefetch =
        isMediaPlanModeChanged &&
        ((mediaPlanMode === MediaPlanMode.APPROVAL && previousMediaPlanMode === MediaPlanMode.DEFAULT) ||
          (mediaPlanMode === MediaPlanMode.DEFAULT && previousMediaPlanMode === MediaPlanMode.APPROVAL));

      if (mediaPlan.loading === LoadingState.Idle || mediaPlanId !== previousMediaPlanId || isMediaPlanModeRefetch) {
        dispatch(getMediaPlanMetrics.request({ projectId, mediaPlanId }));
        fetchMediaPlan(mediaPlanId);
      }
    }, [
      dispatch,
      fetchMediaPlan,
      mediaPlan.loading,
      mediaPlanId,
      mediaPlanMode,
      mediaPlans.loading,
      previousMediaPlanId,
      previousMediaPlanMode,
      projectId,
      projectLoading,
    ]);

    return (
      <>
        <Head title={getProjectName(projectDetails)} />

        <SMediaPlanContainer>
          <div className={classes('spreadsheet', { isFullScreen })}>
            <div className={classes('container')}>
              <MediaPlanHeaderContainer
                projectId={projectId}
                mediaPlanId={mediaPlanId}
                projectLoading={projectLoading}
                projectDetails={projectDetails}
                isFullScreen={isFullScreen}
                onFullscreenClick={toggleFullScreen}
                onExportClick={onExportClick}
              />

              <SpreadsheetContainer
                fetchMediaPlan={refetchAllMediaPlanData}
                ref={spreadsheetRef}
                projectId={projectId}
                mediaPlanId={mediaPlanId}
                isLoading={isSpreadsheetLoading}
              />
            </div>

            {renderFooter(isFullScreen)}
          </div>

          <MediaPlanWarningModal
            message="The Media Plan you are trying to open has been deleted and no longer exists."
            isOpen={isMediaPlanNotFoundModalOpened}
            onClose={() => dispatch(setMediaPlanNotFoundModalOpened(false))}
          />

          <MediaPlanWarningModal
            message="This Media Plan has been updated since you received the approval request."
            isOpen={isMediaPlanUpdatedModalOpened}
            onClose={() => {
              dispatch(closeMediaPlanUpdatedWarningModal());
              history.replace(paths.mediaPlan(projectId, mediaPlanId));
            }}
          />
        </SMediaPlanContainer>
      </>
    );
  }
);
