import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { BanUnbanForm } from "../BanUnbanForm";

const getRequiredElement = <T extends Element>(elements: T[], index: number): T => {
  const element = elements[index];
  if (!element) {
    throw new Error(`Expected element at index ${String(index)}`);
  }
  return element;
};

describe("BanUnbanForm", () => {
  it("disables the ban button while the ban request is pending and shows success", async () => {
    let resolveBan: () => void = () => undefined;
    const onBan = vi.fn(
      () =>
        new Promise<void>((resolve) => {
          resolveBan = resolve;
        }),
    );
    const onUnban = vi.fn().mockResolvedValue(undefined);
    const user = userEvent.setup();

    render(<BanUnbanForm jailNames={["sshd"]} onBan={onBan} onUnban={onUnban} />);

    const banIpInputs = screen.getAllByPlaceholderText("e.g. 192.168.1.100");
    const banIpInput = getRequiredElement(banIpInputs, 0);
    await user.type(banIpInput, "192.168.1.100");

    const selectElements = screen.getAllByRole("combobox");
    const banSelect = getRequiredElement(selectElements, 0);
    await user.selectOptions(banSelect, "sshd");

    const banButton = screen.getByRole("button", { name: /^ban$/i });
    await user.click(banButton);

    expect(onBan).toHaveBeenCalledWith("sshd", "192.168.1.100");
    expect(banButton).toBeDisabled();

    resolveBan();
    await waitFor(() => {
      expect(screen.getByText(/192\.168\.1\.100 banned in sshd\./i)).toBeInTheDocument();
      expect(banButton).not.toBeDisabled();
    });
  });

  it("shows an error when unban fails and keeps the unban button disabled during the request", async () => {
    let rejectUnban: (reason?: unknown) => void = () => undefined;
    const onBan = vi.fn().mockResolvedValue(undefined);
    const onUnban = vi.fn(
      () =>
        new Promise<void>((_resolve, reject) => {
          rejectUnban = reject;
        }),
    );
    const user = userEvent.setup();

    render(<BanUnbanForm jailNames={["sshd"]} onBan={onBan} onUnban={onUnban} />);

    const unbanIpInputs = screen.getAllByPlaceholderText("e.g. 192.168.1.100");
    const unbanIpInput = getRequiredElement(unbanIpInputs, 1);
    await user.type(unbanIpInput, "10.0.0.1");

    const unbanButton = screen.getByRole("button", { name: /^unban$/i });
    await user.click(unbanButton);

    await waitFor(() => {
      expect(unbanButton).toBeDisabled();
    });

    rejectUnban(new Error("unban failed"));

    await waitFor(() => {
      expect(screen.getByText(/unban failed/i)).toBeInTheDocument();
      expect(unbanButton).not.toBeDisabled();
    });
  });
});
