import { normalizeURI, buildURI, parseURI } from 'lbryURI';
import { makeSelectCurrentParam } from 'redux/selectors/navigation';
import { selectSearchUrisByQuery } from 'redux/selectors/search';
import { createSelector } from 'reselect';
import { isClaimNsfw } from 'util/claim';

const selectState = state => state.claims || {};

export const selectClaimsById = createSelector(selectState, state => state.byId || {});

export const selectClaimsByUri = createSelector(selectState, selectClaimsById, (state, byId) => {
  const byUri = state.claimsByUri || {};
  const claims = {};

  Object.keys(byUri).forEach(uri => {
    const claimId = byUri[uri];

    // NOTE returning a null claim allows us to differentiate between an
    // undefined (never fetched claim) and one which just doesn't exist. Not
    // the cleanest solution but couldn't think of anything better right now
    if (claimId === null) {
      claims[uri] = null;
    } else {
      claims[uri] = byId[claimId];
    }
  });

  return claims;
});

export const selectAllClaimsByChannel = createSelector(
  selectState,
  state => state.claimsByChannel || {}
);

export const selectPendingById = createSelector(selectState, state => state.pendingById || {});

export const selectPendingClaims = createSelector(selectState, state =>
  Object.values(state.pendingById || [])
);

export const makeSelectClaimIsPending = uri =>
  createSelector(selectPendingById, pendingById => {
    const { claimId } = parseURI(uri);
    return Boolean(pendingById[claimId]);
  });

export const makeSelectPendingByUri = uri =>
  createSelector(selectPendingById, pendingById => {
    const { claimId } = parseURI(uri);
    return pendingById[claimId];
  });

export const makeSelectClaimForUri = uri =>
  createSelector(selectClaimsByUri, selectPendingById, (byUri, pendingById) => {
    // Check if a claim is pending first
    // It won't be in claimsByUri because resolving it will return nothing
    const { claimId } = parseURI(uri);
    const pendingClaim = pendingById[claimId];
    if (pendingClaim) {
      return pendingClaim;
    }

    return byUri && byUri[normalizeURI(uri)];
  });

export const selectMyClaimsRaw = createSelector(selectState, state => state.myClaims);

export const selectAbandoningIds = createSelector(selectState, state =>
  Object.keys(state.abandoningById || {})
);

export const selectMyActiveClaims = createSelector(
  selectMyClaimsRaw,
  selectAbandoningIds,
  (claims, abandoningIds) =>
    new Set(
      claims &&
        claims
          .map(claim => claim.claim_id)
          .filter(claimId => Object.keys(abandoningIds).indexOf(claimId) === -1)
    )
);

export const makeSelectClaimIsMine = rawUri => {
  const uri = normalizeURI(rawUri);
  return createSelector(
    selectClaimsByUri,
    selectMyActiveClaims,
    (claims, myClaims) =>
      claims && claims[uri] && claims[uri].claim_id && myClaims.has(claims[uri].claim_id)
  );
};

export const selectAllFetchingChannelClaims = createSelector(
  selectState,
  state => state.fetchingChannelClaims || {}
);

export const makeSelectFetchingChannelClaims = uri =>
  createSelector(selectAllFetchingChannelClaims, fetching => fetching && fetching[uri]);

export const makeSelectClaimsInChannelForPage = (uri, page) =>
  createSelector(selectClaimsById, selectAllClaimsByChannel, (byId, allClaims) => {
    const byChannel = allClaims[uri] || {};
    const claimIds = byChannel[page || 1];

    if (!claimIds) return claimIds;

    return claimIds.map(claimId => byId[claimId]);
  });

export const makeSelectClaimsInChannelForCurrentPage = uri => {
  const pageSelector = makeSelectCurrentParam('page');

  return createSelector(
    selectClaimsById,
    selectAllClaimsByChannel,
    pageSelector,
    (byId, allClaims, page) => {
      const byChannel = allClaims[uri] || {};
      const claimIds = byChannel[page || 1];

      if (!claimIds) return claimIds;

      return claimIds.map(claimId => byId[claimId]);
    }
  );
};

export const makeSelectMetadataForUri = uri =>
  createSelector(makeSelectClaimForUri(uri), claim => {
    const metadata = claim && claim.value && claim.value.stream && claim.value.stream.metadata;

    return metadata || (claim === undefined ? undefined : null);
  });

export const makeSelectTitleForUri = uri =>
  createSelector(makeSelectMetadataForUri(uri), metadata => metadata && metadata.title);

export const makeSelectContentTypeForUri = uri =>
  createSelector(makeSelectClaimForUri(uri), claim => {
    const source = claim && claim.value && claim.value.stream && claim.value.stream.source;
    return source ? source.contentType : undefined;
  });

export const selectIsFetchingClaimListMine = createSelector(
  selectState,
  state => state.isFetchingClaimListMine
);

export const selectMyClaims = createSelector(
  selectMyActiveClaims,
  selectClaimsById,
  selectAbandoningIds,
  selectPendingClaims,
  (myClaimIds, byId, abandoningIds, pendingClaims) => {
    const claims = [];

    myClaimIds.forEach(id => {
      const claim = byId[id];

      if (claim && abandoningIds.indexOf(id) === -1) claims.push(claim);
    });

    return [...claims, ...pendingClaims];
  }
);

export const selectMyClaimsWithoutChannels = createSelector(selectMyClaims, myClaims =>
  myClaims.filter(claim => !claim.name.match(/^@/))
);

export const selectAllMyClaimsByOutpoint = createSelector(
  selectMyClaimsRaw,
  claims =>
    new Set(claims && claims.length ? claims.map(claim => `${claim.txid}:${claim.nout}`) : null)
);

export const selectMyClaimsOutpoints = createSelector(selectMyClaims, myClaims => {
  const outpoints = [];

  myClaims.forEach(claim => outpoints.push(`${claim.txid}:${claim.nout}`));

  return outpoints;
});

export const selectFetchingMyChannels = createSelector(
  selectState,
  state => state.fetchingMyChannels
);

export const selectMyChannelClaims = createSelector(
  selectState,
  selectClaimsById,
  (state, byId) => {
    const ids = state.myChannelClaims || [];
    const claims = [];

    ids.forEach(id => {
      if (byId[id]) {
        // I'm not sure why this check is necessary, but it ought to be a quick fix for https://github.com/lbryio/lbry-desktop/issues/544
        claims.push(byId[id]);
      }
    });

    return claims;
  }
);

export const selectResolvingUris = createSelector(selectState, state => state.resolvingUris || []);

export const makeSelectIsUriResolving = uri =>
  createSelector(
    selectResolvingUris,
    resolvingUris => resolvingUris && resolvingUris.indexOf(uri) !== -1
  );

export const selectFeaturedUris = createSelector(selectState, state => state.featuredUris);

export const selectFetchingFeaturedUris = createSelector(
  selectState,
  state => state.fetchingFeaturedContent
);

export const selectTrendingUris = createSelector(selectState, state => state.trendingUris);

export const selectFetchingTrendingUris = createSelector(
  selectState,
  state => state.fetchingTrendingContent
);

export const selectPlayingUri = createSelector(selectState, state => state.playingUri);

export const selectChannelClaimCounts = createSelector(
  selectState,
  state => state.channelClaimCounts || {}
);

export const makeSelectTotalItemsForChannel = uri =>
  createSelector(selectChannelClaimCounts, byUri => byUri && byUri[uri]);

export const makeSelectTotalPagesForChannel = (uri, pageSize = 10) =>
  createSelector(
    selectChannelClaimCounts,
    byUri => byUri && byUri[uri] && Math.ceil(byUri[uri] / pageSize)
  );

export const makeSelectNsfwCountFromUris = uris =>
  createSelector(selectClaimsByUri, claims =>
    uris.reduce((acc, uri) => {
      const claim = claims[uri];
      if (isClaimNsfw(claim)) {
        return acc + 1;
      }
      return acc;
    }, 0)
  );

export const makeSelectNsfwCountForChannel = uri => {
  const pageSelector = makeSelectCurrentParam('page');

  return createSelector(
    selectClaimsById,
    selectAllClaimsByChannel,
    pageSelector,
    (byId, allClaims, page) => {
      const byChannel = allClaims[uri] || {};
      const claimIds = byChannel[page || 1];

      if (!claimIds) return 0;

      return claimIds.reduce((acc, claimId) => {
        const claim = byId[claimId];
        if (isClaimNsfw(claim)) {
          return acc + 1;
        }
        return acc;
      }, 0);
    }
  );
};

export const makeSelectRecommendedContentForUri = uri =>
  createSelector(
    makeSelectClaimForUri(uri),
    selectSearchUrisByQuery,
    (claim, searchUrisByQuery) => {
      const atVanityURI = !uri.includes('#');

      let recommendedContent;
      if (claim) {
        // If we are at a vanity uri, build the full uri so we can properly filter
        const currentUri = atVanityURI
          ? buildURI({ claimId: claim.claim_id, claimName: claim.name })
          : uri;

        const { title } = claim.value.stream.metadata;

        let searchUris = searchUrisByQuery[title.replace(/\//, ' ')];
        if (searchUris) {
          searchUris = searchUris.filter(searchUri => searchUri !== currentUri);
          recommendedContent = searchUris;
        }
      }

      return recommendedContent;
    }
  );

export const makeSelectFirstRecommendedFileForUri = uri =>
  createSelector(
    makeSelectRecommendedContentForUri(uri),
    recommendedContent => (recommendedContent ? recommendedContent[0] : null)
  );

// Returns the associated channel uri for a given claim uri
export const makeSelectChannelForClaimUri = (uri, includePrefix = false) =>
  createSelector(makeSelectClaimForUri(uri), claim => {
    if (!claim) {
      return null;
    }

    const { channel_name: channelName, value } = claim;
    const channelClaimId =
      value && value.publisherSignature && value.publisherSignature.certificateId;

    return channelName && channelClaimId
      ? buildURI({ channelName, claimId: channelClaimId }, includePrefix)
      : null;
  });