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

import { Checker } from "./index";

describe("Checker", () => {
  /* COMMON */

  afterEach(() => {
    cleanup();
  });

  test("renders input with passed label", () => {
    render(<Checker name={"test"} label={"Text label"} />);

    const input = screen.queryByRole("checkbox");
    const label = document.querySelector(".checkbox__label");

    assert.notStrictEqual(input, null);
    assert.notStrictEqual(label, null);
    assert.strictEqual(label?.textContent, "Text label");
  });

  test("renders initial isChecked and isDisabled states", () => {
    render(<Checker name={"test"} isChecked={true} isDisabled={true} />);

    const input = screen.getByRole("checkbox") as HTMLInputElement;

    assert.strictEqual(input.checked, true);
    assert.strictEqual(input.disabled, true);
  });

  test("should render as checkbox by default", () => {
    render(<Checker name={"test"} type={"checkbox"} />);
    const input = screen.getByRole("checkbox") as HTMLInputElement;

    assert.notStrictEqual(input, null);
    assert.strictEqual(input.getAttribute("type"), "checkbox");
  });

  test("should render as radio when type='radio'", () => {
    render(<Checker name={"test"} type={"radio"} value={"option1"} />);
    const input = screen.getByRole("radio") as HTMLInputElement;

    assert.notStrictEqual(input, null);
    assert.strictEqual(input.getAttribute("type"), "radio");
    assert.strictEqual(input.getAttribute("value"), "option1");
  });

  test("changes state on props change", () => {
    const { rerender } = render(<Checker name={"test"} isChecked={false} />);

    const input = screen.getByRole("checkbox") as HTMLInputElement;
    assert.strictEqual(input.checked, false);

    rerender(<Checker name={"test"} isChecked={true} />);
    assert.strictEqual(input.checked, true);
  });

  /* HANDLERS */

  test("calls onChange when checker is changed", async () => {
    const user = userEvent.setup();
    const onChange = mock.fn();

    render(<Checker onChange={onChange} />);
    await user.click(screen.getByRole("checkbox"));

    assert.strictEqual(onChange.mock.calls.length, 1);
    const arg = onChange.mock.calls[0]?.arguments?.[0];
    assert.ok(arg && typeof arg === "object");
    assert.ok("stateActions" in arg);
  });

  test("does not change checked state and does not call onChange when disabled", async () => {
    const user = userEvent.setup();
    const onChange = mock.fn();

    render(<Checker onChange={onChange} isDisabled={true} isChecked={true} />);
    const checker = screen.getByRole("checkbox") as HTMLInputElement;
    await user.click(checker);

    assert.strictEqual(onChange.mock.calls.length, 0);
    assert.strictEqual(checker.checked, true);
  });

  /* CHILDREN */

  test("renders children instead of default content", () => {
    render(
      <Checker name={"test"}>
        <div data-testid={"custom-child"}>Custom Child</div>
      </Checker>
    );

    assert.notStrictEqual(screen.queryByTestId("custom-child"), null);
    assert.strictEqual(screen.queryByRole("checkbox"), null);
  });

  test("renders html inside label with dangerouslySetInnerHTML", () => {
    render(<Checker name={"test"} label={"<b>Bold Label</b>"} />);

    const label = document.querySelector(".checkbox__label") as HTMLElement | null;
    assert.ok(label);
    assert.strictEqual(label.innerHTML, "<b>Bold Label</b>");
  });
});
