import * as React from "react";
import * as R from "ramda";
import { renderHook, act } from "@testing-library/react-hooks";
import { NetworkStatusContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NetworkStatusContext";
import { NetworkStatusProvider } from "../NetworkStatusProvider";
import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
import NetInfo from "@react-native-community/netinfo";

const NetInfoMock = {
  type: "cellular",
  details: {
    cellularGeneration: "3g",
    isConnectionExpensive: true,
  },
  deviceStatus: null,
  isConnected: true,
  isInternetReachable: true,
};

const getWrapper = (store) => ({
  // eslint-disable-next-line react/prop-types,react/display-name
  wrapper: ({ children }) => (
    <WrappedWithProviders store={store}>
      <NetworkStatusProvider>{children}</NetworkStatusProvider>
    </WrappedWithProviders>
  ),
});

let setNetInfoState;

const useNetInfoMock = () => {
  const [state, setState] = React.useState(NetInfoMock);
  setNetInfoState = setState;

  return state;
};

describe("NetworkStatusProvider", function () {
  it("should return netInfoData", function () {
    const store = {
      plugins: [{}],
    };

    // @ts-ignore
    jest.spyOn(NetInfo, "useNetInfo").mockImplementation(useNetInfoMock);

    const { result } = renderHook(
      () => React.useContext(NetworkStatusContext),
      getWrapper(store)
    );

    expect(result.current).toStrictEqual(NetInfoMock);
  });

  it("should call onConnectionLost callback when connection type changes to none", function () {
    const onConnectionLost = jest.fn();

    const store = {
      plugins: [{ module: { networkHooks: { onConnectionLost } } }],
    };

    // @ts-ignore
    jest.spyOn(NetInfo, "useNetInfo").mockImplementation(useNetInfoMock);

    renderHook(() => React.useContext(NetworkStatusContext), getWrapper(store));

    expect(onConnectionLost).toBeCalledTimes(0);

    act(() => {
      setNetInfoState(
        R.mergeRight(NetInfoMock, {
          type: "none",
          isConnected: false,
          isInternetReachable: false,
        })
      );
    });

    expect(onConnectionLost).toBeCalledTimes(1);
  });

  it("should call onConnectionRestored callback when connection type switches from none", function () {
    const onConnectionRestored = jest.fn();

    const store = {
      plugins: [{ module: { networkHooks: { onConnectionRestored } } }],
    };

    // @ts-ignore
    jest.spyOn(NetInfo, "useNetInfo").mockImplementation(useNetInfoMock);

    renderHook(() => React.useContext(NetworkStatusContext), getWrapper(store));

    act(() => {
      setNetInfoState(
        R.mergeRight(NetInfoMock, {
          type: "none",
          isConnected: false,
          isInternetReachable: false,
        })
      );
    });

    expect(onConnectionRestored).toBeCalledTimes(0);

    act(() => {
      setNetInfoState(
        R.mergeRight(NetInfoMock, {
          type: "cellular",
          isConnected: true,
          isInternetReachable: true,
        })
      );
    });

    expect(onConnectionRestored).toBeCalledTimes(1);
  });

  it("should call onConnectionTypeChanged callback when connection type changes", function () {
    const onConnectionTypeChanged = jest.fn();

    const store = {
      plugins: [{ module: { networkHooks: { onConnectionTypeChanged } } }],
    };

    // @ts-ignore
    jest.spyOn(NetInfo, "useNetInfo").mockImplementation(useNetInfoMock);

    renderHook(() => React.useContext(NetworkStatusContext), getWrapper(store));

    act(() => {
      setNetInfoState(R.set(R.lensProp("type"), "wifi")(NetInfoMock));
    });

    expect(onConnectionTypeChanged).toBeCalledWith("wifi");
  });
});
