import * as React from "react";
import * as R from "ramda";
import axios from "axios";
import { renderHook } from "@testing-library/react-hooks";
import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
import * as zappRedux from "@applicaster/zapp-react-native-redux";
import * as helpers from "../../../helpers";
import * as feedLoader from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";
import * as useNavigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";

import { Provider } from "react-redux";
import configureStore from "redux-mock-store";

const rivers = {
  A1234: {
    id: "A1234",
  },
};

const navigator = {
  currentRoute: "/river/A1234",
  replace: jest.fn(),
};

const mockStore = configureStore();

jest
  .spyOn(useNavigationHooks, "useNavigation")
  .mockImplementation(() => navigator);

const helperSpy = jest.spyOn(helpers, "handlePresentNavigation");
const feedLoaderSpy = jest.spyOn(feedLoader, "useFeedLoader");

const { usePresentSchemeHandler } = require("../usePresentSchemeHandler");

const onFinish = jest.fn((cb) => {
  cb();
});

describe("usePresentSchemeHandler", () => {
  beforeEach(() => {
    onFinish.mockClear();
    helperSpy.mockClear();
    navigator.replace.mockClear();
  });

  const store = mockStore({ test: "test", rivers });

  const wrapper: React.FC<any> = ({ children }) => (
    <Provider store={store}>{children}</Provider>
  );

  describe("loading link url", () => {
    const query = { link_url: "http://applicaster.com" };

    const url = `scheme://present?link_url=${encodeURIComponent(
      "http://applicaster.com"
    )}`;

    it("opens the link entry", () => {
      renderHook(() => usePresentSchemeHandler({ query, url, onFinish }), {
        wrapper,
      });

      expect(onFinish).toBeCalled();

      expect(helperSpy).toBeCalledWith(
        expect.objectContaining({
          data: {
            id: "url_scheme_entry",
            type: { value: "link" },
            link: { href: query.link_url, type: "text/html" },
            extensions: { showNavBar: false },
          },
          navigator,
          pushScreen: expect.anything(),
        })
      );
    });

    it("uses the provided content type if it exists", () => {
      const queryWithContentType = {
        ...query,
        content_type: "link-type",
      };

      const urlWithContentType = `scheme://present?link_url=${encodeURIComponent(
        "http.applicaster.com"
      )}&content_type=link-type`;

      renderHook(
        () =>
          usePresentSchemeHandler({
            query: queryWithContentType,
            url: urlWithContentType,
            onFinish,
          }),
        {
          wrapper,
        }
      );

      expect(helperSpy).toBeCalledWith(
        expect.objectContaining({
          data: {
            id: "url_scheme_entry",
            type: { value: queryWithContentType.content_type },
            link: { href: queryWithContentType.link_url, type: "text/html" },
            extensions: { showNavBar: false },
          },
        })
      );
    });

    it("returns showNavBar truthy value when no show_nav_bar is set to 'true'", () => {
      const query = {
        link_url: "http://applicaster.com",
        show_nav_bar: "true",
      };

      const url = `scheme://present?link_url=${encodeURIComponent(
        "http://applicaster.com"
      )}&show_nav_bar=true`;

      renderHook(() => usePresentSchemeHandler({ query, url, onFinish }), {
        wrapper,
      });

      expect(onFinish).toBeCalled();

      expect(helperSpy).toBeCalledWith(
        expect.objectContaining({
          data: {
            id: "url_scheme_entry",
            type: { value: "link" },
            link: { href: query.link_url, type: "text/html" },
            extensions: { showNavBar: true },
          },
          navigator,
          pushScreen: expect.anything(),
        })
      );
    });

    it("adds the screen id when present in the query", () => {
      const screen_id = "A1234";

      const query = {
        link_url: "http://applicaster.com",
        screen_id,
      };

      const url = `scheme://present?link_url=${encodeURIComponent(
        "http://applicaster.com"
      )}&screen_id=${screen_id}`;

      renderHook(() => usePresentSchemeHandler({ query, url, onFinish }), {
        wrapper,
      });

      expect(onFinish).toBeCalled();

      expect(helperSpy).toBeCalledWith(
        expect.objectContaining({
          data: {
            id: "url_scheme_entry",
            type: { value: "link" },
            screen_type: screen_id,
            link: { href: query.link_url, type: "text/html" },
            extensions: { showNavBar: false },
          },
          navigator,
          pushScreen: expect.anything(),
        })
      );
    });
  });

  describe("loading new river", () => {
    it("Re-sets app ready state and calls loadAppContextData with new river and cellStyles data", async () => {
      const store = mockStore({ test: "test" });

      const wrapper: React.FC<any> = ({ children }) => (
        <Provider store={store}>{children}</Provider>
      );

      const query = {
        rivers_configuration_id: "test",
      };

      const storageSpy = jest.spyOn(sessionStorage, "getAllItems");
      const axiosGetSpy = jest.spyOn(axios, "get");
      const zappReduxSpy = jest.spyOn(zappRedux, "loadAppContextData");

      const getAllItems = jest.fn().mockResolvedValue({
        accountsAccountId: "accountsAccountId",
        bundleIdentifier: "bundleIdentifier",
        app_family_id: "app_family_id",
        version_name: "version_name",
        store: "store",
      });

      const get = jest.fn().mockResolvedValue({ data: "url" });

      storageSpy.mockImplementation(getAllItems);
      axiosGetSpy.mockImplementation(get);
      zappReduxSpy.mockReturnValue(null);

      renderHook(() => usePresentSchemeHandler({ query, url: "", onFinish }), {
        wrapper,
      });

      return () => {
        const actions = store.getActions();

        expect(
          actions?.find(R.propEq("type", "SET_APP_NOT_READY"))
        ).toBeDefined();

        expect(actions?.find(R.propEq("type", "SET_APP_READY"))).toBeDefined();

        expect(zappReduxSpy).toBeCalledWith(expect.any(Function), {
          cellStyles: "url",
          rivers: "url",
        });

        expect(navigator.replace).toBeCalledWith({});
      };
    });
  });

  describe("loading feed", () => {
    const query = { data_source: "http://applicaster.com/datasource" };
    const mockEntry = { id: 10, type: { value: "foo" } };
    const mockFeed = { id: 1, type: { value: "feed" }, entry: [mockEntry] };

    const useFeedLoader = ({ feedUrl }) => ({
      data: mockFeed,
      url: feedUrl,
      loading: true,
      loadNext: jest.fn(),
      reloadData: jest.fn(),
    });

    feedLoaderSpy.mockImplementation(useFeedLoader);

    beforeEach(() => {
      feedLoaderSpy.mockClear();
      helperSpy.mockClear();
    });

    it("calls feedLoader  with the provided datasource", (done) => {
      renderHook(
        () =>
          usePresentSchemeHandler({
            query,
            url: "",
            onFinish: (cb) => {
              cb();
              done();
            },
          }),
        {
          wrapper,
        }
      );

      expect(feedLoaderSpy).toBeCalledWith({ feedUrl: query.data_source });

      expect(helperSpy).toBeCalledWith({
        data: {
          content: {
            src: "http://applicaster.com/datasource",
          },
          screen_type: undefined,
        },
        navigator,
        pushScreen: undefined,
      });
    });

    it("attached the screen_type to the feed, if screen_id provided", (done) => {
      const screen_id = "test_id";

      renderHook(
        () =>
          usePresentSchemeHandler({
            query: { ...query, screen_id },
            url: "",
            onFinish: (cb) => {
              cb();
              done();
            },
          }),
        {
          wrapper,
        }
      );

      expect(helperSpy).toBeCalledWith(
        expect.objectContaining({
          data: {
            content: {
              src: "http://applicaster.com/datasource",
            },
            screen_type: screen_id,
          },
          navigator,
          pushScreen: undefined,
        })
      );
    });

    it("returns entry from fetched feed if entry_id is provided", (done) => {
      const entry_id = mockEntry.id;

      renderHook(
        () =>
          usePresentSchemeHandler({
            query: { ...query, entry_id },
            url: "",
            onFinish: (cb) => {
              cb();
              done();
            },
          }),
        {
          wrapper,
        }
      );

      expect(helperSpy).toBeCalledWith(
        expect.objectContaining({
          data: {
            id: 10,
            type: { value: "foo" },
          },
          navigator,
          pushScreen: undefined,
        })
      );
    });

    it("attaches resumeTime to entry, if provided", (done) => {
      const resumeTime = 100;

      const entry_id = mockEntry.id;

      renderHook(
        () =>
          usePresentSchemeHandler({
            query: { ...query, entry_id, resumeTime },
            url: "",
            onFinish: (cb) => {
              cb();
              done();
            },
          }),
        {
          wrapper,
        }
      );

      expect(helperSpy).toBeCalledWith(
        expect.objectContaining({
          data: {
            id: 10,
            type: { value: "foo" },
            extensions: {
              resumeTime,
            },
          },
          navigator,
          pushScreen: undefined,
        })
      );
    });
  });
});
