import { fetchGQL } from 'mweb/common/fetch/fetchGQL';
import { GameDetails, GameDetailsMap } from 'mweb/common/reducers/data/games';

const GameFragment: string = require('mweb/common/fetch/fragments/game');
const GamesQuery: string = require('mweb/common/fetch/queries/games');

const GAME_PAGE_SIZE = 40;
const UNKNOWN_BOX_ART_TEMPLATE =
  'https://static-cdn.jtvnw.net/ttv-static/404_boxart-{width}x{height}.jpg';

export interface GameGQL {
  id: string;
  name: string;
  displayName: string;
  boxArtURL: string | null;
  viewersCount: number | null;
}

interface GameNodeGQL {
  cursor: string;
  node: GameGQL;
}

export interface GamesGQL {
  data: {
    games: {
      edges: GameNodeGQL[];
    };
  };
}

interface FetchGamesOpts {
  cursor: string | null;
  count?: number;
}

export function compileFetchGamesOperation(): string {
  return `${GamesQuery}
  ${GameFragment}
  `;
}

export async function fetchGames(
  opts: FetchGamesOpts,
): Promise<GamesDataPayload> {
  const gamesGQL = await fetchGQL<GamesGQL>(
    compileFetchGamesOperation(),
    { count: GAME_PAGE_SIZE, ...opts },
    'GamesQuery',
  );
  return parseGamesGQL(gamesGQL);
}

// mutable type for use in reduce accumulator
interface GameDetailsAccumulator {
  [gameName: string]: GameDetails;
}

export interface GamesDataPayload {
  gameDetails: GameDetailsMap;
  lastGameCursor: string | null;
}

export function parseGameGQL(node: GameGQL): GameDetails {
  return {
    id: node.id,
    name: node.name,
    displayName: node.displayName,
    boxArtURL: node.boxArtURL || UNKNOWN_BOX_ART_TEMPLATE,
    viewersCount: node.viewersCount || 0,
  };
}

export function parseGamesGQL(json: GamesGQL): GamesDataPayload {
  const entries = json.data.games.edges;
  const gameDetailsMap: GameDetailsMap = entries.reduce(
    (acc, { node }) => {
      if (node) {
        acc[node.name] = parseGameGQL(node);
      }
      return acc;
    },
    {} as GameDetailsAccumulator,
  );
  const lastGameCursor =
    entries.length > 0 ? entries[entries.length - 1].cursor : null;
  return {
    gameDetails: gameDetailsMap,
    lastGameCursor,
  };
}
