import { Dispatch } from 'redux';

import { SPADE_URI_FALLBACK } from 'mweb/common/config/spadeRotator';
import { fetchSpadeURI } from 'mweb/common/fetch/spadeURI';
import { Browser, OS } from 'mweb/common/reducers/device';
import { RootState } from 'mweb/common/reducers/root';
import { convertUserAgentToBrowserAndOS } from 'mweb/common/utils/convertUserAgentToBrowserAndOS';

interface ServerPlatformOpts {
  readonly gitHash: string;
}
interface ClientPlatformOpts {
  readonly userAgent: string;
}

interface SwitchedToServerPayload {
  readonly gitHash: string;
  readonly spadeURI: string;
}

export const PLATFORM_SWITCHED_TO_SERVER_ACTION_TYPE =
  'PLATFORM_SWITCHED_TO_SERVER_ACTION_TYPE';
export interface PlatformSwitchedToServerAction {
  readonly type: typeof PLATFORM_SWITCHED_TO_SERVER_ACTION_TYPE;
  readonly payload: SwitchedToServerPayload;
}

export const PLATFORM_SWITCHED_TO_CLIENT_ACTION_TYPE =
  'PLATFORM_SWITCHED_TO_CLIENT_ACTION_TYPE';

export interface NetInfo {
  readonly bandwidth: number | undefined;
  readonly bandwidth_max: number | undefined;
  readonly round_trip_time: number | undefined;
  readonly mobile_connection_type: ConnectionType | undefined;
  readonly effective_mobile_connection_type:
    | EffectiveConnectionType
    | undefined;
}

export interface PlatformSwitchedToClientAction {
  readonly type: typeof PLATFORM_SWITCHED_TO_CLIENT_ACTION_TYPE;
  readonly payload: {
    readonly os: OS;
    readonly browser: Browser;
    readonly netInfo: NetInfo;
  };
}

export type PlatformAction =
  | PlatformSwitchedToServerAction
  | PlatformSwitchedToClientAction;

// cheap "in-memory" cache
const SPADE_URI_CACHE_TTL = 6 * 60 * 60 * 1000; // 6 hours
let todaysSpadeData = {
  uri: '',
  lastUpdate: 0,
};

export function platfromSwitchToServer(
  opts: ServerPlatformOpts,
): (dispatch: Dispatch<RootState>) => Promise<void> {
  return async dispatch => {
    const now = new Date().valueOf();
    const isCacheValid =
      todaysSpadeData.uri &&
      now - SPADE_URI_CACHE_TTL < todaysSpadeData.lastUpdate;
    let spadeURI;
    if (isCacheValid) {
      spadeURI = todaysSpadeData.uri;
    } else {
      try {
        spadeURI = await fetchSpadeURI();
        todaysSpadeData = {
          uri: spadeURI,
          lastUpdate: now,
        };
      } catch {
        spadeURI = SPADE_URI_FALLBACK;
      }
    }
    dispatch(
      platformSwitchedToServerBuilder({
        gitHash: opts.gitHash,
        spadeURI,
      }),
    );
  };
}

function platformSwitchedToServerBuilder(
  payload: SwitchedToServerPayload,
): PlatformSwitchedToServerAction {
  return {
    type: PLATFORM_SWITCHED_TO_SERVER_ACTION_TYPE,
    payload,
  };
}

export function getNetInfo(): NetInfo {
  if (navigator === undefined) {
    return {} as NetInfo;
  }
  const connection =
    navigator.connection ||
    navigator.mozConnection ||
    navigator.webkitConnection ||
    ({} as NetworkInformation);
  return {
    bandwidth: connection.downlink,
    bandwidth_max: connection.downlinkMax,
    round_trip_time: connection.rtt,
    mobile_connection_type: connection.type,
    effective_mobile_connection_type: connection.effectiveType,
  };
}

export function platformSwitchToClient(
  opts: ClientPlatformOpts,
): PlatformSwitchedToClientAction {
  const { os, browser } = convertUserAgentToBrowserAndOS(opts.userAgent);
  return {
    type: PLATFORM_SWITCHED_TO_CLIENT_ACTION_TYPE,
    payload: {
      os,
      browser,
      netInfo: getNetInfo(),
    },
  };
}
