import { fetchData } from 'mweb/common/fetch/fetchData';
import { VODDetails } from 'mweb/common/reducers/data/vods';
import { formatVideoLength } from 'mweb/common/utils/formatVideoLength';
import { logger } from 'tachyon-logger';
import {
  GQLBroadcastType,
  mapGQLBroadcastTypeToVideoType,
} from 'mweb/common/fetch/vods';

// channelID in the request determines the channel & stream payloads in the response
// streamID in the request determines the video payload in the response
export interface LiveToVODResponse {
  channel: ChannelResponse;
  stream: StreamResponse | null;
  videos: VODResponse[];
}

export interface ChannelResponse {
  status: string;
  display_name: string;
  game: string;
  _id: string;
  name: string;
  logo: string;
}

interface StreamResponse {
  viewers: number;
  _id: string;
  channel: ChannelResponse;
  preview: {
    template: string;
  };
}

export interface VODResponse {
  title: string;
  description: string;
  _id: string; // this is still v-prefixed
  game: string | null;
  broadcast_type: string;
  preview: string;
  created_at: string;
  recorded_at: string;
  views: number;
  length: number;
  channel: {
    name: string;
  };
}

export const GO_TO_CHANNEL_STATUS = 'GO_TO_CHANNEL_STATUS';
export interface GoToChannelStatus {
  status: typeof GO_TO_CHANNEL_STATUS;
  channel: string;
  streamID: string | undefined;
}

export const GO_TO_VOD_STATUS = 'GO_TO_VOD_STATUS';
export interface GoToVODStatus {
  status: typeof GO_TO_VOD_STATUS;
  channel: string;
  vod: VODDetails;
}

export type LiveToVODStatus = GoToChannelStatus | GoToVODStatus;

export function buildLiveToVODStatusURL(
  channelID: string,
  streamID: string,
): string {
  return `https://api.twitch.tv/v5/content?channel_id=${channelID}&stream_id=${streamID}`;
}

export async function fetchLiveToVODStatus(
  channelID: string,
  streamID: string,
): Promise<LiveToVODStatus> {
  const { channel, stream, videos } = await fetchData<LiveToVODResponse>(
    buildLiveToVODStatusURL(channelID, streamID),
  );

  if (stream) {
    const currentStreamID = `${stream._id}`;
    if (currentStreamID === streamID) {
      logger.info(`Stream ${streamID} is still live for channel ${channel}`);
      return {
        status: GO_TO_CHANNEL_STATUS,
        channel: channel.name,
        streamID: currentStreamID,
      } as LiveToVODStatus;
    } else {
      logger.info(
        `Stream ${streamID} is not the current stream for channel ${channel}`,
      );
    }
  }

  if (
    videos &&
    videos[0] &&
    videos[0].channel &&
    videos[0].channel.name === channel.name
  ) {
    const vod = parseVODResponse(videos[0]);
    logger.info(`Found vod ${vod.id} for target stream ${streamID}`);
    return {
      status: GO_TO_VOD_STATUS,
      channel: channel.name,
      vod,
    } as LiveToVODStatus;
  }

  logger.info(`No corresponding vod for stream ${streamID}`);
  return {
    status: GO_TO_CHANNEL_STATUS,
    channel: channel.name,
    streamID: undefined,
  } as LiveToVODStatus;
}

function isGQLBroadcastType(type: string): type is GQLBroadcastType {
  return Object.keys(GQLBroadcastType)
    .map(key => (GQLBroadcastType as any)[key])
    .includes(type);
}

export function parseVODResponse(json: VODResponse): VODDetails {
  const type = json.broadcast_type.toUpperCase();
  const videoType = isGQLBroadcastType(type)
    ? mapGQLBroadcastTypeToVideoType(type)
    : undefined;

  return {
    id: json._id.slice(1),
    channel: json.channel.name,
    title: json.title || '',
    thumbnailURL: json.preview,
    date: new Date(json.recorded_at || json.created_at || 0).valueOf(),
    viewCount: json.views || 0,
    length: json.length || 0,
    formattedLength: formatVideoLength(json.length),
    game: json.game || undefined,
    description: json.description,
    videoType,
  };
}
