import {
  getVODsForChannel,
  getClipsForChannel,
  RootState,
  VODSortTarget,
} from 'mweb/common/reducers/root';
import { getChannelDetails } from 'mweb/common/selectors/data/channels';
import {
  ChannelOnlineStatus,
  ChannelDetails,
} from 'mweb/common/reducers/data/channels';
import {
  BaseVideoDetails,
  VideoType,
} from 'mweb/common/reducers/data/baseVideoDetails';
import { VODDetails } from 'mweb/common/reducers/data/vods';
import { ClipDetails } from 'mweb/common/reducers/data/clips';
import { Enum } from 'mweb/common/utils/enum';

enum ChannelProfileContentType {
  Archives = 'archives',
  RecentHighlights = 'recentHighlights',
  RecentClips = 'recentClips',
  TopClips = 'topClips',
  TopHighlights = 'topHighlights',
  RecentPremieresAndUploads = 'recentPremieresAndUploads',
}

/**
 * Returns whether or not the user has landed on the channelProfile page
 * because of an offline redirect (User tries to open stream -> stream is
 * offline -> redirected to profile page).
 *
 * @param state store state
 */
export function redirectedAsOffline(state: RootState): boolean {
  return state.pages.channelProfile.redirectedAsOffline;
}

export function showChannelOfflineToast(state: RootState): boolean {
  const channelName = state.pages.channelProfile.currentChannel;
  const channel = getChannelDetails(state, channelName);
  if (!channel) {
    return false;
  }

  return (
    !showProfileEmptyState(state) &&
    channel.onlineStatus === ChannelOnlineStatus.Offline &&
    redirectedAsOffline(state)
  );
}

export function showProfileEmptyState(state: RootState): boolean {
  const content = getProfilePageContent(state);
  return (
    content.archives.length === 0 &&
    content.recentClips.length === 0 &&
    content.recentHighlights.length === 0 &&
    content.otherVideos.length === 0 &&
    !content.featuredContent
  );
}

export function hasReceivedVodsAndClipsForChannel(state: RootState): boolean {
  const channelName = state.pages.channelProfile.currentChannel;
  return (
    !!state.data.clips.clipsByChannelLoadStatus[channelName] &&
    !!state.data.vods.vodsByChannelLoadStatus[channelName]
  );
}

export type ChannelProfileContentCounts = {
  [K in ChannelProfileContentType]: number
};

type ChannelProfileVideos = {
  [K in ChannelProfileContentType]: BaseVideoDetails[]
};

export type FeaturedContent = BaseVideoDetails | ChannelDetails;

export function isChannelDetails(
  content: FeaturedContent,
): content is ChannelDetails {
  return (content as ChannelDetails).onlineStatus !== undefined;
}

export interface ChannelProfileContent {
  featuredContent: FeaturedContent | undefined;
  targetVideoCounts: ChannelProfileContentCounts;
  archives: VODDetails[];
  recentClips: ClipDetails[];
  recentHighlights: VODDetails[];
  otherVideos: VODDetails[];
  infoCardVideo: BaseVideoDetails | undefined;
}

function getProfileFeaturedContentType(): ChannelProfileContentType {
  // can be varied on experiments in the future
  return ChannelProfileContentType.Archives;
}

function getProfileVideoListSize(type: ChannelProfileContentType): number {
  // can be varied on experiments in the future
  switch (type) {
    case ChannelProfileContentType.Archives:
    case ChannelProfileContentType.RecentClips:
    case ChannelProfileContentType.RecentHighlights:
    case ChannelProfileContentType.RecentPremieresAndUploads:
      return 6;
    case ChannelProfileContentType.TopClips:
    case ChannelProfileContentType.TopHighlights:
      return 1;
  }
}

export function getProfileContentTargetVideoCounts(): ChannelProfileContentCounts {
  const featuredType = getProfileFeaturedContentType();
  return Enum.values(ChannelProfileContentType).reduce(
    (prev, type) => {
      return {
        ...prev,
        [type]: getProfileVideoListSize(type) + (featuredType === type ? 1 : 0),
      };
    },
    {} as ChannelProfileContentCounts,
  );
}

function getProfilePagePremieresAndUploads(
  state: RootState,
): BaseVideoDetails[] {
  const channelName = state.pages.channelProfile.currentChannel;
  const count = getProfileContentTargetVideoCounts()[
    ChannelProfileContentType.RecentPremieresAndUploads
  ];
  const uploads = getVODsForChannel(state, {
    sortTarget: VODSortTarget.Date,
    videoType: VideoType.Upload,
    channelName,
    count,
  });
  const premieres = getVODsForChannel(state, {
    sortTarget: VODSortTarget.Date,
    videoType: VideoType.PastPremiere,
    channelName,
    count,
  });

  return uploads
    .concat(premieres)
    .sort((a, b) => b.date - a.date)
    .slice(0, count);
}

export function getProfilePageContent(state: RootState): ChannelProfileContent {
  const channelName = state.pages.channelProfile.currentChannel;
  const featuredType = getProfileFeaturedContentType();
  const targetVideoCounts = getProfileContentTargetVideoCounts();

  const videos: ChannelProfileVideos = {
    archives: getVODsForChannel(state, {
      count: targetVideoCounts[ChannelProfileContentType.Archives],
      videoType: VideoType.Archive,
      sortTarget: VODSortTarget.Date,
      channelName,
    }),
    recentClips: getClipsForChannel(state, {
      count: targetVideoCounts[ChannelProfileContentType.RecentClips],
      filterForLastWeek: true,
      channelName,
    }),
    recentHighlights: getVODsForChannel(state, {
      count: targetVideoCounts[ChannelProfileContentType.RecentHighlights],
      sortTarget: VODSortTarget.Date,
      videoType: VideoType.Highlight,
      channelName,
    }),
    topClips: getClipsForChannel(state, {
      count: targetVideoCounts[ChannelProfileContentType.TopClips],
      filterForLastWeek: false,
      channelName,
    }),
    topHighlights: getVODsForChannel(state, {
      count: targetVideoCounts[ChannelProfileContentType.TopHighlights],
      sortTarget: VODSortTarget.ViewCount,
      videoType: VideoType.Highlight,
      channelName,
    }),
    recentPremieresAndUploads: getProfilePagePremieresAndUploads(state),
  };

  const featuredVideo = (videos as Indexable<BaseVideoDetails[]>)[
    featuredType
  ].shift();

  const infoCardVideo =
    videos[ChannelProfileContentType.TopHighlights][0] ||
    videos[ChannelProfileContentType.TopClips][0];

  return {
    archives: videos[ChannelProfileContentType.Archives] as VODDetails[],
    recentClips: videos[ChannelProfileContentType.RecentClips] as ClipDetails[],
    recentHighlights: videos[
      ChannelProfileContentType.RecentHighlights
    ] as VODDetails[],
    otherVideos: videos[
      ChannelProfileContentType.RecentPremieresAndUploads
    ] as VODDetails[],
    featuredContent:
      getFeaturedChannelDetails(state, channelName) || featuredVideo,
    infoCardVideo,
    targetVideoCounts,
  };
}

/**
 * Get the channel details for the featured content if content is live.
 *
 * @param state
 * @param channelName The name of the channel to retrieve.
 */
function getFeaturedChannelDetails(
  state: RootState,
  channelName: string,
): ChannelDetails | undefined {
  const channelDetails = getChannelDetails(state, channelName);
  if (
    channelDetails &&
    channelDetails.onlineStatus === ChannelOnlineStatus.Online
  ) {
    if (channelDetails.hostedChannel) {
      return getChannelDetails(state, channelDetails.hostedChannel);
    }
    return channelDetails;
  }
}
