import React from "react";

import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";

import {
  getInflatedDataSourceUrl,
  getSearchContext,
  useGetUrlInflater,
} from "../useInflatedUrl";

import { reactHooksLogger } from "../../logger";

jest.mock("../../navigation", () => ({
  useRoute: () => ({ pathname: "/mock/path" }),
}));

// mock contexts hooks
jest.mock("@applicaster/zapp-react-native-ui-components/Contexts", () => ({
  ZappPipesEntryContext: {
    useZappPipesContext: () => [
      { id: "entry-1", extensions: { showId: "A123" } },
    ],
  },
  ZappPipesSearchContext: {
    useZappPipesContext: () => ["user%20query"],
  },
  ZappPipesScreenContext: {
    useZappPipesContext: () => [{ id: "screen-1" }],
  },
}));

jest.mock("../../logger", () => ({
  reactHooksLogger: {
    warning: jest.fn(),
    addSubsystem: jest.fn(),
  },
}));

let mockStore;

describe("getInflatedDataSourceUrl", () => {
  beforeEach(() => {
    reactHooksLogger.warning.mockClear();

    const {
      appStore,
    } = require("@applicaster/zapp-react-native-redux/AppStore");

    mockStore = {
      getState: jest.fn().mockReturnValue({ pipesEndpoints: {} }),
    };

    appStore.set(mockStore);
  });

  it("returns the url inflated with the right params", () => {
    // setup
    const source = "http://foo.com/show/{{showId}}";

    const contexts = {
      entry: { id: "show-1" },
    };

    const mapping = {
      showId: {
        property: "id",
        source: "entry",
      },
    };

    // run
    const result = getInflatedDataSourceUrl({
      source,
      contexts,
      mapping,
    });

    // verify
    expect(result).toBe("http://foo.com/show/show-1");

    expect(reactHooksLogger.warning).not.toHaveBeenCalled();
  });

  it("returns null if required entry is undefined", () => {
    // setup
    const source = "http://foo.com/show/seriesId={{seriesId}}";

    const contexts = {
      entry: undefined,
    };

    const mapping = {
      seriesId: {
        property: "id",
        source: "entry",
      },
    };

    // run
    const result = getInflatedDataSourceUrl({
      source,
      contexts,
      mapping,
    });

    // verify
    expect(result).toBeNull();
    expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);

    expect(reactHooksLogger.warning).toHaveBeenCalledWith({
      message:
        "Not enough data to fully inflate url: {{entry.id}} source: http://foo.com/show/seriesId={{seriesId}}",
      data: { source, contexts, mapping },
    });
  });

  it("returns null if dont have enough data", () => {
    // setup
    const source =
      "https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName}}";

    const contexts = {
      entry: {
        extensions: {},
      },
    };

    const mapping = {
      groupName: {
        property: "extensions.groupName",
        source: "entry",
      },
    };

    // run
    const result = getInflatedDataSourceUrl({
      source,
      contexts,
      mapping,
    });

    // verify
    expect(result).toBeNull();
    expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);

    expect(reactHooksLogger.warning).toHaveBeenCalledWith({
      message:
        "Not enough data to fully inflate url: {{entry.extensions.groupName}} source: https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName}}",
      data: { source, contexts, mapping },
    });
  });

  it("returns null if dont have enough data", () => {
    // setup
    const source =
      "https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName1}}?test={{groupName2}}";

    const contexts = {
      entry: {
        extensions: {
          groupName1: "groupName_1",
        },
      },
    };

    const mapping = {
      groupName1: {
        property: "extensions.groupName1",
        source: "entry",
      },
      groupName2: {
        property: "extensions.groupName2",
        source: "entry",
      },
    };

    // run
    const result = getInflatedDataSourceUrl({
      source,
      contexts,
      mapping,
    });

    // verify
    expect(result).toBeNull();
    expect(reactHooksLogger.warning).toHaveBeenCalledTimes(1);

    expect(reactHooksLogger.warning).toHaveBeenCalledWith({
      message:
        "Not enough data to fully inflate url: {{entry.extensions.groupName2}} source: https://zapp-1689-emmys.web.app/emmys/group-categories/{{groupName1}}?test={{groupName2}}",
      data: { source, contexts, mapping },
    });
  });
});

describe("getSearchContext", () => {
  it("returns an object with the search context and the correct prop name", () => {
    // setup
    const searchContext = "query%20typed%20by%20the%20user";

    const mapping = {
      queryParam: {
        property: "q",
        source: "search",
      },
    };

    // run
    const result = getSearchContext(searchContext, mapping);

    // verify
    expect(result).toHaveProperty(mapping.queryParam.property, searchContext);
  });
});

describe("useGetUrlInflater", () => {
  const { renderHook } = require("@testing-library/react-hooks");

  it("returns original url when mapping is not provided", () => {
    const { result } = renderHook(() => useGetUrlInflater(), {
      wrapper: WrappedWithProviders,
    });

    const src = "https://foo.com/feed";
    expect(result.current(src)).toBe(src);
  });

  it("inflates url using entry/search/screen contexts when mapping provided", () => {
    const { result } = renderHook(() => useGetUrlInflater(), {
      wrapper: ({ children }) => (
        <WrappedWithProviders>{children}</WrappedWithProviders>
      ),
    });

    const source = "https://foo.com/shows/{{showId}}?q={{q}}";

    const mapping = {
      showId: { source: "entry", property: "extensions.showId" },
      q: { source: "search", property: "q" },
    };

    const url = result.current(source, mapping);
    expect(url).toBe("https://foo.com/shows/A123?q=user%20query");
  });
});
