import { fetchGQL } from 'mweb/common/fetch/fetchGQL';
import {
  ChannelGQL,
  mapChannelGQLtoChannelDetails,
} from 'mweb/common/fetch/channels';
import { VODDetails } from 'mweb/common/reducers/data/vods';
import { ChannelDetails } from 'mweb/common/reducers/data/channels';
import { formatVideoLength } from 'mweb/common/utils/formatVideoLength';
import {
  VideoType,
  VODVideoType,
} from 'mweb/common/reducers/data/baseVideoDetails';

const VideoWithChannelFragment: string = require('mweb/common/fetch/fragments/videoWithChannel');
const CoreChannelFragment: string = require('mweb/common/fetch/fragments/coreChannel');
const ChannelSubscriptionFragment: string = require('mweb/common/fetch/fragments/channelSubscription');
const GameFragment: string = require('mweb/common/fetch/fragments/game');
const VideoFragment: string = require('mweb/common/fetch/fragments/video');
const StreamFragment: string = require('mweb/common/fetch/fragments/stream');
const VideoQuery: string = require('mweb/common/fetch/queries/video');

export enum GQLBroadcastType {
  Archive = 'ARCHIVE',
  Highlight = 'HIGHLIGHT',
  Upload = 'UPLOAD',
  Premiere = 'PAST_PREMIERE',
}

// In cases where typed without `| null`, it's because graphql
// is inconsistent and returns empty strings instead of null
export interface VideoGQL {
  id: string;
  previewThumbnailURL: string;
  title: string | null;
  viewCount: number | null;
  lengthSeconds: number | null;
  broadcastType: GQLBroadcastType | null;
  description: string | null;
  recordedAt: string | null;
  createdAt: string | null;
  game: {
    name: string;
    id: string;
  } | null;
  owner?: ChannelGQL | null;
}

export interface FetchVideoGQL {
  data: {
    video: VideoGQL;
  };
}

export interface VODDataPayload {
  vod: VODDetails;
  channel: ChannelDetails | undefined;
}

export function compileFetchVODOperation(): string {
  return `${VideoQuery}
    ${VideoWithChannelFragment}
    ${VideoFragment}
    ${CoreChannelFragment}
    ${ChannelSubscriptionFragment}
    ${StreamFragment}
    ${GameFragment}
  `;
}

export async function fetchVOD(
  vodID: string,
): Promise<VODDataPayload | undefined> {
  const videoGQL = await fetchGQL<FetchVideoGQL>(
    compileFetchVODOperation(),
    {
      vodID,
    },
    'VideoQuery',
  );
  return parseVideoGQL(videoGQL);
}

function parseVideoGQL({
  data: { video },
}: FetchVideoGQL): VODDataPayload | undefined {
  if (!video || !video.id) {
    // graphql returns "" for id if video doesn't exist
    return undefined;
  }
  return {
    vod: mapVideoGQLToVODDetails(video),
    channel: video.owner
      ? mapChannelGQLtoChannelDetails(video.owner)
      : undefined,
  };
}

export function mapVideoGQLToVODDetails(
  video: VideoGQL,
  channel?: string,
): VODDetails {
  return {
    id: video.id,
    channel: (video.owner && video.owner.login) || channel,
    thumbnailURL: video.previewThumbnailURL,
    description: video.description || undefined,
    game: video.game ? video.game.name : undefined,
    title: video.title || undefined,
    viewCount: video.viewCount || 0,
    length: video.lengthSeconds || 0,
    formattedLength: formatVideoLength(video.lengthSeconds || 0),
    date: new Date(video.recordedAt || video.createdAt || 0).valueOf(),
    videoType: mapGQLBroadcastTypeToVideoType(video.broadcastType),
  };
}

/**
 * Maps provided GQLBroadcastType into a VideoType. This transformation allows
 * us to have to fold both VODs and Clips into the same BaseVideoDetails.
 * VideoType supports both vods and clips whereas GQLBroadcastType is an
 * specifically for VODs.
 *
 * @param type GQLBroadcastType to transform
 */
export function mapGQLBroadcastTypeToVideoType(
  type: GQLBroadcastType | null,
): VODVideoType | undefined {
  if (!type) {
    return undefined;
  }
  switch (type) {
    case GQLBroadcastType.Archive:
      return VideoType.Archive;
    case GQLBroadcastType.Highlight:
      return VideoType.Highlight;
    case GQLBroadcastType.Upload:
      return VideoType.Upload;
    case GQLBroadcastType.Premiere:
      return VideoType.PastPremiere;
    default:
      return undefined;
  }
}
