/*
Copyright (C) 2025  Cloudbase Solutions SRL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import NumberedPagination from "./NumberedPagination";
import TestUtils from "@tests/TestUtils";

const NumberedPaginationWithDefaultProps = (
  props: Partial<NumberedPagination["props"]>,
) => (
  <NumberedPagination
    itemsCount={props.itemsCount || 100}
    currentPage={props.currentPage || 2}
    itemsPerPage={props.itemsPerPage || 10}
    itemsPerPageOptions={props.itemsPerPageOptions || [5, 10, 20, 50]}
    onPageChange={props.onPageChange || (() => {})}
    onItemsPerPageChange={props.onItemsPerPageChange || (() => {})}
    style={props.style}
    hasNextPage={props.hasNextPage}
  />
);

describe("NumberedPagination", () => {
  it("renders", () => {
    render(<NumberedPaginationWithDefaultProps />);
    expect(
      TestUtils.select("NumberedPagination__PageNumber")?.textContent,
    ).toBe("Page 2 of 10");
  });

  it("handles previous and next click", async () => {
    const onPageChange = jest.fn();

    render(<NumberedPaginationWithDefaultProps onPageChange={onPageChange} />);

    const previousButton = screen.getByRole("button", { name: "Previous" });
    const nextButton = screen.getByRole("button", { name: "Next" });

    await userEvent.click(previousButton);
    expect(onPageChange).toHaveBeenCalled();

    await userEvent.click(nextButton);
    expect(onPageChange).toHaveBeenCalled();
  });

  it("handles disabled states", async () => {
    let onPageChange = jest.fn();

    const { rerender } = render(
      <NumberedPaginationWithDefaultProps
        onPageChange={onPageChange}
        currentPage={1}
        itemsCount={30}
        itemsPerPage={10}
      />,
    );
    let previousButton = screen.getByRole("button", { name: "Previous" });
    let nextButton = screen.getByRole("button", { name: "Next" });

    await userEvent.click(previousButton);
    expect(onPageChange).not.toHaveBeenCalled();

    await userEvent.click(nextButton);
    expect(onPageChange).toHaveBeenCalled();

    onPageChange = jest.fn();
    rerender(
      <NumberedPaginationWithDefaultProps
        onPageChange={onPageChange}
        currentPage={3}
        itemsCount={30}
        itemsPerPage={10}
      />,
    );

    previousButton = screen.getByRole("button", { name: "Previous" });
    nextButton = screen.getByRole("button", { name: "Next" });

    await userEvent.click(previousButton);
    expect(onPageChange).toHaveBeenCalled();

    onPageChange = jest.fn();

    expect(nextButton).toHaveProperty("disabled", true);
    await userEvent.click(nextButton);
    expect(onPageChange).not.toHaveBeenCalled();
  });

  it("renders all items per page options", () => {
    const itemsPerPageOptions = [5, 10, 20, 50, 100];

    render(
      <NumberedPaginationWithDefaultProps
        itemsPerPageOptions={itemsPerPageOptions}
      />,
    );

    const options = screen.getAllByRole("option");

    expect(options.length).toBe(itemsPerPageOptions.length);
    itemsPerPageOptions.forEach((option, index) => {
      expect((options[index] as HTMLOptionElement).value).toBe(
        option.toString(),
      );
      expect(options[index].textContent).toBe(option.toString());
    });
  });

  describe("API pagination mode (hasNextPage prop)", () => {
    it("shows 'of N' total derived from itemsCount even when hasNextPage prop is provided", () => {
      render(
        <NumberedPaginationWithDefaultProps
          currentPage={1}
          itemsCount={100}
          itemsPerPage={10}
          hasNextPage={true}
        />,
      );
      expect(
        TestUtils.select("NumberedPagination__PageNumber")?.textContent,
      ).toBe("Page 1 of 10");
    });

    it("disables Next button when hasNextPage is false even if itemsCount suggests more pages", () => {
      const onPageChange = jest.fn();
      render(
        <NumberedPaginationWithDefaultProps
          currentPage={1}
          itemsCount={100}
          itemsPerPage={10}
          hasNextPage={false}
          onPageChange={onPageChange}
        />,
      );
      const nextButton = screen.getByRole("button", { name: "Next" });
      expect(nextButton).toHaveProperty("disabled", true);
    });

    it("enables Next button when hasNextPage is true", () => {
      const onPageChange = jest.fn();
      render(
        <NumberedPaginationWithDefaultProps
          currentPage={3}
          itemsCount={30}
          itemsPerPage={10}
          hasNextPage={true}
          onPageChange={onPageChange}
        />,
      );
      const nextButton = screen.getByRole("button", { name: "Next" });
      expect(nextButton).not.toHaveProperty("disabled", true);
    });
  });
});
