// @flow
import * as PAGES from 'constants/pages';
import React from 'react';
import classnames from 'classnames';
import { useHistory } from 'react-router';
import UserEmailNew from 'component/userEmailNew';
import UserEmailVerify from 'component/userEmailVerify';
import UserFirstChannel from 'component/userFirstChannel';
import UserChannelFollowIntro from 'component/userChannelFollowIntro';
import UserTagFollowIntro from 'component/userTagFollowIntro';
import YoutubeSync from 'page/youtubeSync';
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
import { YOUTUBE_STATUSES } from 'lbryinc';
import { SETTINGS } from 'lbry-redux';
import REWARDS from 'rewards';
import UserVerify from 'component/userVerify';
import Spinner from 'component/spinner';
import YoutubeTransferStatus from 'component/youtubeTransferStatus';
import useFetched from 'effects/use-fetched';
import Confetti from 'react-confetti';
import usePrevious from 'effects/use-previous';
import { SHOW_TAGS_INTRO } from 'config';

const REDIRECT_PARAM = 'redirect';
const REDIRECT_IMMEDIATELY_PARAM = 'immediate';
const STEP_PARAM = 'step';

type Props = {
  user: ?User,
  emailToVerify: ?string,
  channels: ?Array<string>,
  balance: ?number,
  fetchingChannels: boolean,
  claimingReward: boolean,
  claimConfirmEmailReward: () => void,
  claimNewUserReward: () => void,
  fetchUser: () => void,
  claimedRewards: Array<Reward>,
  youtubeChannels: Array<any>,
  syncEnabled: boolean,
  hasSynced: boolean,
  syncingWallet: boolean,
  creatingChannel: boolean,
  setClientSetting: (string, boolean, ?boolean) => void,
  followingAcknowledged: boolean,
  tagsAcknowledged: boolean,
  rewardsAcknowledged: boolean,
  interestedInYoutubeSync: boolean,
  doToggleInterestedInYoutubeSync: () => void,
  prefsReady: boolean,
};

function UserSignUp(props: Props) {
  const {
    emailToVerify,
    user,
    claimingReward,
    claimedRewards,
    channels,
    claimConfirmEmailReward,
    claimNewUserReward,
    balance,
    fetchUser,
    youtubeChannels,
    syncEnabled,
    syncingWallet,
    hasSynced,
    fetchingChannels,
    creatingChannel,
    followingAcknowledged,
    tagsAcknowledged,
    rewardsAcknowledged,
    setClientSetting,
    interestedInYoutubeSync,
    doToggleInterestedInYoutubeSync,
    prefsReady,
  } = props;
  const {
    location: { search, pathname },
    replace,
  } = useHistory();
  const urlParams = new URLSearchParams(search);
  const redirect = urlParams.get(REDIRECT_PARAM);
  const step = urlParams.get(STEP_PARAM);
  const shouldRedirectImmediately = urlParams.get(REDIRECT_IMMEDIATELY_PARAM);
  const [initialSignInStep, setInitialSignInStep] = React.useState();
  const hasVerifiedEmail = user && user.has_verified_email;
  const rewardsApproved = user && user.is_reward_approved;
  const isIdentityVerified = user && user.is_identity_verified;
  const passwordSet = user && user.password_set;
  const hasFetchedReward = useFetched(claimingReward);
  const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail);
  const channelCount = channels ? channels.length : 0;
  const hasClaimedEmailAward = claimedRewards.some((reward) => reward.reward_type === REWARDS.TYPE_CONFIRM_EMAIL);
  const hasYoutubeChannels = youtubeChannels && Boolean(youtubeChannels.length);
  const isYoutubeTransferComplete =
    hasYoutubeChannels &&
    youtubeChannels.every(
      (channel) =>
        channel.transfer_state === YOUTUBE_STATUSES.COMPLETED_TRANSFER ||
        channel.sync_status === YOUTUBE_STATUSES.YOUTUBE_SYNC_ABANDONDED
    );
  // Complexity warning
  // We can't just check if we are currently fetching something
  // We may want to keep a component rendered while something is being fetched, instead of replacing it with the large spinner
  // The verbose variable names are an attempt to alleviate _some_ of the confusion from handling all edge cases that come from
  // reward claiming, channel creation, account syncing, and youtube transfer
  // The possible screens for the sign in flow
  const showEmail = !hasVerifiedEmail;
  const showEmailVerification = (emailToVerify && !hasVerifiedEmail) || (!hasVerifiedEmail && passwordSet);
  const showUserVerification = hasVerifiedEmail && !rewardsApproved && !isIdentityVerified && !rewardsAcknowledged;
  const showChannelCreation =
    hasVerifiedEmail &&
    ((balance !== undefined &&
      balance !== null &&
      balance > DEFAULT_BID_FOR_FIRST_CHANNEL &&
      channelCount === 0 &&
      !hasYoutubeChannels) ||
      interestedInYoutubeSync);
  const showYoutubeTransfer = hasVerifiedEmail && hasYoutubeChannels && !isYoutubeTransferComplete;
  const showFollowIntro = step === 'channels' || (hasVerifiedEmail && !followingAcknowledged);
  const showTagsIntro = SHOW_TAGS_INTRO && (step === 'tags' || (hasVerifiedEmail && !tagsAcknowledged));
  const canHijackSignInFlowWithSpinner = hasVerifiedEmail && !showFollowIntro && !showTagsIntro && !rewardsAcknowledged;
  const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet || creatingChannel;
  const isWaitingForSomethingToFinish =
    // If the user has claimed the email award, we need to wait until the balance updates sometime in the future
    (!hasFetchedReward && !hasClaimedEmailAward) || (syncEnabled && !hasSynced);
  const showLoadingSpinner =
    canHijackSignInFlowWithSpinner && (isCurrentlyFetchingSomething || isWaitingForSomethingToFinish);

  function setSettingAndSync(setting, value) {
    setClientSetting(setting, value, true);
  }

  React.useEffect(() => {
    fetchUser();
  }, [fetchUser]);

  React.useEffect(() => {
    if (previousHasVerifiedEmail === false && hasVerifiedEmail && prefsReady) {
      setSettingAndSync(SETTINGS.FIRST_RUN_STARTED, true);
    }
  }, [hasVerifiedEmail, previousHasVerifiedEmail, prefsReady]);

  React.useEffect(() => {
    // Don't claim the reward if sync is enabled until after a sync has been completed successfully
    // If we do it before, we could end up trying to sync a wallet with a non-zero balance which will fail to sync
    const delayForSync = syncEnabled && !hasSynced;

    if (hasVerifiedEmail && !hasClaimedEmailAward && !hasFetchedReward && !delayForSync) {
      claimConfirmEmailReward();
    }
  }, [
    hasVerifiedEmail,
    claimConfirmEmailReward,
    hasClaimedEmailAward,
    hasFetchedReward,
    syncEnabled,
    hasSynced,
    balance,
  ]);

  // Loop through this list from the end, until it finds a matching component
  // If it never finds one, assume the user has completed every step and redirect them
  const SIGN_IN_FLOW = [
    showEmail && (
      <UserEmailNew
        interestedInYoutubSync={interestedInYoutubeSync}
        doToggleInterestedInYoutubeSync={doToggleInterestedInYoutubeSync}
      />
    ),
    showEmailVerification && <UserEmailVerify />,
    showUserVerification && (
      <UserVerify
        onSkip={() => {
          setSettingAndSync(SETTINGS.REWARDS_ACKNOWLEDGED, true);
        }}
      />
    ),
    showChannelCreation &&
      (interestedInYoutubeSync ? (
        <YoutubeSync inSignUpFlow doToggleInterestedInYoutubeSync={doToggleInterestedInYoutubeSync} />
      ) : (
        <UserFirstChannel doToggleInterestedInYoutubeSync={doToggleInterestedInYoutubeSync} />
      )),
    showFollowIntro && (
      <UserChannelFollowIntro
        onContinue={() => {
          if (urlParams.get('reset_scroll')) {
            urlParams.delete('reset_scroll');
            urlParams.append('reset_scroll', '2');
          }

          urlParams.delete(STEP_PARAM);

          setSettingAndSync(SETTINGS.FOLLOWING_ACKNOWLEDGED, true);
          replace(`${pathname}?${urlParams.toString()}`);
        }}
        onBack={() => {
          if (urlParams.get('reset_scroll')) {
            urlParams.delete('reset_scroll');
            urlParams.append('reset_scroll', '3');
          }

          setSettingAndSync(SETTINGS.FOLLOWING_ACKNOWLEDGED, false);
          replace(`${pathname}?${urlParams.toString()}`);
        }}
      />
    ),
    showTagsIntro && (
      <UserTagFollowIntro
        onContinue={() => {
          let url = `/$/${PAGES.AUTH}?reset_scroll=1&${STEP_PARAM}=channels`;
          if (redirect) {
            url += `&${REDIRECT_PARAM}=${redirect}`;
          }
          if (shouldRedirectImmediately) {
            url += `&${REDIRECT_IMMEDIATELY_PARAM}=true`;
          }

          replace(url);
          setSettingAndSync(SETTINGS.TAGS_ACKNOWLEDGED, true);
        }}
      />
    ),
    showYoutubeTransfer && (
      <div>
        <YoutubeTransferStatus /> <Confetti recycle={false} style={{ position: 'fixed' }} />
      </div>
    ),
    showLoadingSpinner && (
      <div className="main--empty">
        <Spinner />
      </div>
    ),
  ];

  //   $FlowFixMe
  function getSignInStep() {
    for (var i = SIGN_IN_FLOW.length - 1; i > -1; i--) {
      const Component = SIGN_IN_FLOW[i];
      if (Component) {
        // If we want to redirect immediately,
        // remember the first step so we can redirect once a new step has been reached
        // Ignore the loading step
        if (redirect && shouldRedirectImmediately) {
          if (!initialSignInStep) {
            setInitialSignInStep(i);
          } else if (i !== initialSignInStep && i !== SIGN_IN_FLOW.length - 1) {
            replace(redirect);
          }
        }

        const scrollableSteps = [2, 4, 5];
        const isScrollable = scrollableSteps.includes(i);
        return [Component, isScrollable];
      }
    }

    return [undefined, false];
  }

  const [componentToRender, isScrollable] = getSignInStep();

  React.useEffect(() => {
    if (!componentToRender) {
      claimNewUserReward();
    }
  }, [componentToRender, claimNewUserReward]);

  if (!componentToRender) {
    replace(redirect || '/');
  }

  return (
    <section className={classnames('main--contained', { 'main--hoisted': isScrollable })}>{componentToRender}</section>
  );
}

export default UserSignUp;
