import { useEffect, useMemo, useRef, useState } from "react";
import { withErrorBoundary } from "react-error-boundary";
import { connect } from "react-redux";

import { ErrorPage, ErrorToast, Loading, MainWrapper, Modal, Notification, RestoreModal } from "@ax/components";
import { appActions } from "@ax/containers/App";
import { menuActions, navigationActions } from "@ax/containers/Navigation";
import { RouteLeavingGuard } from "@ax/guards";
import { useIsDirty, useModal, usePermission } from "@ax/hooks";
import type { ILanguage, INavigation, INavigationLanguage, INotification, IRootState, ISite } from "@ax/types";

import Editor from "./Editor";

import * as S from "./style";

const DefaultsEditor = (props: IProps) => {
	const {
		isSaving,
		isLoading,
		lang,
		siteLanguages,
		setLanguage,
		getValues,
		createNavigation,
		editorContent,
		updateNavigation,
		currentDefaultsContent,
		navLanguages,
		isNewTranslation,
		createTranslation,
		selectedDefault,
		setHeader,
		setFooter,
		setHistoryPush,
		getMenus,
		currentSiteInfo,
		restoreNavigation,
	} = props;

	const [notification, setNotification] = useState<INotification | null>(null);
	const { isOpen, toggleModal } = useModal();
	const { isOpen: isRestoreOpen, toggleModal: toggleRestoreModal } = useModal();
	const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent, isNewTranslation);
	const currentDefaultNav = currentDefaultsContent.find((item) => item.setAsDefault);
	const isNew = !editorContent?.id;
	const isDeleted = !!editorContent?.deleted;

	const deletedNotificationText = `This ${editorContent?.type} has been deleted and can't be edited. It will be permanently removed from the trash after 30 days.`;

	const currentLanguages = useMemo(() => {
		if (!navLanguages) return [];
		return navLanguages
			.map((navLang) => siteLanguages.find((siteLang) => siteLang.id === navLang.languageId))
			.filter((siteLang): siteLang is ILanguage => !!siteLang);
	}, [navLanguages, siteLanguages]);

	const isSetAsDefault = editorContent?.setAsDefault;
	const browserRef = useRef<HTMLDivElement>(null);

	const isAllowedToCreate = usePermission(`navigation.createSite${selectedDefault}`);

	useEffect(() => {
		const init = async () => await getValues();
		init();
	}, [getValues]);

	useEffect(() => {
		if (isNew) {
			setIsDirty(false);
		}
	}, [isNew, setIsDirty]);

	// biome-ignore lint/correctness/useExhaustiveDependencies: resetDirty is not referentially stable (not wrapped in useCallback)
	useEffect(() => {
		resetDirty();
	}, [lang]);

	useEffect(() => {
		const editorType = editorContent?.type;
		const navigationModuleComponent = editorType ? currentSiteInfo?.navigationModules?.[editorType] : undefined;
		const currentNavigation = currentDefaultsContent?.find((item) => item.id === editorContent?.id);
		if (navigationModuleComponent && editorContent && currentNavigation) {
			const isNavigationModuleChanged = navigationModuleComponent !== currentNavigation.component;
			if (isNavigationModuleChanged) setIsDirty(true);
		}
	}, [currentSiteInfo, editorContent, currentDefaultsContent, setIsDirty]);

	const save = async () => {
		const isSaved =
			isNew || isNewTranslation
				? await createNavigation(browserRef?.current)
				: editorContent?.id
					? await updateNavigation(editorContent.id, editorContent, true, browserRef?.current)
					: false;
		if (isSaved) resetDirty();
	};

	const saveAsDefault = async () => {
		await save();
		toggleModal();
	};

	const saveButtonAction = () => {
		const isCurrentDefault = currentDefaultNav && currentDefaultNav.id === editorContent?.id;
		const isNewDefault = isNew || !isCurrentDefault;
		isSetAsDefault && isNewDefault && !isNewTranslation ? toggleModal() : save();
	};

	const rightButtonProps = {
		label: !isDirty && !isNewTranslation && !isNew ? "Saved" : isSaving ? "Saving" : "Save",
		disabled: (!isDirty && !isNewTranslation && !isNew) || isSaving,
		action: saveButtonAction,
	};

	const getNavigation = async (id: number) => {
		const isHeader = selectedDefault === "Headers";
		isHeader ? setHeader(id) : setFooter(id);
		await getValues();
	};

	const getSelectedNavLanguage = (language: ILanguage) =>
		navLanguages.find((navLang) => navLang.languageId === language.id);

	const handleLanguage = async (language: ILanguage) => {
		const { locale, id } = language;
		setLanguage({ locale, id });

		const selectedNavLanguage = getSelectedNavLanguage(language);

		if (selectedNavLanguage) {
			createTranslation(false);
			await getNavigation(selectedNavLanguage.navigationId);
			resetDirty();
		} else {
			setIsDirty(true);
			createTranslation(true);
			await getMenus();
			editorContent?.id && (await getNavigation(editorContent.id));
		}
	};

	const handleRestorePage = async () => {
		if (!editorContent?.id) return;
		const isRestored = await restoreNavigation(editorContent.id, editorContent.type);
		if (isRestored) {
			if (isRestoreOpen) toggleRestoreModal();
			await getValues();
		}
	};

	const leavingGuardText = (
		<>
			Some content <strong>is not saved</strong> on this page.
		</>
	);

	const setDefaultModalText = editorContent && (
		<p>{`There is already a ${editorContent.type} defined as default. If you change it to this one, it will be shown on all the pages on this site. Are you sure your want to make this change?`}</p>
	);

	const mainModalAction = { title: "Yes, set as default", onClick: saveAsDefault };
	const secondaryModalAction = { title: "Cancel", onClick: toggleModal };
	const goBack = (path: string) => setHistoryPush(path, true);

	const availableLanguages = isAllowedToCreate && !isDeleted ? siteLanguages : currentLanguages;

	const backLinkRoute = "/sites/navigations/modules";

	return isLoading ? (
		<Loading />
	) : (
		<>
			<RouteLeavingGuard when={isDirty || isSaving} action={goBack} text={leavingGuardText} />
			<MainWrapper
				title={editorContent?.title ?? ""}
				backLink={backLinkRoute}
				rightButton={rightButtonProps}
				fixedAppBar={true}
				language={lang}
				languageAction={handleLanguage}
				availableLanguages={availableLanguages}
				currentLanguages={currentLanguages}
				fullWidth={true}
			>
				{notification && (
					<S.NotificationWrapper>
						<Notification
							type={notification.type}
							text={notification.text}
							btnText={notification.btnText}
							onClick={notification.onClick}
							resetError={() => setNotification(null)}
						/>
					</S.NotificationWrapper>
				)}
				{isDeleted && (
					<S.NotificationWrapper>
						<Notification
							type="error"
							text={deletedNotificationText}
							btnText={`Restore ${editorContent?.type}`}
							onClick={toggleRestoreModal}
						/>
					</S.NotificationWrapper>
				)}
				<ErrorToast size="l" />
				<Modal
					isOpen={isOpen}
					hide={toggleModal}
					title={`Default ${editorContent?.component}`}
					secondaryAction={secondaryModalAction}
					mainAction={mainModalAction}
					size="S"
				>
					{isOpen ? <S.ModalContent>{setDefaultModalText}</S.ModalContent> : null}
				</Modal>
				<S.Content>
					<Editor browserRef={browserRef} setNotification={setNotification} isDisabled={isDeleted} />
				</S.Content>
				<RestoreModal
					isOpen={isRestoreOpen}
					toggleModal={toggleRestoreModal}
					{...{
						isChild: false,
						hasIssues: false,
						restorePage: handleRestorePage,
					}}
				/>
			</MainWrapper>
		</>
	);
};

const mapStateToProps = (state: IRootState) => ({
	isSaving: state.app.isSaving,
	isLoading: state.app.isLoading,
	lang: state.app.lang,
	siteLanguages: state.sites.currentSiteLanguages,
	selectedDefault: state.navigation.selectedDefault,
	editorContent: state.navigation.editorContent,
	header: state.navigation.header,
	footer: state.navigation.footer,
	navLanguages: state.navigation.currentNavigationLanguages,
	currentDefaultsContent: state.navigation.currentDefaultsContent,
	isNewTranslation: state.navigation.isNewTranslation,
	currentSiteInfo: state.sites.currentSiteInfo,
});

interface IStateProps {
	isSaving: boolean;
	isLoading: boolean;
	lang: { locale: string; id: number | null };
	siteLanguages: ILanguage[];
	currentDefaultsContent: INavigation[];
	selectedDefault: string;
	editorContent: INavigation | null;
	header: number | null;
	footer: number | null;
	navLanguages: INavigationLanguage[];
	isNewTranslation: boolean;
	currentSiteInfo: ISite | null;
}

const mapDispatchToProps = {
	setHistoryPush: appActions.setHistoryPush,
	setLanguage: appActions.setLanguage,
	getValues: navigationActions.getValues,
	createNavigation: navigationActions.createNavigation,
	updateNavigation: navigationActions.updateNavigation,
	createTranslation: navigationActions.createNewTranslation,
	setHeader: navigationActions.setHeader,
	setFooter: navigationActions.setFooter,
	getMenus: menuActions.getMenus,
	restoreNavigation: navigationActions.restoreNavigation,
};

interface IDispatchProps {
	setHistoryPush(path: string, isEditor?: boolean): void;
	setLanguage(lang: { locale: string; id: number | null }): void;
	getValues(): Promise<void>;
	createNavigation(navHtml?: HTMLDivElement | null): Promise<boolean>;
	updateNavigation(
		navID: number,
		data: INavigation,
		fromEditor?: boolean,
		navHtml?: HTMLDivElement | null,
	): Promise<boolean>;
	createTranslation(isNewTranslation: boolean): void;
	setHeader(id: number | null): void;
	setFooter(id: number | null): void;
	getMenus(): Promise<void>;
	restoreNavigation(navID: number | number[], type: string): Promise<boolean>;
}

type IProps = IStateProps & IDispatchProps;

const DefaultsEditorWithErrorBoundary = withErrorBoundary(DefaultsEditor, {
	fallback: <ErrorPage type="wrongEditor" />,
});

export default connect(mapStateToProps, mapDispatchToProps)(DefaultsEditorWithErrorBoundary);
