import React from "react";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {
  Modal,
  ModalHeader,
  ModalTitle,
  ModalContent,
  ModalFooter,
} from "../Modal";

describe("Modal", () => {
  // Basic rendering tests
  describe("Rendering", () => {
    it("does not render when closed", () => {
      render(<Modal open={false}>Modal content</Modal>);
      expect(screen.queryByText("Modal content")).not.toBeInTheDocument();
    });

    it("renders when open", () => {
      render(<Modal open={true}>Modal content</Modal>);
      expect(screen.getByText("Modal content")).toBeInTheDocument();
    });

    it("renders with custom className", () => {
      render(
        <Modal open={true} className="custom-modal">
          Content
        </Modal>
      );
      const dialog = screen.getByRole("dialog", { hidden: true });
      expect(dialog).toHaveClass("custom-modal");
    });

    it("renders with different sizes", () => {
      const { rerender } = render(
        <Modal open={true} size="sm">
          Small
        </Modal>
      );
      let dialog = screen.getByRole("dialog", { hidden: true });
      expect(dialog).toHaveClass("max-w-md");

      rerender(
        <Modal open={true} size="lg">
          Large
        </Modal>
      );
      dialog = screen.getByRole("dialog", { hidden: true });
      expect(dialog).toHaveClass("max-w-2xl");

      rerender(
        <Modal open={true} size="full">
          Full
        </Modal>
      );
      dialog = screen.getByRole("dialog", { hidden: true });
      expect(dialog).toHaveClass("max-w-[95vw]");
    });
  });

  // Props tests
  describe("Props", () => {
    it("renders close button by default", () => {
      render(<Modal open={true}>Content</Modal>);
      expect(screen.getByLabelText("Close")).toBeInTheDocument();
    });

    it("hides close button when showCloseButton is false", () => {
      render(
        <Modal open={true} showCloseButton={false}>
          Content
        </Modal>
      );
      expect(screen.queryByLabelText("Close")).not.toBeInTheDocument();
    });

    it("sets proper role", () => {
      const { rerender } = render(
        <Modal open={true} role="dialog">
          Dialog
        </Modal>
      );
      let dialog = screen.getByRole("dialog", { hidden: true });
      expect(dialog).toHaveAttribute("role", "dialog");

      rerender(
        <Modal open={true} role="alertdialog">
          Alert Dialog
        </Modal>
      );
      dialog = screen.getByRole("alertdialog", { hidden: true });
      expect(dialog).toHaveAttribute("role", "alertdialog");
    });

    it("supports ARIA attributes", () => {
      render(
        <Modal
          open={true}
          ariaLabelledBy="modal-title"
          ariaDescribedBy="modal-description"
        >
          Content
        </Modal>
      );
      const dialog = screen.getByRole("dialog", { hidden: true });
      expect(dialog).toHaveAttribute("aria-labelledby", "modal-title");
      expect(dialog).toHaveAttribute("aria-describedby", "modal-description");
    });
  });

  // Interaction tests
  describe("Interactions", () => {
    it("calls onClose when close button is clicked", async () => {
      const handleClose = jest.fn();
      const user = userEvent.setup();
      render(
        <Modal open={true} onClose={handleClose}>
          Content
        </Modal>
      );

      await user.click(screen.getByLabelText("Close"));
      expect(handleClose).toHaveBeenCalledTimes(1);
    });

    it("calls onOpenChange when close button is clicked", async () => {
      const handleOpenChange = jest.fn();
      const user = userEvent.setup();
      render(
        <Modal open={true} onOpenChange={handleOpenChange}>
          Content
        </Modal>
      );

      await user.click(screen.getByLabelText("Close"));
      expect(handleOpenChange).toHaveBeenCalledWith(false);
    });

    it("calls onClose when overlay is clicked", async () => {
      const handleClose = jest.fn();
      const user = userEvent.setup();
      render(
        <Modal open={true} onClose={handleClose}>
          Content
        </Modal>
      );

      const overlay = screen.getByRole("dialog", {
        hidden: true,
      }).parentElement;
      await user.click(overlay!);
      expect(handleClose).toHaveBeenCalledTimes(1);
    });

    it("does not close when overlay is clicked if closeOnOverlayClick is false", async () => {
      const handleClose = jest.fn();
      const user = userEvent.setup();
      render(
        <Modal open={true} onClose={handleClose} closeOnOverlayClick={false}>
          Content
        </Modal>
      );

      const overlay = screen.getByRole("dialog", {
        hidden: true,
      }).parentElement;
      await user.click(overlay!);
      expect(handleClose).not.toHaveBeenCalled();
    });

    it("calls onClose when Escape key is pressed", async () => {
      const handleClose = jest.fn();
      const user = userEvent.setup();
      render(
        <Modal open={true} onClose={handleClose} closeOnEscape={true}>
          Content
        </Modal>
      );

      await user.keyboard("{Escape}");
      expect(handleClose).toHaveBeenCalledTimes(1);
    });

    it("does not close when Escape key is pressed if closeOnEscape is false", async () => {
      const handleClose = jest.fn();
      const user = userEvent.setup();
      render(
        <Modal open={true} onClose={handleClose} closeOnEscape={false}>
          Content
        </Modal>
      );

      await user.keyboard("{Escape}");
      expect(handleClose).not.toHaveBeenCalled();
    });
  });

  // Accessibility tests
  describe("Accessibility", () => {
    it("traps focus within modal", async () => {
      const user = userEvent.setup();
      render(
        <Modal open={true}>
          <button>First Button</button>
          <button>Second Button</button>
        </Modal>
      );

      const firstButton = screen.getByText("First Button");
      const secondButton = screen.getByText("Second Button");

      // Initially, first button should be focused
      await waitFor(() => {
        expect(firstButton).toHaveFocus();
      });

      // Tab to second button
      await user.tab();
      expect(secondButton).toHaveFocus();

      // Tab should wrap to first button (excluding close button from tab order)
      await user.tab();
      expect(firstButton).toHaveFocus();

      // Shift+Tab from first should go to second
      await user.tab({ shift: true });
      expect(secondButton).toHaveFocus();
    });

    it("focuses first focusable element on open", async () => {
      const { rerender } = render(
        <Modal open={false}>
          <button>Focusable Button</button>
        </Modal>
      );

      rerender(
        <Modal open={true}>
          <button>Focusable Button</button>
        </Modal>
      );

      await waitFor(() => {
        expect(screen.getByText("Focusable Button")).toHaveFocus();
      });
    });

    it("restores focus to trigger element on close", async () => {
      const triggerButton = document.createElement("button");
      triggerButton.textContent = "Open Modal";
      document.body.appendChild(triggerButton);
      triggerButton.focus();

      const { rerender } = render(
        <Modal open={false}>
          <div>Modal Content</div>
        </Modal>
      );

      rerender(
        <Modal open={true}>
          <div>Modal Content</div>
        </Modal>
      );

      // Close modal
      rerender(
        <Modal open={false}>
          <div>Modal Content</div>
        </Modal>
      );

      await waitFor(() => {
        expect(triggerButton).toHaveFocus();
      });

      document.body.removeChild(triggerButton);
    });

    it("prevents body scroll when open", () => {
      const { rerender } = render(<Modal open={false}>Content</Modal>);

      expect(document.body).not.toHaveStyle("overflow: hidden");

      rerender(<Modal open={true}>Content</Modal>);

      expect(document.body).toHaveStyle("overflow: hidden");
    });
  });

  // Edge cases
  describe("Edge cases", () => {
    it("forwards ref correctly", () => {
      const ref = React.createRef<HTMLDivElement>();
      render(
        <Modal open={true} ref={ref}>
          Content
        </Modal>
      );
      expect(ref.current).toBeInstanceOf(HTMLDivElement);
    });
  });
});

describe("ModalHeader", () => {
  it("renders with children", () => {
    render(
      <Modal open={true}>
        <ModalHeader>Header content</ModalHeader>
      </Modal>
    );
    expect(screen.getByText("Header content")).toBeInTheDocument();
  });

  it("renders with custom className", () => {
    render(
      <Modal open={true}>
        <ModalHeader className="custom-header">Header</ModalHeader>
      </Modal>
    );
    const header = screen.getByText("Header");
    expect(header).toHaveClass("custom-header");
  });

  it("forwards ref correctly", () => {
    const ref = React.createRef<HTMLDivElement>();
    render(
      <Modal open={true}>
        <ModalHeader ref={ref}>Header</ModalHeader>
      </Modal>
    );
    expect(ref.current).toBeInstanceOf(HTMLDivElement);
  });
});

describe("ModalTitle", () => {
  it("renders with children", () => {
    render(
      <Modal open={true}>
        <ModalTitle>Title content</ModalTitle>
      </Modal>
    );
    expect(screen.getByText("Title content")).toBeInTheDocument();
  });

  it("renders with different heading levels", () => {
    const { rerender } = render(
      <Modal open={true}>
        <ModalTitle as="h1">Title</ModalTitle>
      </Modal>
    );
    let title = screen.getByText("Title");
    expect(title.tagName).toBe("H1");

    rerender(
      <Modal open={true}>
        <ModalTitle as="h2">Title</ModalTitle>
      </Modal>
    );
    title = screen.getByText("Title");
    expect(title.tagName).toBe("H2");
  });

  it("defaults to h2", () => {
    render(
      <Modal open={true}>
        <ModalTitle>Default Title</ModalTitle>
      </Modal>
    );
    const title = screen.getByText("Default Title");
    expect(title.tagName).toBe("H2");
  });

  it("forwards ref correctly", () => {
    const ref = React.createRef<HTMLHeadingElement>();
    render(
      <Modal open={true}>
        <ModalTitle ref={ref}>Title</ModalTitle>
      </Modal>
    );
    expect(ref.current).toBeInstanceOf(HTMLHeadingElement);
  });
});

describe("ModalContent", () => {
  it("renders with children", () => {
    render(
      <Modal open={true}>
        <ModalContent>Content here</ModalContent>
      </Modal>
    );
    expect(screen.getByText("Content here")).toBeInTheDocument();
  });

  it("renders with custom className", () => {
    render(
      <Modal open={true}>
        <ModalContent className="custom-content">Content</ModalContent>
      </Modal>
    );
    const content = screen.getByText("Content");
    expect(content).toHaveClass("custom-content");
  });

  it("forwards ref correctly", () => {
    const ref = React.createRef<HTMLDivElement>();
    render(
      <Modal open={true}>
        <ModalContent ref={ref}>Content</ModalContent>
      </Modal>
    );
    expect(ref.current).toBeInstanceOf(HTMLDivElement);
  });
});

describe("ModalFooter", () => {
  it("renders with children", () => {
    render(
      <Modal open={true}>
        <ModalFooter>Footer content</ModalFooter>
      </Modal>
    );
    expect(screen.getByText("Footer content")).toBeInTheDocument();
  });

  it("handles different justify options", () => {
    const { rerender } = render(
      <Modal open={true}>
        <ModalFooter justify="start">Start</ModalFooter>
      </Modal>
    );
    let footer = screen.getByText("Start");
    expect(footer).toHaveClass("justify-start");

    rerender(
      <Modal open={true}>
        <ModalFooter justify="center">Center</ModalFooter>
      </Modal>
    );
    footer = screen.getByText("Center");
    expect(footer).toHaveClass("justify-center");

    rerender(
      <Modal open={true}>
        <ModalFooter justify="end">End</ModalFooter>
      </Modal>
    );
    footer = screen.getByText("End");
    expect(footer).toHaveClass("justify-end");

    rerender(
      <Modal open={true}>
        <ModalFooter justify="between">Between</ModalFooter>
      </Modal>
    );
    footer = screen.getByText("Between");
    expect(footer).toHaveClass("justify-between");
  });

  it("forwards ref correctly", () => {
    const ref = React.createRef<HTMLDivElement>();
    render(
      <Modal open={true}>
        <ModalFooter ref={ref}>Footer</ModalFooter>
      </Modal>
    );
    expect(ref.current).toBeInstanceOf(HTMLDivElement);
  });
});
