import {
  getSpadeDataWithTimeAndLocation,
  SpadeDataWithTimeAndLocation,
} from 'mweb/common/selectors/tracking/base';
import {
  RootState,
  getCurrentChannelForChannelViewer,
  getCurrentVODForVODViewer,
  getCurrentChannelForVODViewer,
  getCurrentChannelForChatEmbed,
  getCurrentChannelForProfile,
} from 'mweb/common/reducers/root';
import { Location } from 'mweb/common/reducers/app';
import { ChannelOnlineStatus } from 'mweb/common/reducers/data/channels';
import { VideoType } from 'mweb/common/reducers/data/baseVideoDetails';
import {
  getProfilePageContent,
  isChannelDetails,
} from 'mweb/common/selectors/pages/channelProfile';
import { getCurrentGameForChannelDirectory } from 'mweb/common/selectors/pages/channelDirectory';

interface PageViewSpadeData extends SpadeDataWithTimeAndLocation {
  readonly referrer_url: string | undefined;
}

interface GameDirectoryPageViewSpadeData extends PageViewSpadeData {}

interface ChannelDirectoryPageViewSpadeData extends PageViewSpadeData {
  readonly game: string | undefined;
}

interface ChannelPageViewSpadeData extends PageViewSpadeData {
  readonly channel: string;
  readonly channel_id: number | undefined;
  readonly game: string | undefined;
  readonly player: 'embed';
  readonly is_live: boolean;
}

interface ChannelProfilePageViewSpadeData extends PageViewSpadeData {
  readonly channel: string;
  readonly channel_id: number | undefined;
}

interface ChatEmbedPageViewSpadeData extends PageViewSpadeData {
  readonly channel: string;
  readonly channel_id: number | undefined;
}

interface VODPageViewSpadeData extends PageViewSpadeData {
  readonly vod_id: string | undefined;
  readonly vod_type: string | undefined;
  readonly channel: string | undefined;
  readonly channel_id: number | undefined;
  readonly game: string | undefined;
  readonly player: 'embed';
}

interface UpsellPageViewSpadeData extends PageViewSpadeData {}

interface EventDetailsPageViewSpadeData extends PageViewSpadeData {}

const IS_INTERNAL_HOST = /(?:.+\.)?m\.twitch\.tv$/;
export function getBasePageViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): PageViewSpadeData {
  let referrer_url;
  if (referrer) {
    const hostname = new URL(referrer).hostname;
    if (!IS_INTERNAL_HOST.test(hostname)) {
      referrer_url = referrer;
    }
  }

  return {
    ...getSpadeDataWithTimeAndLocation(state),
    referrer_url,
  };
}

export function getGameDirectoryViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): GameDirectoryPageViewSpadeData {
  return {
    ...getBasePageViewSpadeData(state, referrer),
  };
}

export function getEventDetailsViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): EventDetailsPageViewSpadeData {
  return {
    ...getBasePageViewSpadeData(state, referrer),
  };
}
export function getChannelDirectoryViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): ChannelDirectoryPageViewSpadeData {
  const game = getCurrentGameForChannelDirectory(state);
  return {
    ...getBasePageViewSpadeData(state, referrer),
    game: (game && game.name) || state.pages.channelDirectory.currentGameAlias,
  };
}

export function getChannelPageViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): ChannelPageViewSpadeData {
  const channel = getCurrentChannelForChannelViewer(state);
  return {
    ...getBasePageViewSpadeData(state, referrer),
    channel: state.pages.channelViewer.currentChannel,
    channel_id: channel && parseInt(channel.id),
    game: channel && channel.game,
    player: 'embed',
    is_live: !!channel && channel.onlineStatus === ChannelOnlineStatus.Online,
  };
}

export function getChannelProfilePageViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): ChannelProfilePageViewSpadeData {
  const channel = getCurrentChannelForProfile(state);
  return {
    ...getBasePageViewSpadeData(state, referrer),
    channel: state.pages.channelProfile.currentChannel,
    channel_id: channel && parseInt(channel.id),
  };
}

export function getChatEmbedPageViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): ChatEmbedPageViewSpadeData {
  const channel = getCurrentChannelForChatEmbed(state);
  return {
    ...getBasePageViewSpadeData(state, referrer),
    channel: state.pages.chatEmbed.currentChannel,
    channel_id: channel && parseInt(channel.id),
  };
}

export function getVODPageViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): VODPageViewSpadeData {
  const vod = getCurrentVODForVODViewer(state);
  const channel = getCurrentChannelForVODViewer(state);
  return {
    ...getBasePageViewSpadeData(state, referrer),
    vod_id: vod && `v${vod.id}`,
    vod_type: vod && vod.videoType,
    channel: vod && vod.channel,
    channel_id: channel && parseInt(channel.id),
    game: vod && vod.game,
    player: 'embed',
  };
}

export function getUpsellPageViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): UpsellPageViewSpadeData {
  return {
    ...getBasePageViewSpadeData(state, referrer),
  };
}

function unknownLocationForPageView(location: never): never;
function unknownLocationForPageView(_: Location): undefined {
  return undefined;
}

export function getPageViewSpadeData(
  state: RootState,
  referrer: string | undefined,
): PageViewSpadeData {
  switch (state.app.location) {
    case Location.Channel:
      return getChannelPageViewSpadeData(state, referrer);
    case Location.VOD:
      return getVODPageViewSpadeData(state, referrer);
    case Location.DirectoryGame:
      return getChannelDirectoryViewSpadeData(state, referrer);
    case Location.DirectoryMainGame:
      return getGameDirectoryViewSpadeData(state, referrer);
    case Location.Upsell:
      return getUpsellPageViewSpadeData(state, referrer);
    case Location.EventDetails:
      return getEventDetailsViewSpadeData(state, referrer);
    case Location.ChannelProfile:
      return getChannelProfilePageViewSpadeData(state, referrer);
    case Location.ChatEmbed:
      return getChatEmbedPageViewSpadeData(state, referrer);
    case Location.Unknown:
      throw 'Attempted to log pageview for unknown location';
    default:
      return unknownLocationForPageView(state.app.location);
  }
}

// this is for the profile_page_view event, which is distinct from page_view
interface ProfilePageMetricsSpadeData extends SpadeDataWithTimeAndLocation {
  readonly archive_count: number;
  readonly clip_count: number;
  readonly highlight_count: number;
  readonly premiere_count: number;
  readonly upload_count: number;
  readonly is_live: boolean;
  readonly top_content: VideoType | undefined;
  readonly info_card_content: VideoType | undefined;
}

export function getProfileMetricsSpadeData(
  state: RootState,
): ProfilePageMetricsSpadeData {
  const channel = getCurrentChannelForProfile(state);
  const {
    archives,
    recentClips,
    recentHighlights,
    otherVideos,
    featuredContent,
    infoCardVideo,
  } = getProfilePageContent(state);

  const clip_count =
    infoCardVideo && infoCardVideo.videoType === VideoType.Clip
      ? recentClips.filter(clip => clip.id !== infoCardVideo.id).length + 1
      : recentClips.length;

  const highlight_count =
    infoCardVideo && infoCardVideo.videoType === VideoType.Highlight
      ? recentHighlights.filter(highlight => highlight.id !== infoCardVideo.id)
          .length + 1
      : recentHighlights.length;

  const premiere_count = otherVideos.filter(
    video => video.videoType === VideoType.PastPremiere,
  ).length;

  const upload_count = otherVideos.filter(
    video => video.videoType === VideoType.Upload,
  ).length;

  const counts = {
    archive_count: archives.length,
    clip_count,
    highlight_count,
    premiere_count,
    upload_count,
  };

  if (featuredContent && !isChannelDetails(featuredContent)) {
    (counts as Indexable<number>)[`${featuredContent.videoType}_count`]++;
  }

  return {
    ...getSpadeDataWithTimeAndLocation(state),
    ...counts,
    is_live: !!channel && channel.onlineStatus === ChannelOnlineStatus.Online,
    top_content:
      featuredContent && !isChannelDetails(featuredContent)
        ? featuredContent.videoType
        : undefined,
    info_card_content: infoCardVideo && infoCardVideo.videoType,
  };
}
