import { SUGGESTED_FEATURED, SUGGESTED_TOP_SUBSCRIBED } from 'constants/subscriptions';
import { createSelector } from 'reselect';
import {
  selectAllClaimsByChannel,
  selectClaimsById,
  selectAllFetchingChannelClaims,
  makeSelectChannelForClaimUri,
  selectClaimsByUri,
  parseURI,
  makeSelectClaimForUri,
} from 'lbry-redux';
import { swapKeyAndValue } from 'util/swap-json';

// Returns the entire subscriptions state
const selectState = state => state.subscriptions || {};

// Returns the list of channel uris a user is subscribed to
export const selectSubscriptions = createSelector(
  selectState,
  state => state.subscriptions && state.subscriptions.sort((a, b) => a.channelName.localeCompare(b.channelName))
);

// Fetching list of users subscriptions
export const selectIsFetchingSubscriptions = createSelector(
  selectState,
  state => state.loading
);

// The current view mode on the subscriptions page
export const selectViewMode = createSelector(
  selectState,
  state => state.viewMode
);

// Suggested subscriptions from internal apis
export const selectSuggested = createSelector(
  selectState,
  state => state.suggested
);
export const selectIsFetchingSuggested = createSelector(
  selectState,
  state => state.loadingSuggested
);
export const selectSuggestedChannels = createSelector(
  selectSubscriptions,
  selectSuggested,
  (userSubscriptions, suggested) => {
    if (!suggested) {
      return null;
    }

    // Swap the key/value because we will use the uri for everything, this just makes it easier
    // suggested is returned from the api with the form:
    // {
    //   featured: { "Channel label": uri, ... },
    //   top_subscribed: { "@channel": uri, ... }
    //   top_bid: { "@channel": uri, ... }
    // }
    // To properly compare the suggested subscriptions from our current subscribed channels
    // We only care about the uri, not the label

    // We also only care about top_subscribed and featured
    // top_bid could just be porn or a channel with no content
    const topSubscribedSuggestions = swapKeyAndValue(suggested[SUGGESTED_TOP_SUBSCRIBED]);
    const featuredSuggestions = swapKeyAndValue(suggested[SUGGESTED_FEATURED]);

    // Make sure there are no duplicates
    // If a uri isn't already in the suggested object, add it
    const suggestedChannels = { ...topSubscribedSuggestions };

    Object.keys(featuredSuggestions).forEach(uri => {
      if (!suggestedChannels[uri]) {
        const channelLabel = featuredSuggestions[uri];
        suggestedChannels[uri] = channelLabel;
      }
    });

    userSubscriptions.forEach(({ uri }) => {
      // Note to passer bys:
      // Maybe we should just remove the `lbry://` prefix from subscription uris
      // Most places don't store them like that
      const subscribedUri = uri.slice('lbry://'.length);

      if (suggestedChannels[subscribedUri]) {
        delete suggestedChannels[subscribedUri];
      }
    });

    return Object.keys(suggestedChannels).map(uri => ({
      uri,
      label: suggestedChannels[uri],
    }));
  }
);

export const selectFirstRunCompleted = createSelector(
  selectState,
  state => state.firstRunCompleted
);
export const selectshowSuggestedSubs = createSelector(
  selectState,
  state => state.showSuggestedSubs
);

// Fetching any claims that are a part of a users subscriptions
export const selectSubscriptionsBeingFetched = createSelector(
  selectSubscriptions,
  selectAllFetchingChannelClaims,
  (subscriptions, fetchingChannelClaims) => {
    const fetchingSubscriptionMap = {};
    subscriptions.forEach(sub => {
      const isFetching = fetchingChannelClaims && fetchingChannelClaims[sub.uri];
      if (isFetching) {
        fetchingSubscriptionMap[sub.uri] = true;
      }
    });

    return fetchingSubscriptionMap;
  }
);

export const selectUnreadByChannel = createSelector(
  selectState,
  state => state.unread
);

// Returns the current total of unread subscriptions
export const selectUnreadAmount = createSelector(
  selectUnreadByChannel,
  unreadByChannel => {
    const unreadChannels = Object.keys(unreadByChannel);
    let badges = 0;

    if (!unreadChannels.length) {
      return badges;
    }

    unreadChannels.forEach(channel => {
      badges += unreadByChannel[channel].uris.length;
    });

    return badges;
  }
);

// Returns the uris with channels as an array with the channel with the newest content first
// If you just want the `unread` state, use selectUnread
export const selectUnreadSubscriptions = createSelector(
  selectUnreadAmount,
  selectUnreadByChannel,
  selectClaimsByUri,
  (unreadAmount, unreadByChannel, claimsByUri) => {
    // determine which channel has the newest content
    const unreadList = [];
    if (!unreadAmount) {
      return unreadList;
    }

    const channelUriList = Object.keys(unreadByChannel);

    // There is only one channel with unread notifications
    if (unreadAmount === 1) {
      channelUriList.forEach(channel => {
        const unreadChannel = {
          channel,
          uris: unreadByChannel[channel].uris,
        };
        unreadList.push(unreadChannel);
      });

      return unreadList;
    }

    channelUriList
      .sort((channel1, channel2) => {
        const latestUriFromChannel1 = unreadByChannel[channel1].uris[0];
        const latestClaimFromChannel1 = claimsByUri[latestUriFromChannel1] || {};
        const latestUriFromChannel2 = unreadByChannel[channel2].uris[0];
        const latestClaimFromChannel2 = claimsByUri[latestUriFromChannel2] || {};

        const latestHeightFromChannel1 = latestClaimFromChannel1.height || 0;
        const latestHeightFromChannel2 = latestClaimFromChannel2.height || 0;

        if (latestHeightFromChannel1 !== latestHeightFromChannel2) {
          return latestHeightFromChannel2 - latestHeightFromChannel1;
        }

        return 0;
      })
      .forEach(channel => {
        const unreadSubscription = unreadByChannel[channel];
        const unreadChannel = {
          channel,
          uris: unreadSubscription.uris,
        };

        unreadList.push(unreadChannel);
      });

    return unreadList;
  }
);

// Returns all unread subscriptions for a uri passed in
export const makeSelectUnreadByChannel = uri =>
  createSelector(
    selectUnreadByChannel,
    unread => unread[uri]
  );

// Returns the first page of claims for every channel a user is subscribed to
export const selectSubscriptionClaims = createSelector(
  selectAllClaimsByChannel,
  selectClaimsById,
  selectSubscriptions,
  selectUnreadByChannel,
  (channelIds, allClaims, savedSubscriptions, unreadByChannel) => {
    // no claims loaded yet
    if (!Object.keys(channelIds).length) {
      return [];
    }

    let fetchedSubscriptions = [];

    savedSubscriptions.forEach(subscription => {
      let channelClaims = [];

      // if subscribed channel has content
      if (channelIds[subscription.uri] && channelIds[subscription.uri]['1']) {
        // This will need to be more robust, we will want to be able to load more than the first page

        // Strip out any ids that will be shown as notifications
        const pageOneChannelIds = channelIds[subscription.uri]['1'];

        // we have the channel ids and the corresponding claims
        // loop over the list of ids and grab the claim
        pageOneChannelIds.forEach(id => {
          const grabbedClaim = allClaims[id];

          if (
            unreadByChannel[subscription.uri] &&
            unreadByChannel[subscription.uri].uris.some(uri => uri.includes(id))
          ) {
            grabbedClaim.isNew = true;
          }

          channelClaims = channelClaims.concat([grabbedClaim]);
        });
      }

      fetchedSubscriptions = fetchedSubscriptions.concat(channelClaims);
    });

    return fetchedSubscriptions;
  }
);

// Returns true if a user is subscribed to the channel associated with the uri passed in
// Accepts content or channel uris
export const makeSelectIsSubscribed = uri =>
  createSelector(
    selectSubscriptions,
    makeSelectChannelForClaimUri(uri, true),
    makeSelectClaimForUri(uri),
    (subscriptions, channelUri, claim) => {
      if (channelUri) {
        return subscriptions.some(sub => sub.uri === channelUri);
      }

      // If we couldn't get a channel uri from the claim uri, the uri passed in might be a channel already
      let isChannel;
      try {
        ({ isChannel } = parseURI(uri));
      } catch (e) {}

      if (isChannel && claim) {
        const uri = claim.permanent_url;
        return subscriptions.some(sub => sub.uri === uri);
      }

      return false;
    }
  );

export const makeSelectIsNew = uri =>
  createSelector(
    makeSelectIsSubscribed(uri),
    makeSelectChannelForClaimUri(uri),
    selectUnreadByChannel,
    (isSubscribed, channel, unreadByChannel) => {
      if (!isSubscribed) {
        return false;
      }

      const unreadForChannel = unreadByChannel[`lbry://${channel}`];
      if (unreadForChannel) {
        return unreadForChannel.uris.includes(uri);
      }

      return false;
      // If they are subscribed, check to see if this uri is in the list of unreads
    }
  );
