import { createRef } from "react";
import { render, screen } from "@testing-library/react";
import type { ListStyleType } from ".";
import { List, ListItem } from ".";

const LIST_CLASS_NAME = "mobius-list";
const ITEM_CLASS_NAME = "mobius-list__item";
const ICON_CLASS_NAME = "--has-icon";
const ITEM_CONTENT_CLASSNAME = "mobius-list__item-content";
const LIST_ICON_CLASS_NAME = "mobius-list__icon";

describe("List", () => {
  it("should render without errors", () => {
    render(<List>Sample text</List>);
  });

  it("renders an unordered list of items", () => {
    render(
      <List data-testid="test">
        <ListItem>One</ListItem>
        <ListItem>Two</ListItem>
      </List>,
    );
    const list = screen.getByTestId("test").closest("ul");

    expect(list).toBeTruthy();
    expect(list?.childNodes.length).toEqual(2);
  });

  it("renders an ordered list of items", () => {
    render(
      <List isOrdered data-testid="test">
        <ListItem>One</ListItem>
        <ListItem>Two</ListItem>
      </List>,
    );
    const unorderedList = screen.getByTestId("test").closest("ul");
    const orderedList = screen.getByTestId("test").closest("ol");

    expect(unorderedList).not.toBeTruthy();
    expect(orderedList).toBeTruthy();
    expect(orderedList?.childNodes.length).toEqual(2);
  });

  it("renders a reversed list of items", () => {
    render(
      <List isOrdered isReversed>
        <ListItem>One</ListItem>
        <ListItem>Two</ListItem>
      </List>,
    );

    const list = screen.getByRole("list");

    expect(list).toHaveAttribute("reversed", "");
  });

  it("renders list style", () => {
    render(
      <List isOrdered liststyletype="disc" data-testid="test">
        <ListItem>One</ListItem>
        <ListItem>Two</ListItem>
      </List>,
    );
    expect(screen.getByTestId("test").closest("ol")).not.toHaveAttribute(
      "liststyletype",
      "disc",
    );
  });

  describe("given the liststyletype prop is set", () => {
    it.each([
      "disc",
      "circle",
      "square",
      "decimal",
      "georgian",
      "trad-chinese-informal",
      "kannada",
    ])("it should render with --is-%s class name", liststyletype => {
      const { container } = render(
        <List isOrdered liststyletype={liststyletype as ListStyleType}>
          <ListItem>One</ListItem>
          <ListItem>Two</ListItem>
        </List>,
      );

      expect(container.firstChild).toHaveClass(`--is-${liststyletype}`);
    });
  });

  it("renders list with custom icons", () => {
    render(
      <List icon="✅">
        <ListItem>One</ListItem>
        <ListItem>Two</ListItem>
        <ListItem icon="⚠️">Three</ListItem>
        <ListItem>Four</ListItem>
        <ListItem icon="❌">Five</ListItem>
        <ListItem>Six</ListItem>
      </List>,
    );

    expect(screen.queryAllByText("✅")).toHaveLength(4);
    expect(screen.queryAllByText("⚠️")).toHaveLength(1);
    expect(screen.queryAllByText("❌")).toHaveLength(1);
  });

  describe("uses correct class names", () => {
    it("uses Mobius class", () => {
      render(<List data-testid="test">Sample text</List>);
      expect(screen.getByTestId("test")).toHaveClass("mobius");
    });

    it("uses base Mobius class", () => {
      render(<List data-testid="test">Sample text</List>);
      expect(screen.getByTestId("test")).toHaveClass(LIST_CLASS_NAME);
    });

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

    it("uses base Mobius class for list items", () => {
      render(
        <List isOrdered data-testid="test">
          <ListItem>One</ListItem>
          <ListItem>Two</ListItem>
        </List>,
      );
      const orderedList = screen.getByTestId("test").closest("ol");

      expect(orderedList?.childNodes[0]).toHaveClass(ITEM_CLASS_NAME);
      expect(orderedList?.childNodes[1]).toHaveClass(ITEM_CLASS_NAME);
    });

    it("includes custom class names for list items", () => {
      render(
        <List isOrdered data-testid="test">
          <ListItem className="item-1">One</ListItem>
          <ListItem className="item-2">Two</ListItem>
        </List>,
      );
      const orderedList = screen.getByTestId("test").closest("ol");
      expect(orderedList?.childNodes[0]).toHaveClass(ITEM_CLASS_NAME);
      expect(orderedList?.childNodes[0]).toHaveClass("item-1");
      expect(orderedList?.childNodes[1]).toHaveClass(ITEM_CLASS_NAME);
      expect(orderedList?.childNodes[1]).toHaveClass("item-2");
    });

    it("includes custom class name when icon prop is provided", () => {
      render(
        <List icon="✅">
          <ListItem>One</ListItem>
        </List>,
      );
      expect(screen.getByRole("list")).toHaveClass(ICON_CLASS_NAME);
      expect(screen.getByRole("listitem")).toHaveClass(ICON_CLASS_NAME);
      expect(screen.getByRole("listitem").firstChild).toHaveClass(
        ITEM_CONTENT_CLASSNAME,
      );
      expect(screen.getByText("✅")).toHaveClass(LIST_ICON_CLASS_NAME);
    });
  });

  it("forwards ref", () => {
    const ref = createRef<HTMLOListElement & HTMLUListElement>();
    render(
      <List ref={ref}>
        <ListItem>item</ListItem>
      </List>,
    );
    expect(ref.current).toBeInstanceOf(HTMLUListElement);
  });
});
