import axios from "axios";
import { renderHook, waitFor } from "@testing-library/react-native";
import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
import * as helpers from "../../../helpers";
import * as feedLoader from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";
import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
import { usePresentSchemeHandler } from "../usePresentSchemeHandler";

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

const mock_navigator = {
  currentRoute: "/river/A1234",
  replace: jest.fn(),
  goHome: jest.fn(),
} as any;

jest.mock(
  "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation",
  () => ({
    useNavigation: jest.fn(() => mock_navigator),
  })
);

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

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

const navigator = mock_navigator;
jest.useFakeTimers();

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

  const store = { test: "test", rivers };

  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: WrappedWithProviders,
        initialProps: { store },
      });

      expect(onFinish).toHaveBeenCalled();

      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: WrappedWithProviders,
          initialProps: { store },
        }
      );

      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: WrappedWithProviders,
        initialProps: { store },
      });

      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: WrappedWithProviders,
        initialProps: { store },
      });

      expect(onFinish).toBeCalled();

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

  describe("loading new river", () => {
    const query = { rivers_configuration_id: "layout-uuid" };

    const baseSessionData = {
      accountsAccountId: "account-123",
      bundleIdentifier: "com.example.app",
      app_family_id: "42",
      version_name: "1.0.0",
    };

    let storageSpy: jest.SpyInstance;
    let axiosGetSpy: jest.SpyInstance;

    beforeEach(() => {
      storageSpy = jest.spyOn(sessionStorage, "getAllItems");
      axiosGetSpy = jest.spyOn(axios, "get");
      axiosGetSpy.mockResolvedValue({ data: {} });
    });

    afterEach(() => {
      storageSpy.mockRestore();
      axiosGetSpy.mockRestore();
    });

    it("fetches the rivers, cell styles and presets mapping URLs", async () => {
      storageSpy.mockResolvedValue({ ...baseSessionData, store: "store" });

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

      await waitFor(() => {
        expect(axiosGetSpy).toHaveBeenCalledWith(
          expect.stringContaining("/layouts/layout-uuid.json")
        );
      });
    });

    describe("WEB_STORE_PLATFORM CDN store key mapping", () => {
      it.each([
        ["samsung", "samsung_app_store"],
        ["lg", "lg_content_store"],
        ["vizio", "vizio_app_store"],
      ])(
        'maps store="%s" to "%s" in the rivers URL',
        async (store, expectedCdnKey) => {
          storageSpy.mockResolvedValue({ ...baseSessionData, store });

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

          await waitFor(() => {
            expect(axiosGetSpy).toHaveBeenCalledWith(
              expect.stringContaining(`/${expectedCdnKey}/`)
            );
          });

          // Confirm the raw store name is NOT used in the URL
          expect(axiosGetSpy).not.toHaveBeenCalledWith(
            expect.stringContaining(`/${store}/`)
          );
        }
      );

      it("passes through an unmapped store value unchanged", async () => {
        storageSpy.mockResolvedValue({
          ...baseSessionData,
          store: "apple_tv",
        });

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

        await waitFor(() => {
          expect(axiosGetSpy).toHaveBeenCalledWith(
            expect.stringContaining("/apple_tv/")
          );
        });
      });
    });
  });

  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: WrappedWithProviders,
        }
      );

      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: WrappedWithProviders,
        }
      );

      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: WrappedWithProviders,
        }
      );

      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: WrappedWithProviders,
        }
      );

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