import React from "react";

import { cleanup, renderHook } from "@testing-library/react-hooks";
import { Provider } from "react-redux";
import configureStore from "redux-mock-store";

import * as ReactNative from "react-native";

const mocked_return_value = (tv) => ({
  screen: tv
    ? { width: 50, height: 50, scale: 2, fontScale: 1 }
    : { width: 100, height: 100, scale: 1, fontScale: 1 },
  window: { width: 100, height: 100, scale: 1, fontScale: 1 },
});

const platform = "android";
let mock_tv_flag = false;

ReactNative.Dimensions.get = jest.fn(
  (context) => mocked_return_value(mock_tv_flag)[context]
);

const mockRemoveFn = jest.fn();

ReactNative.Dimensions.addEventListener = jest.fn().mockReturnValue({
  remove: mockRemoveFn,
});

ReactNative.Platform.OS = platform;

const QuickBrickCommunicationModule = {
  initialProps: {
    is_tablet: false,
  },
};

ReactNative.NativeModules.QuickBrickCommunicationModule =
  QuickBrickCommunicationModule;

jest.mock("react-native-safe-area-context", () => ({
  useSafeAreaInsets: () => ({ top: 50 }),
}));

jest.mock("../../../reactUtils", () => ({
  ...jest.requireActual("../../../reactUtils"),
  platformSelect: jest.fn((specs) => specs[platform] || specs.default),
  isTV: jest.fn(() => mock_tv_flag),
}));

jest.mock("../../navigation", () => ({
  useIsScreenActive: () => true,
}));

jest.mock("../../navigation/useNavigation");

const { Dimensions } = require("react-native");
const { useDimensions } = require("..");

const mockStore = configureStore();

const getWrapper = () => {
  const store = mockStore({ appData: {} });

  const Wrapper = ({ children }: { children: React.ReactChild }) => (
    /* @ts-ignore */
    <Provider store={store}>{children}</Provider>
  );

  return Wrapper;
};

const testHook = (context, params?) => {
  const wrapper = getWrapper();

  const {
    result,
    result: { current: dimensions },
  } = renderHook(() => useDimensions(context, params), {
    wrapper,
  });

  return { result, dimensions };
};

describe("useDimensions", () => {
  it("renders", () => {
    const { result } = testHook("window", { fullDimensions: true });

    expect(result).toBeDefined();
  });

  it("sets event listener", () => {
    testHook("window", { fullDimensions: true });

    expect(Dimensions.addEventListener).toBeCalledWith(
      "change",
      expect.any(Function)
    );
  });

  it("removes event listener", () => {
    testHook("window", { fullDimensions: true });
    cleanup();

    expect(mockRemoveFn).toBeCalled();
  });

  it("Returns full dimensions object", () => {
    const { dimensions } = testHook("window", { fullDimensions: true });

    expect(dimensions).toHaveProperty("height");
    expect(dimensions).toHaveProperty("width");
    expect(dimensions).toHaveProperty("scale");
    expect(dimensions).toHaveProperty("fontScale");
  });

  it("Returns shorthand dimensions object", () => {
    const { dimensions } = testHook("window");

    expect(dimensions).toHaveProperty("height");
    expect(dimensions).toHaveProperty("width");
  });

  it("Returns the device info object", () => {
    const { dimensions } = testHook("window", {
      fullDimensions: true,
      deviceInfo: true,
    });

    expect(dimensions).toHaveProperty("height");
    expect(dimensions).toHaveProperty("width");
    expect(dimensions).toHaveProperty("scale");
    expect(dimensions).toHaveProperty("fontScale");
    expect(dimensions).toHaveProperty("deviceInfo");
  });

  describe("it applies the scale to the width & height if needed", () => {
    it("applies the scale on TV, and screen context", () => {
      mock_tv_flag = true;
      const { screen } = mocked_return_value(mock_tv_flag);
      const { dimensions } = testHook("screen");

      expect(dimensions).toHaveProperty("height", screen.height * screen.scale);
      expect(dimensions).toHaveProperty("width", screen.width * screen.scale);
      expect(dimensions).toHaveProperty("scale", 1);

      mock_tv_flag = false;
    });

    it("doesn't apply the scale on mobile with screen context", () => {
      mock_tv_flag = false;
      const { screen } = mocked_return_value(mock_tv_flag);
      const { dimensions } = testHook("screen");

      expect(dimensions).toHaveProperty("height", screen.height);
      expect(dimensions).toHaveProperty("width", screen.width);
      expect(dimensions).toHaveProperty("scale", screen.scale);
    });

    it("doesn't apply the scale on TV with window context", () => {
      mock_tv_flag = true;
      const { window } = mocked_return_value(mock_tv_flag);
      const { dimensions } = testHook("window");

      expect(dimensions).toHaveProperty("height", window.height);
      expect(dimensions).toHaveProperty("width", window.width);
      expect(dimensions).toHaveProperty("scale", window.scale);
    });
  });
});
