import { Nullable } from "@reins/types";
import { act, render, waitFor } from "@testing-library/react";
import { memo } from "react";

import { createForm } from "factory";

type InitialValues = {
  name: string;
  surname: string;
  age: number | null;
  address: Nullable<{
    street: string;
    city: string;
    country: string;
  }>;
};

const initialValues: InitialValues = {
  name: "",
  surname: "",
  age: null,
  address: null,
};

describe("useField [ Base ]", () => {
  const onSubmit = jest.fn();
  let form = createForm(initialValues);
  let rerendersForm = 0;
  let rerendersField1 = 0;
  let rerendersField2 = 0;

  beforeEach(() => {
    form = createForm(initialValues);
    onSubmit.mockClear();
    rerendersForm = 0;
    rerendersField1 = 0;
    rerendersField2 = 0;
  });

  const Field1 = memo(() => {
    const { field } = form.useField<"name">("name");
    rerendersField1 += 1;
    return <input data-testid="1" {...field} onChange={(e) => field.onChange(e.target.value, e)} />;
  });

  const Field2 = memo(() => {
    const { field } = form.useField<"surname">("surname");
    rerendersField2 += 1;
    return <input data-testid="2" {...field} onChange={(e) => field.onChange(e.target.value, e)} />;
  });

  const App = () => {
    rerendersForm += 1;
    return (
      <form.Form onSubmit={onSubmit}>
        <Field1 />
        <Field2 />
      </form.Form>
    );
  };

  it("should optimize unnecessary re-renders", async () => {
    const { getByTestId } = render(<App />);

    act(() => {
      form.form.setFieldValue("name", "new name");
    });

    await waitFor(() => {
      expect(rerendersForm).toBe(1);
      expect(rerendersField1).toBe(2);
      expect(rerendersField2).toBe(1);

      const input1 = getByTestId("1");
      const input2 = getByTestId("2");

      expect(input1).toHaveValue("new name");
      expect(input2).toHaveValue("");
    });
  });
});
