import { useEffect, useMemo, useRef, useState } from "react";

import type { IShareData } from "@ax/api";
import { shareToken as shareTokenApi } from "@ax/api";
import { BrowserContent, Icon, Loading, OcassionalToast, Select, SharePageModal, Toast, Tooltip } from "@ax/components";
import { findByEditorID } from "@ax/forms";
import { DEV_NOW, getShareTokenInfo } from "@ax/helpers";
import { useModal, useOnMessageReceivedFromIframe, useToast } from "@ax/hooks";
import type { HeadingFilter } from "@ax/types";

import { calcAutoZoom, calcDefaultResolution } from "./utils";

import * as S from "./style";

const DEFAULT_RESOLUTION = "1280px";

const resolutionOptions = [
	{ value: "1920px", label: "1920px" },
	{ value: "1440px", label: "1440px" },
	{ value: "1280px", label: "1280px" },
	{ value: "1024px", label: "1024px" },
	{ value: "768px", label: "768px" },
	{ value: "360px", label: "360px" },
];

const zoomOptions = [
	{ value: "reset", label: "Reset" },
	{ value: "100", label: "100%" },
	{ value: "75", label: "75%" },
	{ value: "50", label: "50%" },
	{ value: "25", label: "25%" },
];

const Browser = (props: IBrowserProps): JSX.Element => {
	const {
		url,
		content,
		header,
		footer,
		isPage,
		theme,
		socials,
		cloudinaryName,
		siteLangs,
		disabled,
		siteID,
		isPreview,
		showIframe,
		browserRef,
		actions,
		editorType = "page",
		headingFilter,
		keywordsFilter,
		toggleHeadingsPreview,
		toggleKeywordsPreview,
	} = props;

	const { id, entity, haveDraftPage } = content;
	const domain = window.location.origin;
	const headingFilterParam = !headingFilter || headingFilter === "all" ? "" : `&headingFilter=${headingFilter}`;
	const keywordsFilterParam = !keywordsFilter || !keywordsFilter.length ? "" : `&keywordFilter=${keywordsFilter}`;
	const isPageEditor = editorType === "page";
	const isFormEditor = editorType === "form";
	const isHeadingsEditor = editorType === "headings";
	const isKeywordsEditor = editorType === "keywords";
	const isZoomEditor = isPageEditor || isHeadingsEditor || isKeywordsEditor;

	const [isIframeLoading, setIsIframeLoading] = useState(!!showIframe);
	const [previewResolution, setPreviewResolution] = useState("desktop");
	const [dimensions, setDimensions] = useState({ resolution: DEFAULT_RESOLUTION, zoom: "100" });
	const frameWrapperRef = useRef<HTMLDivElement>(null);

	const urlPreview = `${domain}/editor/page-preview?preview=${!!isPreview}&disabled=${!!disabled}&type=${editorType}${headingFilterParam}${keywordsFilterParam}`;
	const { isVisible, toggleToast, setIsVisible, state: toastState } = useToast();
	const { isOpen: isShareOpen, toggleModal: toggleSharePageModal } = useModal(false, true);
	const [shareData, setShareData] = useState<IShareData | null>(null);

	const tokenInfo = shareData ? getShareTokenInfo(shareData, DEV_NOW) : null;

	useOnMessageReceivedFromIframe(actions);

	useEffect(() => {
		localStorage.setItem("selectedID", "0");
		(window as any).browserRef = null;

		if (!isZoomEditor) return;

		const el = frameWrapperRef.current;
		if (!el) return;

		const getScrollbarWidth = () => {
			return window.innerWidth - document.documentElement.clientWidth || 15;
		};

		let lastWidth = 0;
		const scrollbarThreshold = getScrollbarWidth();

		const observer = new ResizeObserver(([entry]) => {
			const containerWidth = entry.contentRect.width;
			if (containerWidth > 0 && Math.abs(containerWidth - lastWidth) > scrollbarThreshold) {
				lastWidth = containerWidth;
				const resolution = calcDefaultResolution(containerWidth, resolutionOptions);
				const newZoom = calcAutoZoom(containerWidth, resolution);
				setDimensions({ resolution, zoom: newZoom });
			}
		});

		observer.observe(el);

		return () => observer.disconnect();
	}, [isZoomEditor]);

	// Fetch share data when in preview mode
	useEffect(() => {
		if (isPreview && id) {
			const fetchShare = async () => {
				const response = await shareTokenApi.getShare(id);
				if (response.status === 200) {
					setShareData(response.data);
				}
			};
			fetchShare();
		}
	}, [isPreview, id]);

	const selectEditorID = (
		selectedComponent: { editorID: number; component: any; type: string; parentEditorID: number },
		_parentComponent: string | undefined | null,
		e: React.SyntheticEvent,
	) => {
		const { element } = findByEditorID(content, selectedComponent.parentEditorID);
		element && e.stopPropagation();

		const { editorID } = selectedComponent;

		const isNavigationModule = ["header", "footer"].includes(selectedComponent.type);

		if (!isPreview && (!disabled || isNavigationModule)) {
			actions?.setSelectedContentAction(editorID);
		}
	};

	const getWidth = (res: string) => {
		if (!isPreview) return dimensions.resolution;

		switch (res) {
			case "tablet":
				return "768px";
			case "phone":
				return "425px";
			default:
				return "100%";
		}
	};

	const moduleActions = useMemo(
		() => ({
			deleteModuleAction: (editorID: number) => {
				actions?.setSelectedContentAction(0);
				actions?.deleteModuleAction?.([editorID]);
			},
			duplicateModuleAction: (editorID: number) => {
				const duplicatedEditorID = actions?.duplicateModuleAction?.([editorID]);
				duplicatedEditorID && actions?.setSelectedContentAction(duplicatedEditorID);
			},
			copyModuleAction: (editorID: number) => {
				const isCopied = actions?.copyModuleAction?.([editorID]);
				isCopied && toggleToast("1 module copied to clipboard");
			},
		}),
		[actions, toggleToast],
	);

	const maxFittingZoom = parseInt(calcAutoZoom(frameWrapperRef.current?.clientWidth ?? 0, dimensions.resolution));
	const zoomOptionsWithDisabled = useMemo(() => {
		const isCurrentZoomInOptions = zoomOptions.some((opt) => opt.value === dimensions.zoom);
		return [
			...(!isCurrentZoomInOptions ? [{ value: dimensions.zoom, label: `${dimensions.zoom}%` }] : []),
			...zoomOptions.map((option) => ({
				...option,
				isDisabled: option.value === "reset" ? !isCurrentZoomInOptions : parseInt(option.value) > maxFittingZoom,
			})),
		];
	}, [dimensions.zoom, maxFittingZoom]);

	const handleResolutionChange = (resolution: string) => {
		const containerWidth = frameWrapperRef.current?.clientWidth ?? 0;
		setDimensions({ resolution, zoom: calcAutoZoom(containerWidth, resolution) });
	};

	const handleZoomChange = (zoom: string) => {
		if (zoom === "reset") {
			const containerWidth = frameWrapperRef.current?.clientWidth ?? 0;
			setDimensions((prev) => ({ ...prev, zoom: calcAutoZoom(containerWidth, prev.resolution) }));
		} else {
			setDimensions((prev) => ({ ...prev, zoom }));
		}
	};

	const scaledWidth = isZoomEditor
		? Math.floor(parseInt(dimensions.resolution) * (parseInt(dimensions.zoom) / 100))
		: undefined;

	const isCompact = scaledWidth !== undefined && scaledWidth <= 400;
	const floatingNoteConfig = isHeadingsEditor
		? { label: "Headings", onClick: toggleHeadingsPreview }
		: isKeywordsEditor
			? { label: "Keywords", onClick: toggleKeywordsPreview }
			: null;

	return (
		<S.OuterContainer ref={frameWrapperRef}>
			<S.BrowserWrapper
				data-testid="browser-wrapper"
				ref={browserRef}
				scaledWidth={scaledWidth}
				isPreview={!isZoomEditor}
			>
				{isZoomEditor && (
					<S.NavBar>
						<S.NavUrl>{url}</S.NavUrl>
						{isPreview ? (
							<S.NavActions data-testid="nav-actions-wrapper">
								<S.IconWrapper
									data-testid="icon-wrapper-browser"
									onClick={haveDraftPage ? undefined : toggleSharePageModal}
									active={!haveDraftPage}
								>
									<Tooltip
										hideOnClick={!haveDraftPage}
										content={
											haveDraftPage ? "Only available for drafts. This page already has a public URL." : "Share draft"
										}
										bottom
									>
										<Icon name="share" size="24" />
									</Tooltip>
								</S.IconWrapper>
								<S.IconWrapper active={previewResolution === "desktop"} onClick={() => setPreviewResolution("desktop")}>
									<Tooltip content="Desktop" bottom>
										<Icon name="desktop" size="24" />
									</Tooltip>
								</S.IconWrapper>
								<S.IconWrapper
									data-testid="icon-res-tablet"
									active={previewResolution === "tablet"}
									onClick={() => setPreviewResolution("tablet")}
								>
									<Tooltip content="Tablet" bottom>
										<Icon name="tablet" size="24" />
									</Tooltip>
								</S.IconWrapper>
								<S.IconWrapper
									data-testid="icon-res-phone"
									active={previewResolution === "phone"}
									onClick={() => setPreviewResolution("phone")}
								>
									<Tooltip content="Mobile" bottom>
										<Icon name="phone" size="24" />
									</Tooltip>
								</S.IconWrapper>
							</S.NavActions>
						) : (
							<>
								{showIframe && (
									<S.NavActions data-testid="nav-actions-wrapper">
										<S.ResolutionWrapper>
											<S.SelectLabel>Screen</S.SelectLabel>
											<Select
												name="resolution"
												options={resolutionOptions}
												value={dimensions.resolution}
												onChange={handleResolutionChange}
												type="round"
												offSet="30%"
												mandatory
											/>
										</S.ResolutionWrapper>
										<S.ZoomWrapper>
											<Select
												name="zoom"
												options={zoomOptionsWithDisabled}
												value={dimensions.zoom}
												onChange={handleZoomChange}
												type="round"
												mandatory
											/>
										</S.ZoomWrapper>
									</S.NavActions>
								)}
							</>
						)}
					</S.NavBar>
				)}
				{showIframe ? (
					<S.ContentWrapper>
						{floatingNoteConfig && (
							<S.StyledFloatingNote
								message={`You are viewing the ${floatingNoteConfig.label} editor${isCompact ? "" : " of this page"}`}
								btnText={isCompact ? "Back" : "Back to Page Editor"}
								onClick={floatingNoteConfig.onClick}
								$compact={isCompact}
							/>
						)}
						<S.FrameWrapper hasBorder={isZoomEditor} isFormEditor={isFormEditor} data-testid="navbar-iframe-wrapper">
							{isIframeLoading && (
								<S.FrameLoading>
									<Loading />
								</S.FrameLoading>
							)}
							{isPreview ? (
								<iframe
									title="Preview"
									width={getWidth(previewResolution)}
									height="100%"
									src={urlPreview}
									loading="lazy"
									className="frame-content"
									onLoad={() => setIsIframeLoading(false)}
									style={{ visibility: isIframeLoading ? "hidden" : "visible" }}
								/>
							) : (
								<div
									style={{
										width: scaledWidth !== undefined ? `${scaledWidth}px` : "100%",
										height: "100%",
										overflow: "hidden",
										flexShrink: isZoomEditor ? 0 : "unset",
									}}
								>
									<iframe
										title="Preview"
										width={isZoomEditor ? dimensions.resolution : "100%"}
										src={urlPreview}
										loading="lazy"
										className="frame-content"
										onLoad={() => setIsIframeLoading(false)}
										style={{
											display: "block",
											transform: isZoomEditor ? `scale(${parseInt(dimensions.zoom) / 100})` : "scale(1)",
											transformOrigin: "0 0",
											height: isZoomEditor ? `${Math.round(100 / (parseInt(dimensions.zoom) / 100))}%` : "100%",
											visibility: isIframeLoading ? "hidden" : "visible",
										}}
									/>
								</div>
							)}
						</S.FrameWrapper>
					</S.ContentWrapper>
				) : (
					<S.Wrapper data-testid="browser-content-wrapper" className="browser-content">
						<BrowserContent
							cloudinaryName={cloudinaryName}
							theme={theme}
							socials={socials}
							siteLangs={siteLangs}
							selectEditorID={selectEditorID}
							siteID={siteID}
							isPage={isPage}
							content={content}
							header={header}
							footer={footer}
							languageID={content.language}
							pageLanguages={content.pageLanguages}
							moduleActions={moduleActions}
							renderer="editor"
						/>
					</S.Wrapper>
				)}
				{isVisible && <Toast message={toastState} setIsVisible={setIsVisible} />}
				{isPreview && shareData && tokenInfo && (
					<OcassionalToast
						message={
							<>
								{tokenInfo.tokenHasExpired
									? `Access to this draft link expired on ${tokenInfo.tokenExpirationDate}. You need to generate a `
									: `This draft link expires on ${tokenInfo.tokenExpirationDate}. ${tokenInfo.tokenCanBeRenewed ? "Renew it" : "Open details"}${" "}`}
								<button type="button" onClick={toggleSharePageModal}>
									<strong style={{ textDecoration: "underline" }}>
										{tokenInfo.tokenHasExpired ? "new one" : "here"}
									</strong>
								</button>
							</>
						}
						icon={tokenInfo.tokenHasExpired ? "hide" : "view"}
					/>
				)}

				{isPreview && (
					<SharePageModal
						pageTitle={content.title}
						isOpen={isShareOpen}
						hide={toggleSharePageModal}
						pageID={id}
						entity={entity}
						shareData={shareData}
						onShareChange={setShareData}
					/>
				)}
			</S.BrowserWrapper>
		</S.OuterContainer>
	);
};

export interface IBrowserProps {
	content: any;
	header?: any;
	footer?: any;
	url: string;
	isPage: boolean;
	theme: string;
	socials: any;
	cloudinaryName: string | null;
	siteLangs: any[];
	disabled?: boolean;
	siteID?: number;
	isPreview?: boolean;
	showIframe?: boolean;
	browserRef?: React.RefObject<HTMLDivElement>;
	editorType?: "form" | "page" | "headings" | "keywords";
	headingFilter?: HeadingFilter;
	keywordsFilter?: string[];
	toggleHeadingsPreview?(): void;
	toggleKeywordsPreview?(): void;
	actions?: {
		setSelectedContentAction(editorID: number): void;
		deleteModuleAction?(editorID: number[]): void;
		duplicateModuleAction?(editorID: number[]): number;
		copyModuleAction?(editorID: number[]): number | boolean;
		setScrollEditorIDAction?(editorID: number | null): void;
	};
}

export default Browser;
