import { useEffect, useRef, useState } from "react";
import { withErrorBoundary } from "react-error-boundary";
import { connect } from "react-redux";
import type { RouteComponentProps } from "react-router-dom";

import { ErrorPage, ErrorToast, Loading, MainWrapper, Notification, OcassionalToast } from "@ax/components";
import { appActions } from "@ax/containers/App";
import { formsActions } from "@ax/containers/Forms";
import { findFieldsErrors } from "@ax/forms";
import { RouteLeavingGuard } from "@ax/guards";
import { getDefaultTheme } from "@ax/helpers";
import { useModals, usePermissions, useShouldBeSaved } from "@ax/hooks";
import type {
	FormContent,
	FormLanguage,
	FormState,
	IErrorItem,
	ILanguage,
	INotification,
	IRootState,
	ISite,
} from "@ax/types";

import { DeleteModal, UnPublishModal, UpdateModal } from "../atoms";
import FormUseModal from "../FormUseModal";
import Editor from "./Editor";

import * as S from "./style";

const FormEditor = (props: IProps) => {
	const {
		formID,
		isLoading,
		isSaving,
		lang,
		globalLangs,
		formContent,
		isNewTranslation,
		currentSiteInfo,
		skipReviewOnPublish,
		siteLangs,
		setHistoryPush,
		setLanguage,
		deleteForm,
		getForm,
		createNewTranslation,
		setCurrentFormID,
		setTab,
		setSelectedContent,
	} = props;

	const {
		id,
		title,
		state,
		dataLanguages,
		formInUse,
		deleted = false,
	} = formContent || { dataLanguages: [], formInUse: null };
	const formStatus = state === "active" ? "active" : "offline";
	const isTranslated = dataLanguages.length > 1;
	const isSiteView = !!currentSiteInfo;
	const errorNotificationText = "There are some errors on the form so you can not publish yet. Please review them.";
	const deletedNotificationText =
		"This form has been deleted and can’t be edited. It will be permanently removed from the trash after 30 days.";
	const isPublishedForm = state === "active";

	const formLanguages: ILanguage[] = [];
	dataLanguages?.forEach((dataLang) => {
		const formLang = globalLangs.find((lang) => lang.id === dataLang.language);
		if (formLang) {
			formLanguages.push(formLang);
		}
	});

	const availableLanguages = deleted ? formLanguages : isSiteView ? siteLangs : globalLangs;

	const [notification, setNotification] = useState<INotification | null>(null);
	const [deleteAllVersions, setDeleteAllVersions] = useState(false);
	const [errors, setErrors] = useState<IErrorItem[]>([]);
	const { isOpen, toggleModal } = useModals(["delete", "unpublish", "use", "update"]);

	const { isDirty, setIsDirty } = useShouldBeSaved(formContent);
	const browserRef = useRef<HTMLDivElement>(null);

	const isAllowedTo = usePermissions({
		deleteForms: isSiteView ? "forms.deleteForms" : "global.forms.deleteForms",
		publishUnpublishForms: isSiteView ? "forms.publishUnpublishForms" : "global.forms.publishUnpublishForms",
	});

	const toastMsg = (
		<>
			This form is used in some pages. Changes will apply to all pages where it’s{" "}
			<button type="button" onClick={() => toggleModal("use")}>
				in use.
			</button>
		</>
	);

	const backLinkRoute = isSiteView ? "/sites/forms" : "/forms";
	const theme = getDefaultTheme();

	// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
	useEffect(() => {
		const { setTab, resetForm } = props;

		const handleGetForm = async () => await getForm(formID);

		const defaultTab = "content";
		setTab(defaultTab);
		handleGetForm();

		if (!formID) {
			setIsDirty(true);
		}

		return () => {
			resetForm();
		};
	}, [lang]);

	const goToForms = (path: string) => {
		createNewTranslation(false);
		setHistoryPush(path, true);
	};

	const handleSavePage = async (updateState?: FormState) => {
		const { saveForm } = props;

		isOpen("update") && toggleModal("update");

		const skipErrorCheck =
			skipReviewOnPublish || (!isPublishedForm && !updateState) || (isPublishedForm && updateState === "inactive");

		const fieldErrors = skipErrorCheck ? [] : findFieldsErrors(formContent, true);

		if (fieldErrors.length) {
			setErrors(fieldErrors);
			setNotification({ text: errorNotificationText, type: "error" });
		} else {
			const isSaved = await saveForm(browserRef?.current, updateState);
			if (isSaved) {
				setIsDirty(false);
			}
		}
	};

	const removeItem = async () => {
		if (!id) return;
		if (formInUse) {
			setNotification({
				text: "This form is currently used on multiple pages. Please remove it from those pages before deleting.",
				type: "error",
				btnText: "Show pages",
				onClick: () => toggleModal("use"),
			});
		} else {
			const allFormVersions = dataLanguages.map((lang: FormLanguage) => lang.id);
			const isDeleted = deleteAllVersions ? await deleteForm(allFormVersions) : await deleteForm(id);
			if (isDeleted) {
				setHistoryPush(backLinkRoute, false);
				toggleModal("delete");
			}
		}
	};

	const handleDiscarChanges = async () => {
		const { getForm } = props;
		setIsDirty(false);
		await getForm(formID);
	};

	const publishForm = () => handleSavePage("active");

	const unpublishForm = () => {
		if (formInUse) {
			setNotification({
				text: "This form is currently used on multiple pages. Please remove it from those pages before unpublishing.",
				type: "error",
				btnText: "Show pages",
				onClick: () => toggleModal("use"),
			});
		} else {
			handleSavePage("inactive");
		}
		isOpen("unpublish") && toggleModal("unpublish");
	};

	const getSelectedFormLanguage = (language: ILanguage) =>
		dataLanguages?.find((formLang) => formLang.language === language.id);

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

		const selectedFormLanguage = getSelectedFormLanguage(language);

		if (selectedFormLanguage) {
			createNewTranslation(false);
			setCurrentFormID(selectedFormLanguage.id);
		} else {
			createNewTranslation(true);
		}
	};

	const goToError = async (editorID: number | null, tab: string, template: boolean) => {
		const realEditorID = template ? 0 : editorID;
		if (realEditorID !== null) {
			await setSelectedContent(realEditorID);
			setTab(tab);
		}
	};

	const menuOptions = [];

	if (isDirty) {
		menuOptions.push({
			label: "Discard changes",
			icon: "close",
			action: handleDiscarChanges,
		});
	}

	if (isAllowedTo.deleteForms) {
		menuOptions.push({
			label: "Delete form",
			icon: "delete",
			action: () => toggleModal("delete"),
		});
	}

	const publishButton =
		state && state === "active"
			? {
					label: "Unpublish form",
					action: () => toggleModal("unpublish"),
				}
			: {
					label: "Publish form",
					action: publishForm,
				};

	const downArrowMenu = {
		button: isAllowedTo.publishUnpublishForms ? publishButton : undefined,
		options: menuOptions,
		displayed: !deleted,
	};

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

	const rightButtonProps = {
		label: isSaving ? "Saving" : !isDirty ? "Saved" : isPublishedForm ? "Update changes" : "Save",
		disabled: (!isDirty && formID !== null && !isNewTranslation) || isSaving,
		action: () => (formInUse ? toggleModal("update") : handleSavePage()),
	};


	return isLoading ? (
		<Loading />
	) : (
		<>
			<RouteLeavingGuard when={isDirty} action={goToForms} text={modalText} />
			<MainWrapper
				title={title || "New Form"}
				backLink={backLinkRoute}
				rightButton={rightButtonProps}
				downArrowMenu={downArrowMenu}
				fixedAppBar={true}
				pageStatus={formStatus}
				language={lang}
				languageAction={handleLanguage}
				availableLanguages={availableLanguages}
				currentLanguages={formLanguages}
				currentPageID={0}
				fullWidth={true}
				inversed={!isSiteView}
				isFromEditor={true}
				isDirty={isDirty}
				errors={errors}
				errorActions={{ goToError }}
				isSaving={isSaving}
			>
				{notification && (
					<S.NotificationWrapper>
						<Notification
							type={notification.type}
							text={notification.text}
							btnText={notification.btnText}
							onClick={notification.onClick}
							resetError={() => setNotification(null)}
						/>
					</S.NotificationWrapper>
				)}
				{deleted && (
					<S.NotificationWrapper>
						<Notification type="error" text={deletedNotificationText} />
					</S.NotificationWrapper>
				)}
				<ErrorToast size="l" fixed />
				<S.Content>
					<Editor
						isGlobal={true}
						theme={theme}
						errors={errors}
						browserRef={browserRef}
						setNotification={setNotification}
						setErrors={setErrors}
						isDisabled={deleted}
					/>
				</S.Content>
			</MainWrapper>
			<DeleteModal
				isOpen={isOpen("delete")}
				toggleModal={() => toggleModal("delete")}
				onDelete={removeItem}
				{...{ title, isTranslated, deleteAllVersions, setDeleteAllVersions }}
			/>
			<UnPublishModal
				isOpen={isOpen("unpublish")}
				toggleModal={() => toggleModal("unpublish")}
				onUnpublish={unpublishForm}
				title={title}
			/>
			{isOpen("update") && (
				<UpdateModal isOpen={isOpen("update")} toggleModal={() => toggleModal("update")} onUpdate={() => handleSavePage()} />
			)}
			{isOpen("use") && (
				<FormUseModal
					isOpen={isOpen("use")}
					toggleModal={() => toggleModal("use")}
					isSiteView={!!currentSiteInfo?.id}
					formInUse={formInUse}
				/>
			)}
			{formInUse && <OcassionalToast message={toastMsg} />}
		</>
	);
};

const mapStateToProps = (state: IRootState): IFormEditorStateProps => ({
	isSaving: state.app.isSaving,
	isLoading: state.app.isLoading,
	lang: state.app.lang,
	globalLangs: state.app.globalLangs,
	formID: state.forms.currentFormID,
	formContent: state.forms.formContent,
	currentSiteInfo: state.sites.currentSiteInfo,
	isNewTranslation: state.forms.isNewTranslation,
	skipReviewOnPublish: state.app.globalSettings.skipReviewOnPublish,
	siteLangs: state.sites.currentSiteLanguages,
});

interface IFormEditorStateProps {
	isSaving: boolean;
	isLoading: boolean;
	lang: { locale: string; id: number | null };
	globalLangs: ILanguage[];
	formID: number | null;
	formContent: FormContent | null;
	currentSiteInfo: ISite | null;
	isNewTranslation: boolean;
	skipReviewOnPublish?: boolean;
	siteLangs: ILanguage[];
}

const mapDispatchToProps = {
	setHistoryPush: appActions.setHistoryPush,
	setLanguage: appActions.setLanguage,
	getForm: formsActions.getForm,
	setTab: formsActions.setSelectedTab,
	saveForm: formsActions.saveForm,
	resetForm: formsActions.resetForm,
	deleteForm: formsActions.deleteForm,
	createNewTranslation: formsActions.createNewTranslation,
	setCurrentFormID: formsActions.setCurrentFormID,
	setSelectedContent: formsActions.setSelectedContent,
};

interface IFormEditorDispatchProps {
	setHistoryPush: (path: string, isEditor: boolean) => void;
	setLanguage(lang: { locale: string; id: number | null }): void;
	getForm(formID: number | null): Promise<void>;
	setTab(tab: string): void;
	saveForm(formHtml?: HTMLDivElement | null, updateState?: FormState): Promise<boolean>;
	resetForm(): void;
	deleteForm: (formID: number[] | number) => Promise<boolean>;
	createNewTranslation(isNewTranslation: boolean): void;
	setCurrentFormID(formID: number | null): void;
	setSelectedContent(editorID: number): Promise<void>;
}

type IProps = IFormEditorStateProps & IFormEditorDispatchProps & RouteComponentProps;

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

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