import { render, screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";

import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger
} from "./dialog";

describe("Dialog", () => {
  it("opens from the trigger and closes from the close control", async () => {
    const user = userEvent.setup();

    render(
      <Dialog>
        <DialogTrigger>Open dialog</DialogTrigger>
        <DialogContent size="lg">
          <DialogHeader>
            <DialogTitle>Review launch</DialogTitle>
            <DialogDescription>Check the launch checklist before shipping.</DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <DialogClose>Done</DialogClose>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    );

    expect(screen.queryByRole("dialog")).not.toBeInTheDocument();

    await user.click(screen.getByRole("button", { name: "Open dialog" }));

    const dialog = await screen.findByRole("dialog");
    expect(dialog).toHaveAttribute("data-slot", "content");
    expect(dialog).toHaveAttribute("data-size", "lg");
    expect(screen.getByText("Review launch")).toHaveAttribute("data-slot", "label");
    expect(screen.getByText("Check the launch checklist before shipping.")).toHaveAttribute(
      "data-slot",
      "description"
    );
    expect(document.querySelector('[data-slot="overlay"]')).toBeInTheDocument();

    await user.click(screen.getByRole("button", { name: "Close dialog" }));

    await waitFor(() => {
      expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
    });
  });

  it("notifies controlled open state changes from trigger and Escape", async () => {
    const user = userEvent.setup();
    const onOpenChange = vi.fn();

    render(
      <Dialog open={false} onOpenChange={onOpenChange}>
        <DialogTrigger>Launch dialog</DialogTrigger>
        <DialogContent>
          <DialogTitle>Controlled</DialogTitle>
        </DialogContent>
      </Dialog>
    );

    await user.click(screen.getByRole("button", { name: "Launch dialog" }));
    expect(onOpenChange).toHaveBeenCalledWith(true);

    render(
      <Dialog open onOpenChange={onOpenChange}>
        <DialogTrigger>Launch dialog</DialogTrigger>
        <DialogContent>
          <DialogTitle>Controlled</DialogTitle>
        </DialogContent>
      </Dialog>
    );

    await user.keyboard("{Escape}");
    expect(onOpenChange).toHaveBeenCalledWith(false);
  });

  it("renders header and footer slots when provided", async () => {
    const user = userEvent.setup();

    render(
      <Dialog>
        <DialogTrigger>Open summary</DialogTrigger>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Summary</DialogTitle>
          </DialogHeader>
          <DialogFooter>
            <DialogClose>Close</DialogClose>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    );

    await user.click(screen.getByRole("button", { name: "Open summary" }));

    const dialog = await screen.findByRole("dialog");
    expect(within(dialog).getByText("Summary").closest('[data-slot="header"]')).toBeInTheDocument();
    expect(within(dialog).getByRole("button", { name: "Close" }).closest('[data-slot="footer"]')).toBeInTheDocument();
  });

  it("returns focus to the trigger after Escape closes the dialog", async () => {
    const user = userEvent.setup();

    render(
      <Dialog>
        <DialogTrigger>Open accessible dialog</DialogTrigger>
        <DialogContent>
          <DialogTitle>Accessibility</DialogTitle>
        </DialogContent>
      </Dialog>
    );

    const trigger = screen.getByRole("button", { name: "Open accessible dialog" });

    await user.click(trigger);
    expect(await screen.findByRole("dialog")).toBeInTheDocument();

    await user.keyboard("{Escape}");

    await waitFor(() => {
      expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
      expect(trigger).toHaveFocus();
    });
  });
});
