import times from 'lodash-es/times';
import faker from 'faker';

import { mockGQL } from 'mtest/fetchMocks/mockGQL';
import { makeFakeGame } from 'mtest/fetchMocks/games';

import {
  FetchChannelGQL,
  ChannelGQL,
  ChannelDataPayload,
} from 'mweb/common/fetch/channels';
import {
  GQLBroadcastType,
  mapGQLBroadcastTypeToVideoType,
} from 'mweb/common/fetch/vods';
import { VideoType } from 'mweb/common/reducers/data/baseVideoDetails';
import {
  ChannelDetails,
  ChannelOnlineStatus,
  ChannelSubscribableStatus,
} from 'mweb/common/reducers/data/channels';
import {
  VODDetailsMap,
  MutableVODDetailsMap,
} from 'mweb/common/reducers/data/vods';
import {
  ClipDetailsMap,
  MutableClipDetailsMap,
} from 'mweb/common/reducers/data/clips';
import { formatVideoLength } from 'mweb/common/utils/formatVideoLength';

export const CHANNEL = 'twitch';
export const NOT_CHANNEL = 'fewd';

export function makeFakeChannel(
  opts: Partial<ChannelDetails> = {},
): ChannelDetails {
  const name = opts.name || faker.hacker.noun();
  return {
    name,
    displayName: name.toUpperCase(),
    id: String(faker.random.number()),
    onlineStatus: faker.helpers.randomize([
      ChannelOnlineStatus.Online,
      ChannelOnlineStatus.Offline,
    ]),
    viewerCount: faker.random.number(),
    subscribableStatus: faker.helpers.randomize([
      ChannelSubscribableStatus.CanSubscribe,
      ChannelSubscribableStatus.CannotSubscribe,
    ]),
    followerCount: faker.random.number(),
    lifetimeViewerCount: faker.random.number(),
    preview: faker.image.imageUrl(90, 90),
    status: faker.lorem.words(5),
    logoURL: faker.image.avatar(),
    game: faker.random.words(4),
    hostedChannel: undefined,
    description: faker.lorem.paragraph(3),
    bannerImageURL: faker.image.avatar(),
    ...opts,
  };
}

export function makeFakeChannelGQL(login: string): ChannelGQL {
  return {
    login,
    bannerImageURL: faker.image.avatar(),
    profileViewCount: faker.random.number(),
    stream: {
      viewersCount: faker.random.number(),
      previewImageURL: faker.image.imageUrl(90, 90),
      title: faker.lorem.words(5),
      id: '99',
      game: makeFakeGame(),
    },
    description: faker.lorem.paragraph(3),
    followers: {
      totalCount: faker.random.number(),
    },
    displayName: faker.hacker.noun(),
    id: '2',
    profileImageURL: faker.image.avatar(),
    hosting: {
      login: '',
      bannerImageURL: faker.image.avatar(),
      profileViewCount: faker.random.number(),
      stream: {
        viewersCount: faker.random.number(),
        previewImageURL: faker.image.imageUrl(90, 90),
        title: faker.lorem.words(5),
        id: '99',
        game: makeFakeGame(),
      },
      description: faker.lorem.paragraph(3),
      followers: {
        totalCount: faker.random.number(),
      },
      displayName: faker.hacker.noun(),
      id: '2',
      profileImageURL: faker.image.avatar(),
    },
  };
}

export function makeFakeFetchChannelGQL(
  login: string,
  videos: number = 3,
  highlights: number = 3,
  clips: number = 3,
  includeSubs: boolean = true,
): FetchChannelGQL {
  return {
    data: {
      user: {
        ...makeFakeChannelGQL(login),
        subscriptionProducts: includeSubs
          ? [{ id: faker.random.alphaNumeric() }]
          : [],
        archives: {
          edges: times(videos, () => ({
            node: {
              broadcastType: GQLBroadcastType.Archive,
              description: faker.lorem.sentence(),
              game: makeFakeGame(),
              id: `v${faker.random.number()}`,
              title: faker.lorem.words(20),
              previewThumbnailURL: faker.image.technics(),
              viewCount: faker.random.number(),
              lengthSeconds: faker.random.number(),
              recordedAt: faker.date.past().toString(),
              createdAt: faker.date.past().toString(),
            },
          })),
        },
        recentHighlights: {
          edges: times(highlights, () => ({
            node: {
              broadcastType: GQLBroadcastType.Highlight,
              description: faker.lorem.sentence(),
              game: makeFakeGame(),
              id: `v${faker.random.number()}`,
              title: faker.lorem.words(20),
              previewThumbnailURL: faker.image.technics(),
              viewCount: faker.random.number(),
              lengthSeconds: faker.random.number(),
              recordedAt: faker.date.past().toString(),
              createdAt: faker.date.past().toString(),
            },
          })),
        },
        recentClips: {
          edges: times(clips, () => ({
            node: {
              id: faker.random.alphaNumeric(),
              title: faker.lorem.words(20),
              thumbnailURL: faker.image.technics(),
              url: faker.internet.url(),
              createdAt: faker.date.past().toString(),
              viewCount: faker.random.number(),
              durationSeconds: faker.random.number(),
              game: makeFakeGame(),
            },
          })),
        },
      },
    },
  };
}

export const CHANNEL_JSON: FetchChannelGQL = makeFakeFetchChannelGQL(CHANNEL);
export const CHANNEL_JSON_NO_SUBS: FetchChannelGQL = makeFakeFetchChannelGQL(
  CHANNEL,
  0,
  0,
  0,
  false,
);

export const CHANNEL_NOT_FOUND_JSON: FetchChannelGQL = {
  data: {
    user: {
      stream: null,
      displayName: null,
      id: null,
      login: null,
      profileImageURL: null,
      profileViewCount: null,
      hosting: null,
      description: null,
      bannerImageURL: null,
      followers: null,
      archives: {
        edges: [],
      },
      recentHighlights: {
        edges: [],
      },
      recentClips: {
        edges: [],
      },
      subscriptionProducts: [],
    },
  },
};

const USER = CHANNEL_JSON.data.user!;

export const CHANNEL_DETAILS: ChannelDetails = {
  displayName: USER.displayName!,
  game: USER.stream!.game!.name,
  hostedChannel: undefined,
  id: USER.id!,
  logoURL: USER.profileImageURL!,
  name: USER.login!,
  onlineStatus: ChannelOnlineStatus.Online,
  preview: USER.stream!.previewImageURL!,
  status: USER.stream!.title!,
  viewerCount: USER.stream!.viewersCount!,
  description: USER.description!,
  bannerImageURL: USER.bannerImageURL!,
  followerCount: USER.followers!.totalCount,
  lifetimeViewerCount: USER.profileViewCount!,
  subscribableStatus: ChannelSubscribableStatus.CanSubscribe,
};

export const CHANNEL_DETAILS_NO_SUB: ChannelDetails = {
  displayName: USER.displayName!,
  game: USER.stream!.game!.name,
  hostedChannel: undefined,
  id: USER.id!,
  logoURL: USER.profileImageURL!,
  name: USER.login!,
  onlineStatus: ChannelOnlineStatus.Online,
  preview: USER.stream!.previewImageURL!,
  status: USER.stream!.title!,
  viewerCount: USER.stream!.viewersCount!,
  description: USER.description!,
  bannerImageURL: USER.bannerImageURL!,
  followerCount: USER.followers!.totalCount,
  lifetimeViewerCount: USER.profileViewCount!,
  subscribableStatus: ChannelSubscribableStatus.CannotSubscribe,
};

const CHANNEL_VIDEOS: VODDetailsMap = USER.archives!.edges
  .concat(USER.recentHighlights!.edges)
  .reduce(
    (agg, { node: video }) => {
      agg[video.id] = {
        videoType: mapGQLBroadcastTypeToVideoType(video.broadcastType),
        channel: USER.login!,
        description: video.description!,
        game: video!.game!.name,
        id: video.id,
        title: video.title!,
        date: new Date(video.recordedAt!).valueOf(),
        thumbnailURL: video.previewThumbnailURL,
        length: video.lengthSeconds!,
        formattedLength: formatVideoLength(video.lengthSeconds!),
        viewCount: video.viewCount!,
      };
      return agg;
    },
    {} as MutableVODDetailsMap,
  );

const CHANNEL_CLIPS: ClipDetailsMap = USER.recentClips!.edges.reduce(
  (agg, { node: clip }) => {
    agg[clip.id] = {
      videoType: VideoType.Clip,
      id: clip.id,
      title: clip.title,
      thumbnailURL: clip.thumbnailURL,
      url: clip.url,
      date: new Date(clip.createdAt).valueOf(),
      viewCount: clip.viewCount,
      game: clip.game!.name,
      channel: USER.login!,
      length: clip.durationSeconds,
      formattedLength: formatVideoLength(clip.durationSeconds),
    };
    return agg;
  },
  {} as MutableClipDetailsMap,
);

export const CHANNEL_DATA_PAYLOAD: ChannelDataPayload = {
  channel: CHANNEL_DETAILS,
  hostedChannel: undefined,
  videos: CHANNEL_VIDEOS,
  clips: CHANNEL_CLIPS,
};

export function mockChannel(): void {
  mockGQL(CHANNEL_JSON);
}

export function mockStatusNotFound(): void {
  // GQL returns a channel with a bunch of null fields instead of a null channel.
  mockGQL(CHANNEL_NOT_FOUND_JSON);
}

export function mockChannelWithoutSubs(): void {
  mockGQL(CHANNEL_JSON_NO_SUBS);
}

export function mockChannelBanned(): void {
  mockGQL(CHANNEL_NOT_FOUND_JSON);
}
