import React from "react";
import { Modal } from "./index";

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

import "@testing-library/jest-dom";

// Mock react-modal
jest.mock("react-modal", () => {
  const MockReactModal = ({
    isOpen,
    children,
    className,
    overlayClassName,
    onRequestClose,
    parentSelector,
  }: any) => {
    // Call parentSelector to cover the arrow function
    if (parentSelector) parentSelector();
    if (!isOpen) return null;
    return (
      <div data-testid="react-modal" className={className}>
        <div
          data-testid="modal-overlay"
          className={overlayClassName}
          onClick={onRequestClose}
        />
        {children}
      </div>
    );
  };
  MockReactModal.setAppElement = jest.fn();
  return MockReactModal;
});

// Allow overriding the transition state for specific tests
let mockTransitionState: string | null = null;
const setMockTransitionState = (state: string | null) => {
  mockTransitionState = state;
};

// Mock react-transition-group
jest.mock("react-transition-group", () => ({
  Transition: ({ children, in: inProp }: any) => {
    const state = mockTransitionState ?? (inProp ? "entered" : "exited");
    return children(state);
  },
}));

// Mock useBodyScrollLock
jest.mock("../../hooks/use-body-scroll-lock", () => jest.fn());

describe("Modal", () => {
  const defaultProps = {
    isOpen: true,
    onRequestClose: jest.fn(),
  };

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe("Rendering", () => {
    it("renders when isOpen is true", () => {
      render(<Modal {...defaultProps}>Content</Modal>);
      expect(screen.getByTestId("react-modal")).toBeInTheDocument();
      expect(screen.getByText("Content")).toBeInTheDocument();
    });

    it("does not render when isOpen is false", () => {
      render(
        <Modal {...defaultProps} isOpen={false}>
          Content
        </Modal>
      );
      expect(screen.queryByTestId("react-modal")).not.toBeInTheDocument();
    });

    it("has correct displayName", () => {
      expect(Modal.displayName).toBe("Modal");
    });

    it("renders children in body div", () => {
      render(<Modal {...defaultProps}>Hello World</Modal>);
      expect(screen.getByText("Hello World")).toBeInTheDocument();
    });
  });

  describe("Title", () => {
    it("renders title when provided", () => {
      render(<Modal {...defaultProps} title="My Modal" />);
      expect(screen.getByText("My Modal")).toBeInTheDocument();
    });

    it("does not render title section when not provided", () => {
      const { container } = render(<Modal {...defaultProps} />);
      expect(container.querySelector(".border-b")).not.toBeInTheDocument();
    });
  });

  describe("Close button", () => {
    it("renders close button by default", () => {
      render(<Modal {...defaultProps} />);
      expect(screen.getByTestId("close-button")).toBeInTheDocument();
    });

    it("hides close button when hideCloseButton is true", () => {
      render(<Modal {...defaultProps} hideCloseButton={true} />);
      expect(screen.queryByTestId("close-button")).not.toBeInTheDocument();
    });

    it("calls onRequestClose when close button is clicked", () => {
      const onRequestClose = jest.fn();
      render(<Modal {...defaultProps} onRequestClose={onRequestClose} />);
      fireEvent.click(screen.getByTestId("close-button"));
      expect(onRequestClose).toHaveBeenCalledTimes(1);
    });

    it("renders close icon in close button", () => {
      render(<Modal {...defaultProps} />);
      expect(screen.getByText("close")).toBeInTheDocument();
    });
  });

  describe("Size", () => {
    it("applies md size classes by default", () => {
      render(<Modal {...defaultProps} />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("inset-[15%]");
    });

    it("applies xl size classes", () => {
      render(<Modal {...defaultProps} size="xl" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("inset-[5%]");
    });

    it("applies lg size classes", () => {
      render(<Modal {...defaultProps} size="lg" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("inset-[10%]");
    });

    it("applies sm size classes", () => {
      render(<Modal {...defaultProps} size="sm" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("w-[640px]");
    });

    it("applies xs size classes", () => {
      render(<Modal {...defaultProps} size="xs" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("w-[calc(100%-30px)]");
    });
  });

  describe("Shape", () => {
    it("applies default shape", () => {
      render(<Modal {...defaultProps} />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).not.toContain("rounded-3xl");
    });

    it("applies rounded shape", () => {
      render(<Modal {...defaultProps} shape="rounded" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("rounded-3xl");
    });
  });

  describe("Centered", () => {
    it("applies centered classes", () => {
      render(<Modal {...defaultProps} centered={true} />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("top-0");
    });
  });

  describe("CSS classes", () => {
    it("applies custom className", () => {
      render(<Modal {...defaultProps} className="custom-modal" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("custom-modal");
    });

    it("applies bodyClassName to body div", () => {
      render(<Modal {...defaultProps} bodyClassName="custom-body" />);
      const body = screen
        .getByTestId("react-modal")
        .querySelector(".custom-body");
      expect(body).toBeInTheDocument();
    });

    it("applies closeButtonClassName", () => {
      render(<Modal {...defaultProps} closeButtonClassName="custom-close" />);
      const closeBtn = screen.getByTestId("close-button");
      expect(closeBtn.className).toContain("custom-close");
    });
  });

  describe("Body style", () => {
    it("applies bodyStyle to body div", () => {
      render(
        <Modal {...defaultProps} bodyStyle={{ padding: "20px" }}>
          Content
        </Modal>
      );
      const body = screen.getByText("Content");
      // Body content should be present inside the modal
      expect(body).toBeInTheDocument();
    });
  });

  describe("Animation", () => {
    it("applies popper animation classes by default", () => {
      render(<Modal {...defaultProps} />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("transition-transform");
    });

    it("applies bottomSheet animation classes", () => {
      render(<Modal {...defaultProps} animation="bottomSheet" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("ease-in-out");
    });

    it("applies exited state classes for popper when not transitioning", () => {
      // Since our mock always returns 'entered' state when in=true,
      // we can verify it includes entered classes
      render(<Modal {...defaultProps} />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("pointer-events-auto");
    });

    it("returns default popper classes for unknown transition state", () => {
      setMockTransitionState("unmounted");
      render(<Modal {...defaultProps} animation="popper" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("opacity-0");
      expect(modal.className).toContain("translate-y-[-18px]");
      setMockTransitionState(null);
    });

    it("returns default bottomSheet classes for unknown transition state", () => {
      setMockTransitionState("unmounted");
      render(<Modal {...defaultProps} animation="bottomSheet" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("opacity-0");
      expect(modal.className).toContain("translate-y-full");
      setMockTransitionState(null);
    });
  });

  describe("Data attributes", () => {
    it("passes data-testid to body div", () => {
      render(<Modal {...defaultProps} data-testid="modal-body" />);
      const elements = screen.getAllByTestId("modal-body");
      expect(elements.length).toBeGreaterThan(0);
    });

    it("supports parentSelector prop", () => {
      // Add a main element to the document
      const main = document.createElement("main");
      document.body.appendChild(main);
      render(
        <Modal
          {...defaultProps}
          parentSelector={"main" as unknown as string & (() => HTMLElement)}
        />
      );
      expect(screen.getByTestId("react-modal")).toBeInTheDocument();
      document.body.removeChild(main);
    });

    it("falls back to document.body when parentSelector element not found", () => {
      render(
        <Modal
          {...defaultProps}
          parentSelector={
            ".nonexistent" as unknown as string & (() => HTMLElement)
          }
        />
      );
      expect(screen.getByTestId("react-modal")).toBeInTheDocument();
    });

    it("applies closeWrapperClassName", () => {
      const { container } = render(
        <Modal {...defaultProps} closeWrapperClassName="wrapper-class" />
      );
      const wrapper = container.querySelector(".wrapper-class");
      expect(wrapper).toBeInTheDocument();
    });

    it("applies bottomSheet animation classes", () => {
      render(<Modal {...defaultProps} animation="bottomSheet" />);
      const modal = screen.getByTestId("react-modal");
      expect(modal.className).toContain("translate-y-0");
      expect(modal.className).toContain("pointer-events-auto");
    });

    it("returns empty animation classes for unknown animation type", () => {
      render(<Modal {...defaultProps} animation={"unknown" as any} />);
      const modal = screen.getByTestId("react-modal");
      // No animation-specific classes, just the base classes
      expect(modal.className).not.toContain("translate-y");
      expect(modal.className).not.toContain("opacity-0");
    });
  });
});
