import { render, screen } from "@testing-library/react";
import { TextField } from ".";

const WRAPPER_CLASS_NAME = "mobius-text-field";
const INPUT_CLASS_NAME = "mobius-text-field__input";

const INNER_CONTAINER_SELECTOR = ".mobius-text-field__inner-container";
const INPUT_WRAPPER_SELECTOR = ".mobius-text-field__input-wrapper";
const CHILDREN_CONTAINER_CLASS_NAME = "mobius-text-field__children";
const PREFIX_OUTSIDE_CLASS_NAME = "mobius-text-field__prefix-outside";
const PREFIX_INSIDE_CLASS_NAME = "mobius-text-field__prefix-inside";
const SUFFIX_INSIDE_CLASS_NAME = "mobius-text-field__suffix-inside";
const SUFFIX_OUTSIDE_CLASS_NAME = "mobius-text-field__suffix-outside";

describe("Test for TextField", () => {
  it("should render without errors", () => {
    render(<TextField label="First name" data-testid="textfield" />);
    expect(screen.getByTestId("textfield")).toHaveAttribute("type", "text");
  });

  it("correctly passes on type attribute", () => {
    render(
      <TextField label="Secret Value" type="password" data-testid="test" />,
    );
    expect(screen.getByTestId("test")).toHaveAttribute("type", "password");
  });

  describe("autocomplete", () => {
    it('defaults autoComplete to "email" when type is "email"', () => {
      render(<TextField label="Email" type="email" data-testid="test" />);
      expect(screen.getByTestId("test")).toHaveAttribute(
        "autocomplete",
        "email",
      );
    });

    it('defaults autoComplete to "tel" when type is "tel"', () => {
      render(<TextField label="Phone" type="tel" data-testid="test" />);
      expect(screen.getByTestId("test")).toHaveAttribute("autocomplete", "tel");
    });

    it("does not set autoComplete by default for other types", () => {
      render(<TextField label="Name" data-testid="test" />);
      expect(screen.getByTestId("test")).not.toHaveAttribute("autocomplete");
    });

    it("allows autoComplete to be overridden", () => {
      render(
        <TextField
          label="Email"
          type="email"
          autoComplete="username"
          data-testid="test"
        />,
      );
      expect(screen.getByTestId("test")).toHaveAttribute(
        "autocomplete",
        "username",
      );
    });
  });

  it("children props render outside inner container", () => {
    const pText = "Additional content";

    const { container } = render(
      <TextField label="What does your business do?">
        <p>{pText}</p>
      </TextField>,
    );

    const children = screen.getByText(pText);
    const innerContainer = container.querySelector(INNER_CONTAINER_SELECTOR);

    expect(innerContainer?.nextSibling).toBe(children.parentElement);
    expect(children.parentElement).toHaveClass(CHILDREN_CONTAINER_CLASS_NAME);
  });

  describe("isRequired", () => {
    it("should set aria-required to true", () => {
      render(<TextField label="First name" isRequired />);
      expect(screen.getByLabelText("First name")).toBeRequired();
    });

    it("should have className of --is-required when true", () => {
      render(<TextField label="First name" isRequired />);
      expect(screen.getByLabelText("First name")).toHaveClass("--is-required");
    });

    it("should have className of --is-optional when false", () => {
      render(<TextField label="First name" isRequired={false} />);
      expect(screen.getByLabelText("First name")).toHaveClass("--is-optional");
    });

    it("should have neither --is-required nor --is-optional when unset", () => {
      render(<TextField label="First name" />);
      expect(screen.getByLabelText("First name")).not.toHaveClass(
        "--is-required",
      );
      expect(screen.getByLabelText("First name")).not.toHaveClass(
        "--is-optional",
      );
    });
  });

  describe("uses correct class names", () => {
    describe("for container", () => {
      it("uses Mobius class", () => {
        const { container } = render(
          <TextField label="TextField" data-testid="test" />,
        );
        expect(container.firstChild).toHaveClass("mobius");
      });

      it("uses base Mobius class", () => {
        const { container } = render(
          <TextField label="TextField" data-testid="test" />,
        );
        expect(container.firstChild).toHaveClass(WRAPPER_CLASS_NAME);
      });

      it("includes disabled class name", () => {
        const { container } = render(
          <TextField label="TextField" data-testid="test" isDisabled />,
        );
        expect(container.firstChild).toHaveClass("--is-disabled");
      });

      it("includes valid class name", () => {
        const { container } = render(
          <TextField label="TextField" data-testid="test" isInvalid={false} />,
        );
        expect(container.firstChild).toHaveClass("--is-valid");
      });

      it("includes invalid class name", () => {
        const { container } = render(
          <TextField label="TextField" data-testid="test" isInvalid />,
        );
        expect(container.firstChild).toHaveClass("--is-invalid");
      });

      it("includes custom class name", () => {
        const { container } = render(
          <TextField
            label="TextField"
            data-testid="test"
            className="my-class"
          />,
        );
        expect(container.firstChild).toHaveClass("my-class");
      });
    });

    describe("for input", () => {
      it("uses Mobius class", () => {
        render(<TextField label="TextField" data-testid="test" />);
        expect(screen.getByTestId("test")).toHaveClass("mobius");
      });

      it("uses base Mobius class", () => {
        render(<TextField label="TextField" data-testid="test" />);
        expect(screen.getByTestId("test")).toHaveClass(INPUT_CLASS_NAME);
      });

      it("includes disabled class name and attribute", () => {
        render(<TextField label="TextField" data-testid="test" isDisabled />);

        const inputField = screen.getByTestId("test");

        expect(inputField).toBeDisabled();
        expect(screen.getByLabelText("TextField")).toHaveClass("--is-disabled");
      });

      it("includes readonly class name and attribute", () => {
        render(<TextField label="TextField" data-testid="test" isReadOnly />);

        const inputField = screen.getByTestId("test");

        expect(inputField).toHaveAttribute("readonly");
      });

      it("includes valid class name", () => {
        render(<TextField label="TextField" isInvalid={false} />);
        expect(screen.getByLabelText("TextField")).toHaveClass("--is-valid");
      });

      it("includes invalid class name", () => {
        render(<TextField label="TextField" isInvalid />);
        expect(screen.getByLabelText("TextField")).toHaveClass("--is-invalid");
      });

      it("includes custom class name", () => {
        render(
          <TextField
            label="TextField"
            data-testid="test"
            className="my-class"
          />,
        );
        expect(screen.getByTestId("test")).toHaveClass("my-class");
      });
    });
  });

  describe("with errors", () => {
    it("should render the error", () => {
      const { getByText } = render(
        <TextField label="Name" name="name" errorMessage="Required" />,
      );
      expect(getByText("Required")).toBeInTheDocument();
    });

    it("should set aria-invalid to true", () => {
      const { getByLabelText } = render(
        <TextField
          label="Name"
          name="name"
          errorMessage="Required"
          isInvalid
        />,
      );
      expect(getByLabelText("Name").getAttribute("aria-invalid")).toBeTruthy();
    });

    it("should set the correct aria-describedby attribute", () => {
      const { getByLabelText } = render(
        <TextField label="Name" name="name" errorMessage="Required" />,
      );
      expect(getByLabelText("Name")).toHaveAccessibleDescription("Required");
    });
  });

  describe("with adornments", () => {
    describe("given prefixOutside", () => {
      it("should render adornment before input wrapper", () => {
        const buttonText = "Start your quote";

        const { container } = render(
          <TextField
            label="What does your business do?"
            prefixOutside={<button type="button">{buttonText}</button>}
          />,
        );

        const adornment = screen.getByText(buttonText);
        const inputWrapper = container.querySelector(INPUT_WRAPPER_SELECTOR);

        expect(inputWrapper?.previousSibling).toBe(adornment);
        expect(adornment).toHaveClass(PREFIX_OUTSIDE_CLASS_NAME);
      });
    });

    describe("given prefixInside", () => {
      it("should render adornment before input", () => {
        const pText = "$";

        const { container } = render(
          <TextField
            label="What does your business do?"
            prefixInside={<p>{pText}</p>}
          />,
        );

        const adornment = screen.getByText(pText);
        const inputWrapper = container.querySelector(INPUT_WRAPPER_SELECTOR);

        expect(inputWrapper?.firstChild).toBe(adornment);
        expect(adornment).toHaveClass(PREFIX_INSIDE_CLASS_NAME);
      });
    });

    describe("given suffixInside", () => {
      it("should render adornment after input", () => {
        const pText = ".00";

        const { container } = render(
          <TextField
            label="What does your business do?"
            suffixInside={<p>{pText}</p>}
          />,
        );

        const adornment = screen.getByText(pText);
        const inputWrapper = container.querySelector(INPUT_WRAPPER_SELECTOR);

        expect(inputWrapper?.lastChild).toBe(adornment);
        expect(adornment).toHaveClass(SUFFIX_INSIDE_CLASS_NAME);
      });
    });

    describe("given suffixOutside", () => {
      it("should render adornment after input container", () => {
        const buttonText = "Start your quote";

        const { container } = render(
          <TextField
            label="What does your business do?"
            suffixOutside={<button type="button">{buttonText}</button>}
          />,
        );

        const adornment = screen.getByText(buttonText);
        const inputWrapper = container.querySelector(INPUT_WRAPPER_SELECTOR);

        expect(inputWrapper?.nextSibling).toBe(adornment);
        expect(adornment).toHaveClass(SUFFIX_OUTSIDE_CLASS_NAME);
      });
    });
  });
});
