import { Lbryio } from 'lbryinc';
import { doUpdateBalance } from 'redux/actions/wallet';
import { doToast } from 'redux/actions/notifications';
import * as ACTIONS from 'constants/action_types';
import { selectUnclaimedRewards, selectWeeklyWatchClaimedThisWeek } from 'redux/selectors/rewards';
import { selectUserIsRewardApproved } from 'redux/selectors/user';
import { doFetchInviteStatus } from 'redux/actions/user';
import rewards from 'rewards';
import { resolveApiMessage } from 'util/api-message';

export function doRewardList() {
  return (dispatch) => {
    dispatch({
      type: ACTIONS.FETCH_REWARDS_STARTED,
    });

    Lbryio.call('reward', 'list', { multiple_rewards_per_type: true })
      .then((userRewards) => {
        dispatch({
          type: ACTIONS.FETCH_REWARDS_COMPLETED,
          data: { userRewards },
        });
      })
      .catch(() => {
        dispatch({
          type: ACTIONS.FETCH_REWARDS_COMPLETED,
          data: { userRewards: [] },
        });
      });
  };
}

export function doClaimRewardType(rewardType, options = {}) {
  return (dispatch, getState) => {
    const state = getState();
    const userIsRewardApproved = selectUserIsRewardApproved(state);
    const unclaimedRewards = selectUnclaimedRewards(state);
    const reward =
      rewardType === rewards.TYPE_REWARD_CODE || rewardType === rewards.TYPE_NEW_ANDROID
        ? { reward_type: rewards.TYPE_REWARD_CODE }
        : unclaimedRewards.find((ur) => ur.reward_type === rewardType);

    // Try to claim the email reward right away, even if we haven't called reward_list yet
    if (
      rewardType !== rewards.TYPE_REWARD_CODE &&
      rewardType !== rewards.TYPE_CONFIRM_EMAIL &&
      rewardType !== rewards.TYPE_WEEKLY_WATCH &&
      rewardType !== rewards.TYPE_NEW_ANDROID
    ) {
      if (!reward || reward.transaction_id) {
        // already claimed or doesn't exist, do nothing
        return;
      }
    }

    if (
      !userIsRewardApproved &&
      rewardType !== rewards.TYPE_CONFIRM_EMAIL &&
      rewardType !== rewards.TYPE_REWARD_CODE &&
      rewardType !== rewards.TYPE_NEW_ANDROID
    ) {
      if (!options || (!options.failSilently && rewards.callbacks.rewardApprovalRequested)) {
        rewards.callbacks.rewardApprovalRequested();
      }

      return;
    }

    // Set `claim_code` so the api knows which reward to give if there are multiple of the same type
    const params = options.params || {};
    if (!params.claim_code && reward) {
      params.claim_code = reward.claim_code;
    }

    dispatch({
      type: ACTIONS.CLAIM_REWARD_STARTED,
      data: { reward },
    });

    const success = (successReward) => {
      // Temporary timeout to ensure the sdk has the correct balance after claiming a reward
      setTimeout(() => {
        dispatch(doUpdateBalance()).then(() => {
          dispatch({
            type: ACTIONS.CLAIM_REWARD_SUCCESS,
            data: {
              reward: successReward,
            },
          });
          if (successReward.reward_type === rewards.TYPE_NEW_USER && rewards.callbacks.claimFirstRewardSuccess) {
            rewards.callbacks.claimFirstRewardSuccess();
          } else if (successReward.reward_type === rewards.TYPE_REFERRAL) {
            dispatch(doFetchInviteStatus());
          }

          dispatch(doRewardList());

          if (options.callback) {
            options.callback();
          }
        });
      }, 2000);
    };

    const failure = (error) => {
      dispatch({
        type: ACTIONS.CLAIM_REWARD_FAILURE,
        data: {
          reward,
          error: !options || !options.failSilently ? error : undefined,
        },
      });

      if (options.notifyError) {
        dispatch(doToast({ message: resolveApiMessage(error.message), isError: true }));
      }

      if (options.callback) {
        options.callback(error);
      }
    };

    return rewards.claimReward(rewardType, params).then(success, failure);
  };
}

export function doClaimInitialRewards() {
  return (dispatch) => {
    dispatch(doClaimRewardType(rewards.TYPE_NEW_USER));
    dispatch(doClaimRewardType(rewards.TYPE_CONFIRM_EMAIL));
  };
}

export function doClaimEligiblePurchaseRewards() {
  return (dispatch, getState) => {
    const state = getState();
    const unclaimedRewards = selectUnclaimedRewards(state);
    const userIsRewardApproved = selectUserIsRewardApproved(state);

    if (!userIsRewardApproved || !Lbryio.enabled) {
      return;
    }

    if (unclaimedRewards.find((ur) => ur.reward_type === rewards.TYPE_FIRST_STREAM)) {
      dispatch(doClaimRewardType(rewards.TYPE_FIRST_STREAM));
    } else {
      dispatch(doClaimRewardType(rewards.TYPE_MANY_DOWNLOADS, { failSilently: true }));

      if (!selectWeeklyWatchClaimedThisWeek(state)) {
        dispatch(doClaimRewardType(rewards.TYPE_WEEKLY_WATCH, { failSilently: true }));
      }
    }
  };
}

export function doClaimRewardClearError(reward) {
  return (dispatch) => {
    dispatch({
      type: ACTIONS.CLAIM_REWARD_CLEAR_ERROR,
      data: { reward },
    });
  };
}

export function doFetchRewardedContent() {
  return (dispatch) => {
    const success = (nameToClaimId) => {
      dispatch({
        type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED,
        data: {
          claimIds: Object.values(nameToClaimId),
          success: true,
        },
      });
    };

    const failure = () => {
      dispatch({
        type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED,
        data: {
          claimIds: [],
          success: false,
        },
      });
    };

    Lbryio.call('reward', 'list_featured').then(success, failure);
  };
}