export const BRANCH_CHANNEL = 'mobile_web';

/**
 * Supported Branch.io event names that can be passed to `branch.track`
 */
export enum BranchEvent {
  PageView = 'pageview',
  FiveMinutePlay = '5mp',
}

/**
 * Type of Branch journey to display. In the branch.io dashboard, this
 * correlated with the metadata selector for 'eligible_for`. For example,
 * BranchJourneyType.Live would try to display a Journey with
 * `elgibile_for=live`
 */
export enum BranchJourneyType {
  Live = 'live',
  MainDir = 'main_dir',
  GameDir = 'game_dir',
  Vod = 'vod',
  Profile = 'profile',
}

export interface BranchLiveJourney {
  type: BranchJourneyType.Live;
  channel: string;
}

export interface BranchMainDirJourney {
  type: BranchJourneyType.MainDir;
}

export interface BranchGameDirJourney {
  type: BranchJourneyType.GameDir;
  game: string;
}

export interface BranchVodJourney {
  type: BranchJourneyType.Vod;
  channel: string | undefined;
}

export interface BranchProfileJourney {
  type: BranchJourneyType.Profile;
  channel: string;
}

export type BranchJourney =
  | BranchLiveJourney
  | BranchMainDirJourney
  | BranchGameDirJourney
  | BranchVodJourney
  | BranchProfileJourney;

function isBranchAvailable(): boolean {
  return typeof window !== 'undefined' && !!window.branch;
}

export function trackBranchEvent(eventName: BranchEvent, data?: object): void {
  if (isBranchAvailable()) {
    window.branch.track(eventName, data);
  }
}

/**
 * Asynchronously creates a Branch.io deeplink URL
 *
 * @param feature feature of app that the link is associated with, used for tracking
 * @param sessionID session ID of app that the link is associated with
 * @param deviceID device ID of app that the link is associated with
 *
 * @returns promise that resolves with Branch.io URL
 */
export function fetchBranchURL(
  feature: string,
  sessionID: string,
  deviceID: string,
): Promise<string> {
  return new Promise((resolve, reject) => {
    if (!isBranchAvailable()) {
      reject();
    }

    window.branch.link(
      {
        feature,
        channel: BRANCH_CHANNEL,
        data: {
          app_session_id: sessionID,
          device_id: deviceID,
        },
      },
      (err, url) => {
        if (url) {
          resolve(url);
        } else {
          reject(err);
        }
      },
    );
  });
}

function handleUnknown(type: never): never;
function handleUnknown(_: BranchJourney): undefined {
  return undefined;
}

export function showBranchJourney(
  journey: BranchJourney,
  sessionID: string,
  deviceID: string,
): void {
  if (!isBranchAvailable()) {
    return;
  }

  const baseData = {
    app_session_id: sessionID,
    device_id: deviceID,
  };

  // set metadata depending on journey type
  switch (journey.type) {
    case BranchJourneyType.MainDir:
      window.branch.setBranchViewData({
        data: {
          ...baseData,
        },
      });
      break;
    case BranchJourneyType.GameDir:
      window.branch.setBranchViewData({
        data: {
          ...baseData,
          game: journey.game,
        },
      });
      break;
    case BranchJourneyType.Live:
    case BranchJourneyType.Vod:
    case BranchJourneyType.Profile:
      window.branch.setBranchViewData({
        data: {
          ...baseData,
          channel: journey.channel,
        },
      });
      break;
    default:
      handleUnknown(journey);
  }

  // page view triggers showing journey
  trackBranchEvent(BranchEvent.PageView, {
    ...baseData,
    eligible_for: journey.type,
  });
}

export function closeBranchJourney(): void {
  if (isBranchAvailable()) {
    window.branch.closeJourney();
  }
}

export function addBranchListener(
  listener: Branch.BranchJourneyListener,
): void {
  if (isBranchAvailable()) {
    window.branch.addListener(listener);
  }
}

export function removeBranchListener(
  listener: Branch.BranchJourneyListener,
): void {
  if (isBranchAvailable()) {
    window.branch.removeListener(listener);
  }
}
