import React from "react";

import { ThemeProvider } from "styled-components";
import { render, cleanup, screen, fireEvent } from "@testing-library/react";
import { parseTheme } from "@ax/helpers";
import "@testing-library/jest-dom";

import type { HeadingNode } from "@ax/types";
import HeadingsPreviewModal from "@ax/components/HeadingsPreviewModal";
import * as headingsUtils from "@ax/components/HeadingsPreviewModal/utils";
import globalTheme from "@ax/themes/theme.json";

jest.mock("@ax/components/HeadingsPreviewModal/utils", () => ({
	...jest.requireActual("@ax/components/HeadingsPreviewModal/utils"),
	parseHeadingsTree: jest.fn().mockReturnValue([]),
}));

afterEach(cleanup);

const createHeadingNode = (
	tag: HeadingNode["tag"],
	text: string,
	level: HeadingNode["level"],
	children: HeadingNode[] = [],
): HeadingNode => ({
	tag,
	text,
	level,
	isHidden: false,
	children,
});

const mockHeadings: HeadingNode[] = [
	createHeadingNode("h1", "Main Title", 1, [createHeadingNode("h2", "Subtitle", 2)]),
];

const browserRef = { current: document.createElement("div") } as React.RefObject<HTMLDivElement>;

const defaultProps = {
	isOpen: false,
	browserRef: { current: null } as React.RefObject<HTMLDivElement>,
	toggleModal: jest.fn(),
	headingsFilter: "all" as const,
	setHeadingsFilter: jest.fn(),
};

const renderComponent = (props = defaultProps) =>
	render(
		<ThemeProvider theme={parseTheme(globalTheme)}>
			<HeadingsPreviewModal {...props} />
		</ThemeProvider>,
	);

describe("HeadingsPreviewModal component rendering", () => {
	it("should not render the headings content when closed", () => {
		renderComponent();

		expect(screen.queryByText("Show headings:")).toBeNull();
		expect(screen.queryByTestId("wrapper")).toBeNull();
	});

	it("should render the empty state when open with no headings", () => {
		renderComponent({ ...defaultProps, isOpen: true });

		const floatingPanel = screen.getByTestId("floating-panel");
		expect(floatingPanel).toBeTruthy();

		const emptyState = screen.getByTestId("wrapper");
		expect(emptyState).toBeTruthy();
		expect(emptyState).toHaveTextContent("There are no headings available to display");
	});

	it("should render heading items when open with headings", () => {
		(headingsUtils.parseHeadingsTree as jest.Mock).mockReturnValueOnce(mockHeadings);

		renderComponent({ ...defaultProps, isOpen: true, browserRef });

		const floatingPanel = screen.getByTestId("floating-panel");
		expect(floatingPanel).toBeTruthy();

		const allButtons = screen.getAllByRole("button");
		const headingItems = allButtons.filter((el) => el.hasAttribute("data-heading-id"));
		expect(headingItems.length).toBeGreaterThan(0);
	});

	it("should render filter buttons when open with headings", () => {
		(headingsUtils.parseHeadingsTree as jest.Mock).mockReturnValueOnce(mockHeadings);

		renderComponent({ ...defaultProps, isOpen: true, browserRef });

		const filterText = screen.getByText("Show headings:");
		expect(filterText).toBeTruthy();

		const allFilter = screen.getByText("all");
		expect(allFilter).toBeTruthy();

		const h1Texts = screen.getAllByText("h1");
		const h1FilterButton = h1Texts.find((el) => el.tagName === "BUTTON");
		expect(h1FilterButton).toBeTruthy();

		const h2Texts = screen.getAllByText("h2");
		const h2FilterButton = h2Texts.find((el) => el.tagName === "BUTTON");
		expect(h2FilterButton).toBeTruthy();
	});

	it("should render the errors banner when there are SEO errors", () => {
		const headingsWithoutH1: HeadingNode[] = [createHeadingNode("h2", "Subtitle", 2)];
		(headingsUtils.parseHeadingsTree as jest.Mock).mockReturnValueOnce(headingsWithoutH1);

		renderComponent({ ...defaultProps, isOpen: true, browserRef });

		const seoAlerts = screen.getByText("SEO Alerts");
		expect(seoAlerts).toBeTruthy();
	});

	it("should not render the errors banner when there are no SEO errors", () => {
		(headingsUtils.parseHeadingsTree as jest.Mock).mockReturnValueOnce(mockHeadings);

		renderComponent({ ...defaultProps, isOpen: true, browserRef });

		const seoAlerts = screen.queryByText("SEO Alerts");
		expect(seoAlerts).toBeNull();
	});
});

describe("HeadingsPreviewModal component events", () => {
	it("should call toggleModal when close button is clicked", () => {
		const toggleModalMock = jest.fn();

		renderComponent({ ...defaultProps, isOpen: true, toggleModal: toggleModalMock });

		const closeButton = screen.getByTestId("icon-action-component");
		fireEvent.click(closeButton);
		expect(toggleModalMock).toBeCalled();
	});

	it("should call setHeadingsFilter when a filter button is clicked", () => {
		const setHeadingsFilterMock = jest.fn();
		(headingsUtils.parseHeadingsTree as jest.Mock).mockReturnValueOnce(mockHeadings);

		renderComponent({ ...defaultProps, isOpen: true, browserRef, setHeadingsFilter: setHeadingsFilterMock });

		const h1Texts = screen.getAllByText("h1");
		const h1FilterButton = h1Texts.find((el) => el.tagName === "BUTTON") as HTMLElement;
		expect(h1FilterButton).toBeTruthy();
		fireEvent.click(h1FilterButton);
		expect(setHeadingsFilterMock).toBeCalledWith("h1");
	});

	it("should select a heading item when clicked", () => {
		(headingsUtils.parseHeadingsTree as jest.Mock).mockReturnValueOnce(mockHeadings);

		renderComponent({ ...defaultProps, isOpen: true, browserRef });

		const allButtons = screen.getAllByRole("button");
		const headingItems = allButtons.filter((el) => el.hasAttribute("data-heading-id"));
		expect(headingItems.length).toBeGreaterThan(0);

		fireEvent.click(headingItems[0]);

		expect(headingItems[0]).toBeTruthy();
	});

	it("should deselect a heading item when clicked again", () => {
		(headingsUtils.parseHeadingsTree as jest.Mock).mockReturnValueOnce(mockHeadings);

		renderComponent({ ...defaultProps, isOpen: true, browserRef });

		const allButtons = screen.getAllByRole("button");
		const headingItems = allButtons.filter((el) => el.hasAttribute("data-heading-id"));
		expect(headingItems.length).toBeGreaterThan(0);

		fireEvent.click(headingItems[0]);
		fireEvent.click(headingItems[0]);

		expect(headingItems[0]).toBeTruthy();
	});
});
