import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { fireEvent, render } from "@testing-library/react";
import { getArticle } from "../../lib/api";
import { Language } from "../../lib/types";
import useNavState from "../../store/navStore";
import useUIStore from "../../store/uiStore";
import Panel from "./Panel";

jest.mock("../../store/uiStore");
jest.mock("../../store/navStore");
jest.mock("../../lib/api");
jest.mock("../../constants", () =>
  Promise.resolve({
    API_URL: "http://localhost:8000/api", // Mock the API_URL to a local server for testing
  })
);

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
    },
  },
});
const renderWithClient = (ui: React.ReactElement) => {
  const { rerender, ...result } = render(
    <QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>
  );
  return {
    ...result,
    rerender: (rerenderUi: React.ReactElement) =>
      rerender(
        <QueryClientProvider client={queryClient}>
          {rerenderUi}
        </QueryClientProvider>
      ),
  };
};

const wrapper = ({ children }) => (
  <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);

describe("Panel Component", () => {
  const mockSetSelectedParagraphId = jest.fn();
  const mockUseUIStore = {
    selectedParagraphId: null,
    setSelectedParagraphId: mockSetSelectedParagraphId,
  };
  const mockNavState = {
    celexId: "123",
    articleId: 1,
  };

  beforeEach(() => {
    jest.mocked(useNavState).mockReturnValue(mockNavState);
    jest.mocked(useUIStore).mockReturnValue(mockUseUIStore);
  });

  afterEach(() => {
    jest.clearAllMocks();
    queryClient.clear();
  });

  test("renders loading state", () => {
    (getArticle as jest.Mock).mockReturnValue(new Promise(() => {}));
    const { getByText } = render(<Panel />, { wrapper });
    expect(getByText("Loading...")).toBeInTheDocument();
  });

  test("renders error state", async () => {
    (getArticle as jest.Mock).mockRejectedValue(new Error("Failed to fetch"));
    const { findByText } = renderWithClient(<Panel />);
    expect(
      await findByText("An error has occurred: Failed to fetch")
    ).toBeInTheDocument();
  });

  test("renders article content", async () => {
    const mockData = `<div class='paragraph' data-paragraph-id='1'>Test Content</div>`;
    jest.mocked(getArticle).mockResolvedValue(mockData);

    const result = renderWithClient(<Panel />);
    expect(await result.findByText("Test Content")).toBeInTheDocument();
  });

  test("highlights a paragraph on click", async () => {
    const mockData = `
            <div class='paragraph' data-paragraph-id='1'>Paragraph 1</div>
            <div class='paragraph' data-paragraph-id='2'>Paragraph 2</div>
        `;
    (getArticle as jest.Mock).mockResolvedValue(mockData);

    const result = renderWithClient(<Panel />);

    const paragraph1 = await result.findByText("Paragraph 1");
    const paragraph2 = await result.findByText("Paragraph 2");

    fireEvent.click(paragraph1);
    expect(paragraph1.classList.contains("highlight")).toBe(true);
    expect(paragraph2.classList.contains("highlight")).toBe(false);
    expect(mockSetSelectedParagraphId).toHaveBeenCalledWith("1");

    fireEvent.click(paragraph2);
    expect(paragraph1.classList.contains("highlight")).toBe(false);
    expect(paragraph2.classList.contains("highlight")).toBe(true);
    expect(mockSetSelectedParagraphId).toHaveBeenCalledWith("2");
  });

  test("renders LanguageSwitcher and updates language", async () => {
    jest
      .mocked(getArticle)
      .mockResolvedValue(
        "<div class='paragraph' data-paragraph-id='1'>Test Content</div>"
      );
    const result = renderWithClient(<Panel language={Language.FRA} />);

    const languageSwitcher = await result.findByRole("combobox");
    expect(languageSwitcher).toBeInTheDocument();

    fireEvent.change(languageSwitcher, { target: { value: Language.ENG } });
    expect(jest.mocked(getArticle)).toHaveBeenCalledWith(
      "123",
      1,
      Language.ENG
    );
  });
});
