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

import { EmptyState, FloatingPanel, ToggleField } from "@ax/components";
import type { HeadingFilter, HeadingNode } from "@ax/types";

import ErrorsBanner from "./ErrorsBanner";
import HeadingItem from "./HeadingItem";
import { analyzeHeadings, extractUniqueHeadingTypes, filterHeadings, hasHiddenHeadings, parseHeadingsTree } from "./utils";

import * as S from "./style";

const HeadingsPreviewModal = (props: IHeadingsPreviewProps) => {
	const { isOpen, browserRef, headingsFilter, setHeadingsFilter, toggleModal } = props;

	const isFiltering = headingsFilter !== "all";
	const counter = { value: 0 };

	const [headings, setHeadings] = useState<HeadingNode[]>([]);
	const [selected, setSelected] = useState<number | null>(null);
	const [toggleStatus, setToggleStatus] = useState<boolean>(true);
	const [isErrorsBannerOpen, setIsErrorsBannerOpen] = useState(false);
	const [errorsResetKey, setErrorsResetKey] = useState(0);
	const listRef = useRef<HTMLDivElement>(null);

	const filters = useMemo<HeadingFilter[]>(() => ["all", ...extractUniqueHeadingTypes(headings)], [headings]);
	const filteredHeadings = useMemo(() => filterHeadings(headings, headingsFilter, toggleStatus), [headings, headingsFilter, toggleStatus]);
	const showToggleWrapper = useMemo(() => hasHiddenHeadings(headings, headingsFilter), [headings, headingsFilter]);
	const errors = useMemo(() => analyzeHeadings(filteredHeadings, isFiltering), [filteredHeadings, isFiltering]);

	const getSEOHeadings = useCallback(() => {
		return browserRef.current ? parseHeadingsTree(browserRef.current) : [];
	}, [browserRef]);

	useEffect(() => {
		if (isOpen) {
			const headings = getSEOHeadings();
			setHeadings(headings);
		} else {
			setHeadingsFilter("all");
			window.scrollTo(0, 0);
		}
	}, [isOpen, getSEOHeadings, setHeadingsFilter]);

	const closeErrorsBanner = () => {
		setIsErrorsBannerOpen(false);
		setErrorsResetKey((k) => k + 1);
	};

	const handleFilterClick = (value: HeadingFilter) => () => {
		setSelected(null);
		setHeadingsFilter(value);
		closeErrorsBanner();
	};

	const scrollToHeadingInIframe = (id: number) => {
		const iframe = browserRef.current?.querySelector("iframe");
		const iframeDocument = iframe?.contentDocument || iframe?.contentWindow?.document;
		const heading = iframeDocument?.querySelector(`[data-griddoid="heading-${id}"]`);
		heading?.scrollIntoView({ behavior: "smooth", block: "center" });
	};

	const handleHeadingClick = (id: number) => () => {
		if (id !== selected) {
			setSelected(id);
			scrollToHeadingInIframe(id);
		} else {
			setSelected(null);
		}
		closeErrorsBanner();
	};

	const handleHeadingNavigate = (id: number) => () => {
		setSelected(id);
		scrollToHeadingInIframe(id);

		const listItem = listRef.current?.querySelector(`[data-heading-id="${id}"]`);
		listItem?.scrollIntoView({ behavior: "smooth", block: "center" });
	};

	return (
		<S.Wrapper>
			<FloatingPanel title="Headings" toggleModal={toggleModal} closeOnOutsideClick={false} isOpen={isOpen} width={358}>
				{isOpen &&
					(!filteredHeadings.length ? (
						<S.EmptyWrapper>
							<EmptyState
								message={
									<>
										There are no headings available to display.
										<br />
										Feel free to add some to get started!
									</>
								}
							/>
						</S.EmptyWrapper>
					) : (
						<S.HeadingsWrapper>
							{errors.length > 0 && (
								<ErrorsBanner
									errors={errors}
									onSelectHeading={handleHeadingNavigate}
									isOpen={isErrorsBannerOpen}
									setIsOpen={setIsErrorsBannerOpen}
									resetKey={errorsResetKey}
								/>
							)}
							<S.FiltersWrapper>
								<S.FilterText>Show headings:</S.FilterText>
								{filters.map((type) => (
									<S.FilterItem
										key={type}
										type="button"
										tabIndex={0}
										isSelected={headingsFilter === type}
										onClick={handleFilterClick(type)}
										tag={type}
									>
										{type}
									</S.FilterItem>
								))}
							</S.FiltersWrapper>
							{showToggleWrapper && (
								<S.ToggleWrapper>
									<S.ToggleText>
										Show <strong>hidden headings</strong> (not visible on page)
									</S.ToggleText>
									<ToggleField name="hideHidden" value={toggleStatus} onChange={setToggleStatus} size="s" />
								</S.ToggleWrapper>
							)}
							<S.HeadingsListWrapper ref={listRef}>
								{filteredHeadings.map((head, index) => (
									<HeadingItem
										key={`${head.tag}-${head.level}-${index}-${head.text.slice(0, 20)}`}
										head={head}
										index={index}
										isFiltering={isFiltering}
										selected={selected}
										onHeadingClick={handleHeadingClick}
										counter={counter}
										parentPath=""
									/>
								))}
							</S.HeadingsListWrapper>
						</S.HeadingsWrapper>
					))}
			</FloatingPanel>
		</S.Wrapper>
	);
};

interface IHeadingsPreviewProps {
	isOpen: boolean;
	browserRef: React.RefObject<HTMLDivElement>;
	toggleModal: () => void;
	headingsFilter: HeadingFilter;
	setHeadingsFilter: (value: HeadingFilter) => void;
}

export default HeadingsPreviewModal;
