import React from "react";
import * as useFeedLoaderModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";
import * as useFeedRefreshModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedRefresh";
import { favoritesListener } from "@applicaster/zapp-react-native-bridge/Favorites";
import { UrlFeedResolver } from "../resolvers/UrlFeedResolver";
import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
import * as ZappPipesModule from "@applicaster/zapp-pipes-v2-client";

const mockSubscribeForKeyChanges = jest.fn();

jest
  .spyOn(ZappPipesModule, "subscribeForKeyChanges")
  .mockImplementation(mockSubscribeForKeyChanges);

jest.mock("@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader");

jest.mock(
  "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedRefresh"
);

jest.mock("@applicaster/zapp-pipes-v2-client");

jest.mock("@applicaster/zapp-react-native-bridge/Favorites", () => ({
  favoritesListener: {
    on: jest.fn(),
    removeHandler: jest.fn(),
  },
}));

jest.mock("../utils", () => ({
  isVerticalListOrGrid: jest.fn(
    (component) =>
      component?.type === "vertical_list" || component?.type === "vertical_grid"
  ),
}));

const componentRequiredKeys = { rules: { item_limit: 10 } };

describe("UrlFeedResolver", () => {
  const mockChildren = jest.fn(() => <div data-testid="mock-children" />);
  const mockReloadData = jest.fn();
  const mockLoadNext = jest.fn();

  const mockAddDataSourceListener = jest.fn().mockReturnValue(jest.fn());

  beforeEach(() => {
    jest.clearAllMocks();

    (useFeedLoaderModule.useFeedLoader as jest.Mock).mockReturnValue({
      reloadData: mockReloadData,
      loadNext: mockLoadNext,
      error: null,
      loading: false,
      url: "test-url",
      data: { entry: [{ id: "1" }] },
    });
  });

  it("should use feedUrl directly when provided", () => {
    const props = {
      feedUrl: "https://example.com/feed",
      component: { ...componentRequiredKeys } as any,
      children: mockChildren,
      riverId: "test-river",
    };

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
      feedUrl: "https://example.com/feed",
      pipesOptions: expect.any(Object),
    });
  });

  it("should use component.data.source when no feedUrl is provided", () => {
    const props = {
      component: {
        data: {
          source: "https://example.com/source",
        },
        ...componentRequiredKeys,
      } as any,
      children: mockChildren,
      riverId: "test-river",
    };

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
      feedUrl: "https://example.com/source",
      pipesOptions: expect.any(Object),
    });
  });

  it("should handle FAVOURITES type correctly", () => {
    const props = {
      component: {
        data: {
          source: "favorites-source",
          type: "FAVOURITES",
        },
        ...componentRequiredKeys,
      } as any,
      children: mockChildren,
      riverId: "test-river",
    };

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
      feedUrl: "favorites-source",
      pipesOptions: expect.objectContaining({
        clearCache: true,
        loadLocalFavorites: true,
      }),
    });

    expect(favoritesListener.on).toHaveBeenCalledWith(
      "FAVORITES_CHANGED",
      mockReloadData
    );
  });

  it("should handle APPLICASTER_COLLECTION type correctly", () => {
    const props = {
      component: {
        data: {
          source: "collection-id",
          type: "APPLICASTER_COLLECTION",
        },
        ...componentRequiredKeys,
      } as any,
      children: mockChildren,
      riverId: "test-river",
    };

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
      feedUrl:
        "applicaster://fetchData?type=APPLICASTER_COLLECTION&collectionId=collection-id",
      pipesOptions: expect.any(Object),
    });
  });

  it("should handle APPLICASTER_CATEGORY type correctly", () => {
    const props = {
      component: {
        data: {
          source: "category-id",
          type: "APPLICASTER_CATEGORY",
        },
        ...componentRequiredKeys,
      } as any,
      children: mockChildren,
      riverId: "test-river",
      plugins: [],
    };

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
      feedUrl: expect.stringContaining(
        "applicaster://fetchData?type=APPLICASTER_CATEGORY&categoryId=category-id"
      ),
      pipesOptions: expect.any(Object),
    });
  });

  it("should set up URL context key changes listener", () => {
    const props = {
      feedUrl: "https://example.com/feed",
      component: { ...componentRequiredKeys } as any,
      children: mockChildren,
      riverId: "test-river",
    };

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(mockSubscribeForKeyChanges.mock.calls[0][0]).toMatchObject({
      url: "https://example.com/feed",
      callback: mockReloadData,
    });
  });

  it("should set up pipesv2 URL listener from plugin if available", () => {
    const props = {
      feedUrl: "pipesv2://test-plugin/endpoint",
      component: { ...componentRequiredKeys } as any,
      children: mockChildren,
      riverId: "test-river",
      plugins: [
        {
          identifier: "test-plugin",
          module: {
            addDataSourceListener: mockAddDataSourceListener,
          },
        },
      ] as any,
    };

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(mockAddDataSourceListener).toHaveBeenCalledWith(mockReloadData);
    expect(mockSubscribeForKeyChanges).not.toHaveBeenCalled();
  });

  it("should apply item limit from component rules", () => {
    const props = {
      feedUrl: "https://example.com/feed",
      component: {
        rules: {
          item_limit: "2",
        },
      } as any,
      children: mockChildren,
      riverId: "test-river",
    };

    (useFeedLoaderModule.useFeedLoader as jest.Mock).mockReturnValue({
      reloadData: mockReloadData,
      loadNext: mockLoadNext,
      error: null,
      loading: false,
      url: "test-url",
      data: { entry: [{ id: "1" }, { id: "2" }, { id: "3" }] },
    });

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(mockChildren).toHaveBeenCalledWith(
      expect.objectContaining({
        zappPipesData: expect.objectContaining({
          data: expect.objectContaining({
            entry: [{ id: "1" }, { id: "2" }],
          }),
        }),
      })
    );
  });

  it("should conditionally include loadNextData based on isLast and component type", () => {
    // Case 1: isLast=true, should not include loadNextData regardless of component type
    const props1 = {
      feedUrl: "https://example.com/feed",
      component: {
        type: "vertical_list",
        ...componentRequiredKeys,
      } as any,
      children: mockChildren,
      riverId: "test-river",
      isLast: true,
    };

    renderWithProviders(<UrlFeedResolver {...props1} />);

    expect(mockChildren).toHaveBeenCalledWith(
      expect.objectContaining({
        loadNextData: mockLoadNext,
      })
    );

    mockChildren.mockClear();

    // Case 2: isLast=false, vertical_list component, should not include loadNextData
    const props2 = {
      feedUrl: "https://example.com/feed",
      component: {
        type: "vertical_list",
        ...componentRequiredKeys,
      } as any,
      children: mockChildren,
      riverId: "test-river",
      isLast: false,
    };

    renderWithProviders(<UrlFeedResolver {...props2} />);

    expect(mockChildren).toHaveBeenCalledWith(
      expect.objectContaining({
        loadNextData: undefined,
      })
    );

    mockChildren.mockClear();

    // Case 3: isLast=false, non-vertical component, should include loadNextData
    const props3 = {
      feedUrl: "https://example.com/feed",
      component: {
        type: "horizontal_list",
        ...componentRequiredKeys,
      } as any,
      children: mockChildren,
      riverId: "test-river",
      isLast: false,
    };

    renderWithProviders(<UrlFeedResolver {...props3} />);

    expect(mockChildren).toHaveBeenCalledWith(
      expect.objectContaining({
        loadNextData: mockLoadNext,
      })
    );
  });

  it("should pass correct zappPipesDataProps to children", () => {
    const props = {
      feedUrl: "https://example.com/feed",
      component: { ...componentRequiredKeys } as any,
      children: mockChildren,
      riverId: "test-river",
    };

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(mockChildren).toHaveBeenCalledWith({
      zappPipesData: {
        url: "test-url",
        loading: false,
        data: { entry: [{ id: "1" }] },
        error: null,
      },
      reloadData: mockReloadData,
      loadNextData: mockLoadNext,
    });
  });

  it("should apply feed refresh hook", () => {
    const props = {
      feedUrl: "https://example.com/feed",
      component: { ...componentRequiredKeys } as any,
      children: mockChildren,
      riverId: "test-river",
    };

    renderWithProviders(<UrlFeedResolver {...props} />);

    expect(useFeedRefreshModule.useFeedRefresh).toHaveBeenCalledWith({
      reloadData: mockReloadData,
      component: props.component,
    });
  });

  it("should clean up listeners on unmount", () => {
    const props = {
      component: {
        data: {
          source: "favorites-source",
          type: "FAVOURITES",
        },
        ...componentRequiredKeys,
      } as any,
      children: mockChildren,
      riverId: "test-river",
    };

    const { unmount } = renderWithProviders(<UrlFeedResolver {...props} />);

    unmount();

    expect(favoritesListener.removeHandler).toHaveBeenCalledWith(
      "FAVORITES_CHANGED",
      mockReloadData
    );
  });
});
