import React from "react";
import { render, act, waitFor } from "@testing-library/react-native";
import { StaticFeedResolver } from "../resolvers/StaticFeedResolver";
import { View } from "react-native";
import { ReloadDataFunction } from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";

type MockChildrenArgs = {
  zappPipesData?: {
    loading: boolean;
    data?: any;
    error?: Error | null;
  };
  reloadData?: ReloadDataFunction;
};

describe("StaticFeedResolver", () => {
  const mockChildren = jest.fn((_args: MockChildrenArgs) => (
    <View data-testid="mock-children" />
  ));

  const mockComponent = {
    id: "test-component",
    position: 1,
  } as any;

  const mockGetStaticComponentFeed = jest.fn();

  beforeEach(() => {
    jest.clearAllMocks();
    mockGetStaticComponentFeed.mockResolvedValue({ entry: [{ id: "1" }] });
  });

  it("should generate correct static URL based on component", () => {
    render(
      <StaticFeedResolver
        getStaticComponentFeed={mockGetStaticComponentFeed}
        component={mockComponent}
        componentIndex={0}
        riverId="test-river"
      >
        {mockChildren}
      </StaticFeedResolver>
    );

    expect(mockChildren).toHaveBeenCalledWith(
      expect.objectContaining({
        zappPipesData: expect.objectContaining({
          url: "static://component?id=test-component&position=1",
        }),
      })
    );
  });

  it("should start with loading state", () => {
    render(
      <StaticFeedResolver
        getStaticComponentFeed={mockGetStaticComponentFeed}
        component={mockComponent}
        componentIndex={0}
        riverId="test-river"
      >
        {mockChildren}
      </StaticFeedResolver>
    );

    expect(mockChildren).toHaveBeenCalledWith(
      expect.objectContaining({
        zappPipesData: expect.objectContaining({
          loading: true,
          data: null,
          error: null,
        }),
      })
    );
  });

  it("should fetch data when mounted", async () => {
    render(
      <StaticFeedResolver
        getStaticComponentFeed={mockGetStaticComponentFeed}
        component={mockComponent}
        componentIndex={0}
        riverId="test-river"
      >
        {mockChildren}
      </StaticFeedResolver>
    );

    expect(mockGetStaticComponentFeed).toHaveBeenCalledWith({
      index: 0,
      component: mockComponent,
    });

    await waitFor(() => {
      expect(mockChildren).toHaveBeenLastCalledWith(
        expect.objectContaining({
          zappPipesData: expect.objectContaining({
            loading: false,
            data: { entry: [{ id: "1" }] },
          }),
        })
      );
    });
  });

  it("should handle errors from getStaticComponentFeed", async () => {
    const error = new Error("Test error");
    mockGetStaticComponentFeed.mockRejectedValue(error);

    render(
      <StaticFeedResolver
        getStaticComponentFeed={mockGetStaticComponentFeed}
        component={mockComponent}
        componentIndex={0}
        riverId="test-river"
      >
        {mockChildren}
      </StaticFeedResolver>
    );

    await waitFor(() => {
      expect(mockChildren).toHaveBeenLastCalledWith(
        expect.objectContaining({
          zappPipesData: expect.objectContaining({
            loading: false,
            error,
          }),
        })
      );
    });
  });

  it("should provide a reloadData function that refreshes the data", async () => {
    render(
      <StaticFeedResolver
        getStaticComponentFeed={mockGetStaticComponentFeed}
        component={mockComponent}
        componentIndex={0}
        riverId="test-river"
      >
        {mockChildren}
      </StaticFeedResolver>
    );

    await waitFor(() => {
      expect(mockChildren).toHaveBeenLastCalledWith(
        expect.objectContaining({
          zappPipesData: expect.objectContaining({
            loading: false,
          }),
        })
      );
    });

    const { reloadData } =
      mockChildren.mock.calls[mockChildren.mock.calls.length - 1][0];

    mockGetStaticComponentFeed.mockClear();
    mockGetStaticComponentFeed.mockResolvedValue({ entry: [{ id: "2" }] });

    await act(async () => {
      await reloadData();
    });

    expect(mockGetStaticComponentFeed).toHaveBeenCalledWith({
      index: 0,
      component: mockComponent,
    });

    await waitFor(() => {
      expect(mockChildren).toHaveBeenLastCalledWith(
        expect.objectContaining({
          zappPipesData: expect.objectContaining({
            data: { entry: [{ id: "2" }] },
          }),
        })
      );
    });
  });

  it("should handle errors in reloadData", async () => {
    render(
      <StaticFeedResolver
        getStaticComponentFeed={mockGetStaticComponentFeed}
        component={mockComponent}
        componentIndex={0}
        riverId="test-river"
      >
        {mockChildren}
      </StaticFeedResolver>
    );

    await waitFor(() => {
      expect(mockChildren).toHaveBeenLastCalledWith(
        expect.objectContaining({
          zappPipesData: expect.objectContaining({
            loading: false,
          }),
        })
      );
    });

    const { reloadData } =
      mockChildren.mock.calls[mockChildren.mock.calls.length - 1][0];

    const error = new Error("Reload error");
    mockGetStaticComponentFeed.mockRejectedValue(error);

    await expect(reloadData()).rejects.toEqual(error);

    await waitFor(() => {
      expect(mockChildren).toHaveBeenLastCalledWith(
        expect.objectContaining({
          zappPipesData: expect.objectContaining({
            error,
          }),
        })
      );
    });
  });

  it("should not fetch data if getStaticComponentFeed is not provided", async () => {
    render(
      <StaticFeedResolver
        component={mockComponent}
        componentIndex={0}
        riverId="test-river"
      >
        {mockChildren}
      </StaticFeedResolver>
    );

    expect(mockGetStaticComponentFeed).not.toHaveBeenCalled();
  });

  it("should provide reloadData function that resolves immediately if getStaticComponentFeed is not provided", async () => {
    render(
      <StaticFeedResolver
        component={mockComponent}
        componentIndex={0}
        riverId="test-river"
      >
        {mockChildren}
      </StaticFeedResolver>
    );

    const { reloadData } = mockChildren.mock.calls[0][0];

    await expect(reloadData()).resolves.toBeUndefined();
  });
});
