import * as React from "react";
import { Platform } from "react-native";
import NetInfo, { NetInfoState } from "@react-native-community/netinfo";

import { NetworkStatusContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NetworkStatusContext";
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
import {
  callAllWith,
  getNetworkHooks,
  isOnline,
  subscribeToDeviceNetworkChanges,
  unsubscribeToDeviceNetworkChanges,
} from "./utils";
import { usePrevious } from "@applicaster/zapp-react-native-utils/reactHooks/utils";

// Use default netInfo config for all platforms (important for web platform which otherwise
// will use backend root which is not responding for HEAD request)
if (["web", "lg_tv", "samsung_tv"].includes(Platform.OS)) {
  // @ts-ignore
  NetInfo.configure({
    reachabilityUrl:
      "https://assets-production.applicaster.com/zapp/network_check/health.json",
    reachabilityTest: async (response) => !!response.status,
  });
}

export const NetworkStatusProvider = ({
  children,
}: {
  children: React.ReactNode;
}) => {
  const { plugins } = usePickFromState("plugins");
  const [deviceStatus, setDeviceStatus] = React.useState(null);
  const currentNetInfo = NetInfo.useNetInfo();

  // NetInfo library does not have accurate information for Tizen SmartTVs, and possibly WebOS
  // This combined info helps us gather data from the native connection status APIs
  // and includes it in our definition of... online.
  const combinedNetInfo: NetInfoState & { deviceStatus?: NetStatus } = {
    ...currentNetInfo,
    deviceStatus,
  };

  const { onConnectionTypeChanged, onConnectionLost, onConnectionRestored } =
    React.useMemo<NetworkHooks>(() => getNetworkHooks(plugins), []);

  const online = isOnline(combinedNetInfo);

  const previousOnline = usePrevious(online);
  const previousConnectionType = usePrevious(combinedNetInfo.type);

  const networkChangeHandler: NetCb = (status) => {
    setDeviceStatus(status);
  };

  React.useEffect(() => {
    if (!combinedNetInfo.details) return;

    if (previousOnline !== undefined && previousOnline !== online) {
      online
        ? onConnectionRestored &&
          callAllWith(onConnectionRestored, combinedNetInfo)
        : onConnectionLost && callAllWith(onConnectionLost, combinedNetInfo);
    }
  }, [online]);

  React.useEffect(() => {
    if (!combinedNetInfo.details) return;

    if (
      previousConnectionType !== combinedNetInfo.type &&
      onConnectionTypeChanged
    ) {
      callAllWith(onConnectionTypeChanged, combinedNetInfo.type);
    }
  }, [combinedNetInfo.type]);

  // Subscribe to device specific network changes
  React.useEffect(() => {
    // Instead of using the NetInfo lib use the native device status
    subscribeToDeviceNetworkChanges(networkChangeHandler);

    return () => unsubscribeToDeviceNetworkChanges();
  }, []);

  return (
    <NetworkStatusContext.Provider value={combinedNetInfo}>
      {children}
    </NetworkStatusContext.Provider>
  );
};
