import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { useState } from "react";
import { Drawer } from ".";
import { htmlDialogPolyfill } from "../../utils/htmlDialogPolyfill";
import { mockMatchMedia } from "../../utils/mockMatchMedia";

htmlDialogPolyfill();

const DRAWER_CLASS_NAME = "mobius-drawer";
const DRAWER_CLOSE_CLASS_NAME = "mobius-drawer__close";
const DRAWER_TEST_CLASS_NAME = "my-test-class-name";

describe("Drawer", () => {
  beforeEach(() => {
    mockMatchMedia(true);
  });

  afterAll(() => {
    vi.resetAllMocks();
  });

  it("should render without errors", () => {
    const { container } = render(<Drawer isOpen direction="right" />);
    expect(container).toBeTruthy();
  });

  it("should render with default class names", async () => {
    render(
      <Drawer isOpen direction="right">
        <Drawer.Header />
      </Drawer>,
    );

    await screen.findByRole("dialog");

    const dialog = screen.getByRole("dialog");
    const closeButton = screen.getByLabelText("Close");

    expect(dialog).toHaveClass(DRAWER_CLASS_NAME);
    expect(closeButton).toHaveClass(DRAWER_CLOSE_CLASS_NAME);
  });

  it("should render with custom class name", async () => {
    render(
      <Drawer direction="right" className={DRAWER_TEST_CLASS_NAME} isOpen>
        <Drawer.Header />
      </Drawer>,
    );

    await screen.findByRole("dialog");

    const dialog = screen.getByRole("dialog");
    const closeButton = screen.getByLabelText("Close");

    expect(dialog).toHaveClass(DRAWER_CLASS_NAME);
    expect(dialog).toHaveClass(DRAWER_TEST_CLASS_NAME);
    expect(closeButton).toHaveClass(DRAWER_CLOSE_CLASS_NAME);
  });

  describe("given the direction prop is set", () => {
    it("should render with appropriate class name (right)", async () => {
      render(<Drawer isOpen direction="right" />);

      await screen.findByRole("dialog");

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

      expect(dialog).toHaveClass("--right");
    });

    it("should render with appropriate class name (bottom)", async () => {
      render(<Drawer isOpen direction="bottom" />);

      await screen.findByRole("dialog");

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

      expect(dialog).toHaveClass("--bottom");
    });
  });

  describe("screen reader", () => {
    it("should announce a default message on opening", () => {
      render(<Drawer isOpen direction="right" />);

      const message = screen.getByText("Drawer opened on screen");
      expect(message).toBeInTheDocument();
    });

    it("should respect a custom announce message on opening", () => {
      render(<Drawer isOpen direction="right" announce="Quote details page" />);

      const message = screen.getByText("Quote details page");
      expect(message).toBeInTheDocument();
    });
  });

  describe("pressing close button", () => {
    it("should close the drawer", async () => {
      const App = () => {
        const [isOpen, setIsOpen] = useState<boolean>(true);

        return (
          <Drawer
            direction="right"
            isOpen={isOpen}
            onClose={() => setIsOpen(false)}
          >
            <Drawer.Header>Header</Drawer.Header>
            <Drawer.Content>Sample content</Drawer.Content>
          </Drawer>
        );
      };

      render(<App />);

      await screen.findByRole("dialog");

      const dialog = screen.getByRole("dialog");
      const drawerContent = screen.getByText("Sample content");

      expect(dialog).toHaveAttribute("open");
      expect(drawerContent).toBeInTheDocument();

      const closeButton = screen.getByLabelText("Close");
      await userEvent.click(closeButton);

      await waitFor(() => expect(dialog).not.toHaveAttribute("open"));
    });
  });

  describe("closeLabel prop", () => {
    it("should render the label", () => {
      const closeLabel = "Leave";

      render(
        <Drawer direction="right" isOpen closeLabel={closeLabel}>
          <Drawer.Header />
          Sample content
        </Drawer>,
      );

      const closeButton = screen.getByText(closeLabel);
      expect(closeButton).toBeInTheDocument();
    });
  });

  it("should render the default close button", () => {
    render(
      <Drawer isOpen direction="right">
        <Drawer.Header />
      </Drawer>,
    );

    const closeButton = screen.getByLabelText("Close");
    expect(closeButton).toBeInTheDocument();
  });

  it("should fire onOpen callback if supplied", async () => {
    const onOpen = vi.fn();

    const App = () => {
      const [isOpen, setIsOpen] = useState<boolean>(false);

      return (
        <>
          <button
            aria-label="Show drawer"
            id="btn"
            type="button"
            onClick={() => setIsOpen(true)}
          >
            Show drawer
          </button>

          <Drawer
            isOpen={isOpen}
            onClose={() => setIsOpen(false)}
            direction="right"
            onOpen={onOpen}
          >
            <Drawer.Header>Header</Drawer.Header>
            Sample content
          </Drawer>
        </>
      );
    };

    render(<App />);

    const showBtn = screen.getByText("Show drawer");

    expect(showBtn).toBeInTheDocument();
    await userEvent.click(showBtn);
    expect(onOpen).toHaveBeenCalled();
  });

  it.todo("Drawer closes when Escape key is presed");
  // JSDom does not support HTMLDialog click events
  it.todo("Drawer closes when backdrop is clicked");
});
