import { MemoryRouter } from "react-router-dom";

import { act, cleanup, fireEvent, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { mock } from "jest-mock-extended";
import { ThemeProvider } from "styled-components";
import "@testing-library/jest-dom";

import Browser, { type IBrowserProps } from "@ax/components/Browser";
import { parseTheme } from "@ax/helpers";
import globalTheme from "@ax/themes/theme.json";

let defaultProps: IBrowserProps;

const renderBrowser = (props: IBrowserProps) =>
	render(
		<MemoryRouter>
			<ThemeProvider theme={parseTheme(globalTheme)}>
				<Browser {...props} />
			</ThemeProvider>
		</MemoryRouter>,
	);

beforeEach(() => {
	global.ResizeObserver = jest.fn().mockImplementation(() => ({
		observe: jest.fn(),
		unobserve: jest.fn(),
		disconnect: jest.fn(),
	}));

	defaultProps = mock<IBrowserProps>();
	defaultProps.content = {};
	defaultProps.browserRef = { current: null };
	defaultProps.cloudinaryName = "thesaurus-cms";
	defaultProps.disabled = false;
	defaultProps.footer = {};
	defaultProps.header = {};
	defaultProps.isPage = true;
	defaultProps.siteID = 108;
	defaultProps.siteLangs = [{}];
	defaultProps.socials = {
		facebook: "https://www.facebook.com/SlothSanctuaryCostaRica",
		instagram: "https://www.instagram.com/sloth.sanctuary.costa.rica/",
	};
	defaultProps.theme = "default-theme";
	defaultProps.url = "/gallery-stuff";
});

afterEach(() => {
	jest.resetAllMocks();
	cleanup();
});

describe("Browser component rendering", () => {
	it("should render the component", () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.getByTestId("nav-actions-wrapper")).toBeInTheDocument();
		expect(screen.getByTestId("browser-wrapper")).toBeInTheDocument();
		expect(screen.getByTestId("navbar-iframe-wrapper")).toBeInTheDocument();
		expect(screen.queryByTestId("browser-content-wrapper")).not.toBeInTheDocument();
	});

	it("should render the browserContent", () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = false;

		renderBrowser(defaultProps);

		expect(screen.queryByTestId("nav-actions-wrapper")).not.toBeInTheDocument();
		expect(screen.queryByTestId("navbar-iframe-wrapper")).not.toBeInTheDocument();
		expect(screen.getByTestId("browser-content-wrapper")).toBeInTheDocument();
	});

	it("should change the iFrame width when changing the resolution", () => {
		defaultProps.isPreview = true;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.getByTestId("nav-actions-wrapper")).toBeInTheDocument();
		expect(screen.getByTestId("navbar-iframe-wrapper")).toBeInTheDocument();
		expect(screen.queryByTestId("browser-content-wrapper")).not.toBeInTheDocument();

		const iframe = document.querySelector("iframe");
		expect(iframe).toBeInTheDocument();
		expect(iframe?.width).toBe("100%");

		fireEvent.click(screen.getByTestId("icon-res-tablet"));
		expect(iframe?.width).toBe("768px");

		fireEvent.click(screen.getByTestId("icon-res-phone"));
		expect(iframe?.width).toBe("425px");
	});

	it("should display the url in the navbar", () => {
		renderBrowser(defaultProps);

		expect(screen.getByText("/gallery-stuff")).toBeInTheDocument();
	});

	it("should reset localStorage selectedID to '0' on mount", () => {
		localStorage.setItem("selectedID", "42");

		renderBrowser(defaultProps);

		expect(localStorage.getItem("selectedID")).toBe("0");
	});

	it("should set iframe width to the current resolution in non-preview mode", () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		const iframe = document.querySelector("iframe");
		expect(iframe?.width).toBe("1280px");
	});
});

describe("Browser editorType variants", () => {
	it("should not render the navbar when editorType is form", () => {
		defaultProps.editorType = "form";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.queryByTestId("nav-actions-wrapper")).not.toBeInTheDocument();
		expect(screen.getByTestId("navbar-iframe-wrapper")).toBeInTheDocument();
	});

	it("should show the headings editor banner when editorType is headings", () => {
		defaultProps.editorType = "headings";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.getByTestId("floating-note-wrapper")).toBeInTheDocument();
		expect(screen.getByTestId("floating-note-message")).toHaveTextContent(
			"You are viewing the Headings editor of this page",
		);
	});

	it("should call toggleHeadingsPreview when clicking Back to Page Editor in headings mode", () => {
		defaultProps.editorType = "headings";
		defaultProps.showIframe = true;
		const toggleHeadingsPreviewMock = jest.fn();
		defaultProps.toggleHeadingsPreview = toggleHeadingsPreviewMock;

		renderBrowser(defaultProps);

		fireEvent.click(screen.getByRole("button", { name: "Back to Page Editor" }));

		expect(toggleHeadingsPreviewMock).toHaveBeenCalledTimes(1);
	});

	it("should show the keywords editor banner when editorType is keywords", () => {
		defaultProps.editorType = "keywords";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.getByTestId("floating-note-wrapper")).toBeInTheDocument();
		expect(screen.getByTestId("floating-note-message")).toHaveTextContent(
			"You are viewing the Keywords editor of this page",
		);
	});

	it("should call toggleKeywordsPreview when clicking Back to Page Editor in keywords mode", () => {
		defaultProps.editorType = "keywords";
		defaultProps.showIframe = true;
		const toggleKeywordsPreviewMock = jest.fn();
		defaultProps.toggleKeywordsPreview = toggleKeywordsPreviewMock;

		renderBrowser(defaultProps);

		fireEvent.click(screen.getByRole("button", { name: "Back to Page Editor" }));

		expect(toggleKeywordsPreviewMock).toHaveBeenCalledTimes(1);
	});

	it("should render nav-actions-wrapper for headings editor", () => {
		defaultProps.editorType = "headings";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.getByTestId("nav-actions-wrapper")).toBeInTheDocument();
	});

	it("should render nav-actions-wrapper for keywords editor", () => {
		defaultProps.editorType = "keywords";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.getByTestId("nav-actions-wrapper")).toBeInTheDocument();
	});

	it("should display the url in the headings editor navbar", () => {
		defaultProps.editorType = "headings";

		renderBrowser(defaultProps);

		expect(screen.getByText("/gallery-stuff")).toBeInTheDocument();
	});

	it("should display the url in the keywords editor navbar", () => {
		defaultProps.editorType = "keywords";

		renderBrowser(defaultProps);

		expect(screen.getByText("/gallery-stuff")).toBeInTheDocument();
	});

	it("should not show floating note for form editor", () => {
		defaultProps.editorType = "form";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.queryByTestId("floating-note-wrapper")).not.toBeInTheDocument();
	});
});

describe("Browser iframe URL construction", () => {
	it("should build the correct iframe src URL", () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		const iframe = document.querySelector("iframe");
		expect(iframe?.src).toBe("http://localhost/editor/page-preview?preview=false&disabled=false&type=page");
	});

	it("should include headingFilter param in the URL when a specific filter is set", () => {
		defaultProps.editorType = "headings";
		defaultProps.headingFilter = "h2";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		const iframe = document.querySelector("iframe");
		expect(iframe?.src).toContain("headingFilter=h2");
	});

	it("should not include headingFilter param in the URL when headingFilter is 'all'", () => {
		defaultProps.editorType = "headings";
		defaultProps.headingFilter = "all";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		const iframe = document.querySelector("iframe");
		expect(iframe?.src).not.toContain("headingFilter");
	});

	it("should include keywordFilter param in the URL when keywordsFilter has values", () => {
		defaultProps.editorType = "keywords";
		defaultProps.keywordsFilter = ["seo"];
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		const iframe = document.querySelector("iframe");
		expect(iframe?.src).toContain("keywordFilter=seo");
	});

	it("should not include keywordFilter param in the URL when keywordsFilter is empty", () => {
		defaultProps.editorType = "keywords";
		defaultProps.keywordsFilter = [];
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		const iframe = document.querySelector("iframe");
		expect(iframe?.src).not.toContain("keywordFilter");
	});

	it("should include all keywords in the URL when keywordsFilter has multiple values", () => {
		defaultProps.editorType = "keywords";
		defaultProps.keywordsFilter = ["seo", "cms"];
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		const iframe = document.querySelector("iframe");
		expect(iframe?.src).toContain("keywordFilter=seo,cms");
	});
});

describe("Browser ResizeObserver lifecycle", () => {
	it("should update dimensions based on containerWidth when ResizeObserver fires", () => {
		// Verify ResizeObserver is created and observe is called
		const ResizeObserverConstructor = jest.fn().mockImplementation(() => ({
			observe: jest.fn(),
			unobserve: jest.fn(),
			disconnect: jest.fn(),
		}));
		global.ResizeObserver = ResizeObserverConstructor as any;

		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		// Verify ResizeObserver was created and observe was called
		expect(ResizeObserverConstructor).toHaveBeenCalled();
		expect((global.ResizeObserver as any).mock.results[0].value.observe).toHaveBeenCalled();
	});

	it("should disconnect ResizeObserver on unmount", () => {
		const disconnectMock = jest.fn();
		global.ResizeObserver = jest.fn().mockImplementation(() => ({
			observe: jest.fn(),
			unobserve: jest.fn(),
			disconnect: disconnectMock,
		}));

		defaultProps.showIframe = true;

		const { unmount } = renderBrowser(defaultProps);
		unmount();

		expect(disconnectMock).toHaveBeenCalledTimes(1);
	});

	it("should not update dimensions when ResizeObserver fires with zero width", () => {
		// Verify ResizeObserver is created
		const ResizeObserverConstructor = jest.fn().mockImplementation(() => ({
			observe: jest.fn(),
			unobserve: jest.fn(),
			disconnect: jest.fn(),
		}));
		global.ResizeObserver = ResizeObserverConstructor as any;

		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		// default resolution remains "1280px" - verify initial state
		expect(screen.getByText("1280px")).toBeInTheDocument();
	});
});

describe("Browser compact mode", () => {
	it("should render headings editor with floating note", () => {
		defaultProps.editorType = "headings";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		// Verify the floating note is rendered for headings editor
		expect(screen.getByTestId("floating-note-wrapper")).toBeInTheDocument();
		expect(screen.getByTestId("floating-note-message")).toHaveTextContent("Headings editor");
	});

	it("should render keywords editor with floating note", () => {
		defaultProps.editorType = "keywords";
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		// Verify the floating note is rendered for keywords editor
		expect(screen.getByTestId("floating-note-wrapper")).toBeInTheDocument();
		expect(screen.getByTestId("floating-note-message")).toHaveTextContent("Keywords editor");
	});
});

describe("Browser resolution and zoom selectors", () => {
	it("should render both resolution and zoom select components in non-preview mode", () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		const { container } = renderBrowser(defaultProps);

		expect(container.querySelectorAll('[data-testid="select-component"]')).toHaveLength(2);
	});

	it("should display the default resolution '1280px' in the resolution select", () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.getByText("1280px")).toBeInTheDocument();
	});

	it("should display the default zoom '100%' in the zoom select", () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		expect(screen.getByText("100%")).toBeInTheDocument();
	});

	it("should update the displayed resolution when a new option is selected", async () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		await act(async () => userEvent.click(screen.getByLabelText("resolution")));
		await act(async () => userEvent.click(screen.getByText("1024px")));

		expect(screen.getByText("1024px")).toBeInTheDocument();
	});

	it("should update the displayed zoom when a new option is selected", async () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		await act(async () => userEvent.click(screen.getByLabelText("zoom")));
		await act(async () => userEvent.click(screen.getByText("75%")));

		expect(screen.getByText("75%")).toBeInTheDocument();
	});

	it("should render zoom select component in non-preview mode", () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		// Verify zoom select is rendered
		expect(screen.getByLabelText("zoom")).toBeInTheDocument();
	});

	it("should display Reset option in zoom select", async () => {
		defaultProps.isPreview = false;
		defaultProps.showIframe = true;

		renderBrowser(defaultProps);

		// Open the zoom dropdown
		await act(async () => userEvent.click(screen.getByLabelText("zoom")));

		// Verify Reset option exists
		expect(screen.getByText("Reset")).toBeInTheDocument();
	});
});
