import {
  cleanup,
  render,
  screen,
  waitFor,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import assert from "node:assert";
import {
  afterEach,
  describe,
  mock,
  test,
} from "node:test";
import { MaskedInput } from "./index";

const selectors = {
  hiddenInput: "input[data-js-component-input='maskedInput']",
};

const getHiddenInput = (container: HTMLElement): HTMLInputElement => {
  const hiddenInput = container.querySelector(selectors.hiddenInput) as HTMLInputElement | null;
  assert.notStrictEqual(hiddenInput, null);
  return hiddenInput as HTMLInputElement;
};

describe("MaskedInput", () => {
  afterEach(() => {
    cleanup();
  });

  test("renders visible and hidden inputs", () => {
    const { container } = render(<MaskedInput mask={"digits"} name={"phone"} />);

    const visibleInput = screen.getByRole("textbox");
    const hiddenInput = getHiddenInput(container);

    assert.notStrictEqual(visibleInput, null);
    assert.strictEqual(hiddenInput.type, "hidden");
    assert.strictEqual(hiddenInput.name, "phone");
  });

  test("syncs hidden input with initial value", () => {
    const { container } = render(<MaskedInput mask={"digits"} value={"123"} />);
    const hiddenInput = getHiddenInput(container);

    assert.strictEqual(hiddenInput.value, "123");
  });

  test("updates hidden input value immediately on typing", async () => {
    const user = userEvent.setup();
    const { container } = render(<MaskedInput mask={"digits"} />);

    const visibleInput = screen.getByRole("textbox");
    const hiddenInput = getHiddenInput(container);

    await user.type(visibleInput, "12");
    assert.strictEqual(hiddenInput.value, "12");

    await user.type(visibleInput, "3");
    assert.strictEqual(hiddenInput.value, "123");
  });

  test("keeps hidden input synchronized by blur time", async () => {
    const user = userEvent.setup();
    let hiddenValueOnBlur = "";
    const onBlur = mock.fn((arg) => {
      const hiddenInput = arg.el?.querySelector(selectors.hiddenInput) as HTMLInputElement | null;
      hiddenValueOnBlur = hiddenInput?.value || "";
    });

    const { container } = render(
      <MaskedInput
        mask={"digits"}
        onBlur={onBlur}
      />
    );

    const visibleInput = screen.getByRole("textbox");
    const hiddenInput = getHiddenInput(container);

    await user.type(visibleInput, "456");
    await user.tab();

    await waitFor(() => {
      assert.strictEqual(onBlur.mock.calls.length, 1);
      assert.strictEqual(hiddenValueOnBlur, "456");
      assert.strictEqual(hiddenInput.value, "456");
    });
  });
});

