import React, { useEffect, useState } from "react";
import { connect } from "react-redux";

import { appActions } from "@ax/containers/App";
import { integrationsActions } from "@ax/containers/Integrations";
import { ErrorToast, FieldsBehavior, MainWrapper, Button, FloatingPanel, PageFinder, Toast } from "@ax/components";
import { IIntegration, IIntegrationVariable, ILanguage, IPage, IRootState } from "@ax/types";
import { IError } from "@ax/containers/App/reducer";
import { RouteLeavingGuard } from "@ax/guards";
import { useIsDirty, useModal, useToast } from "@ax/hooks";

import VariablePanel from "./VariablePanel";
import VariableItem from "./VariableItem";

import * as S from "./style";

const IntegrationForm = (props: IProps) => {
	const { setHistoryPush, createIntegration, updateIntegration, siteID, isSaving, siteLangs, setError, integration } =
		props;

	if (!siteID) {
		throw new Error(`ERROR: User reached Integrations with null site info`);
	}

	const isNew = !integration?.id;
	const initState: IIntegration = integration
		? integration
		: {
				name: "",
				description: "",
				contentHead: "",
				contentBody: "",
				contentBodyPosition: "start",
				contentPresence: { presenceType: "all", relatedPages: [] },
				active: true,
				variables: [],
				scriptOrder: 0,
				type: "addon",
				editable: true,
			};

	const [form, setForm] = useState<IIntegration>(initState);
	const { isOpen, toggleModal } = useModal();
	const { isOpen: isVariableOpen, toggleModal: toggleVariableModal } = useModal();
	const { isVisible: isDeletedVisible, toggleToast: toggleDeletedToast, setIsVisible: setIsDeleteVisible } = useToast();
	const { isVisible: isEditedVisible, toggleToast: toggleEditedToast, setIsVisible: setIsEditedVisible } = useToast();
	const { isDirty, resetDirty } = useIsDirty(form, isNew);

	useEffect(() => {
		if (integration) {
			setForm((state) => ({ ...state, ...integration }));
		}
	}, [integration]);

	const pageIDs = form.contentPresence.relatedPages?.length
		? form.contentPresence.relatedPages.map((page: { id: number; title: string }) => page.id)
		: [];

	const handleNameChange = (value: string) => setForm({ ...form, name: value });
	const handleDescriptionChange = (value: string) => setForm({ ...form, description: value });
	const handleContentHeadChange = (value: string) => setForm({ ...form, contentHead: value });
	const handleContentBodyChange = (value: string) => setForm({ ...form, contentBody: value });
	const handleContentBodyPositionChange = (value: string) => setForm({ ...form, contentBodyPosition: value });
	const handleContentPresenceChange = (value: string) =>
		setForm({ ...form, contentPresence: { ...form.contentPresence, presenceType: value, relatedPages: [] } });

	const action = (path: string) => setHistoryPush(path);

	const text = (
		<>
			Some changes <strong>are not saved</strong>.
		</>
	);

	const handleSave = async () => {
		const edited = !!form.id;
		const isSaved = edited ? await updateIntegration(form, siteID) : await createIntegration(form, siteID);
		if (isSaved) {
			resetDirty();
			edited && toggleEditedToast();
		}
	};

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

	const pageTitle = form.name.trim().length > 0 ? form.name : "New Add-on";

	const bodyOptions = [
		{
			value: "start",
			title: "Start of the body",
			name: "start",
		},
		{
			value: "end",
			title: "End of the body",
			name: "end",
		},
	];

	const presenceOptions = [
		{
			value: "all",
			title: "All pages of the site",
			name: "all",
		},
		{
			value: "page-manual",
			title: "Select manually on page",
			name: "page-manual",
		},
		{
			value: "page-specific",
			title: "Choose specific pages",
			name: "page-specific",
		},
	];

	const handleSetPage = (pages: IPage | IPage[]) => {
		if (Array.isArray(pages)) {
			const relPages =
				pages &&
				pages.map((page: IPage) => {
					return { id: page.id, title: page.title };
				});
			setForm({ ...form, contentPresence: { ...form.contentPresence, relatedPages: relPages } });
		}
		toggleModal();
	};

	const handleAddVariable = (variable: IIntegrationVariable) => {
		const newVariables: IIntegrationVariable[] = form.variables ? [...form.variables, variable] : [variable];
		setForm({ ...form, variables: newVariables });
	};

	const mapVariablesList = (items: IIntegrationVariable[]) =>
		Array.isArray(items) &&
		items.map((item: IIntegrationVariable, i: number) => {
			const handleDeleteVariable = () => {
				if (form.contentBody.includes(item.variableKey) || form.contentHead.includes(item.variableKey)) {
					setError({
						code: undefined,
						text: `To delete the variable key ${item.variableKey}, you need to remove it from the code.`,
					});
				} else {
					const newVariables = form.variables?.filter(
						(vari: IIntegrationVariable) => vari.variableKey !== item.variableKey,
					);
					setForm({ ...form, variables: newVariables });
					toggleDeletedToast();
				}
			};

			const handleEditVariable = (variable: IIntegrationVariable) => {
				const newVariables = form.variables ? [...form.variables] : [];
				newVariables[i] = variable;
				setForm({ ...form, variables: newVariables });
			};

			return (
				<VariableItem
					key={`${item.variableKey}-${i}`}
					item={item}
					deleteVariable={handleDeleteVariable}
					editVariable={handleEditVariable}
					siteLangs={siteLangs}
				/>
			);
		});

	return (
		<MainWrapper backLink={true} title={pageTitle} rightButton={rightButtonProps}>
			<RouteLeavingGuard when={isDirty} action={action} text={text} />
			<ErrorToast size="l" />
			<S.Wrapper>
				<S.SettingsWrapper>
					<S.SettingContent>
						<FieldsBehavior
							title="Name"
							name="name"
							fieldType="TextField"
							placeholder="Type a name"
							mandatory
							value={form.name}
							onChange={handleNameChange}
						/>
						<FieldsBehavior
							title="Description"
							name="description"
							fieldType="TextArea"
							placeholder="Type a description"
							value={form.description}
							onChange={handleDescriptionChange}
						/>
					</S.SettingContent>
				</S.SettingsWrapper>
				<S.SettingsWrapper>
					<S.Heading>Add custom Code</S.Heading>
					<S.SettingContent>
						<S.SettingText>
							The third-party has to give you instructions about where to place the code and how to use it.
						</S.SettingText>
						<FieldsBehavior
							title="Page Head Code"
							name="contentHead"
							fieldType="TextArea"
							placeholder="Enter the code that will be injected into the head's page."
							value={form.contentHead}
							onChange={handleContentHeadChange}
							rows={10}
						/>
						<FieldsBehavior
							title="Page Body Code"
							name="contentBody"
							fieldType="TextArea"
							placeholder="Enter the code that will be injected into the body's page."
							value={form.contentBody}
							onChange={handleContentBodyChange}
							rows={10}
						/>
						<FieldsBehavior
							title="Page body code in:"
							name="contentBodyPosition"
							fieldType="RadioGroup"
							value={form.contentBodyPosition}
							options={bodyOptions}
							onChange={handleContentBodyPositionChange}
							disabled={!form.contentBody || !form.contentBody.trim().length}
						/>
						<FieldsBehavior
							title="Add code to:"
							name="contentPresence"
							fieldType="RadioGroup"
							value={form.contentPresence.presenceType}
							options={presenceOptions}
							onChange={handleContentPresenceChange}
						/>
						{form.contentPresence.presenceType === "page-specific" && (
							<>
								{!!form.contentPresence.relatedPages?.length && (
									<S.PagesWrapper>
										{form.contentPresence.relatedPages.map((relPage: any) => (
											<S.PageElement key={relPage.title}>{relPage.title}</S.PageElement>
										))}
									</S.PagesWrapper>
								)}
								<S.ButtonWrapper>
									<Button type="button" buttonStyle="line" onClick={toggleModal}>
										Select Pages
									</Button>
								</S.ButtonWrapper>
							</>
						)}
					</S.SettingContent>
				</S.SettingsWrapper>
				<S.SettingsWrapper>
					<S.Heading>Add Variables</S.Heading>
					<S.SettingContent>
						<S.SettingText>
							You can add variables in the code to customize some properties in the page that appears.
							<br />
							E.g. change the greeting text in a chatbot on a specific page.
						</S.SettingText>
						<S.StyledButton type="button" buttonStyle="text" icon="addCircle" onClick={toggleVariableModal}>
							Add variable
						</S.StyledButton>
						{!!form.variables?.length && <S.Table>{mapVariablesList(form.variables)}</S.Table>}
					</S.SettingContent>
				</S.SettingsWrapper>
				<FloatingPanel title="Select pages" toggleModal={toggleModal} isOpen={isOpen}>
					<S.ContentWrapper>
						{isOpen && (
							<PageFinder onClick={handleSetPage} isOpen={isOpen} multiple={true} hideSites={true} pages={pageIDs} />
						)}
					</S.ContentWrapper>
				</FloatingPanel>
				{isVariableOpen && (
					<VariablePanel
						onClick={handleAddVariable}
						langs={siteLangs}
						isOpen={isVariableOpen}
						toggleModal={toggleVariableModal}
					/>
				)}
			</S.Wrapper>
			{isDeletedVisible && <Toast setIsVisible={setIsDeleteVisible} message={"Variable deleted"} />}
			{isEditedVisible && <Toast setIsVisible={setIsEditedVisible} message={"Integration edited"} />}
		</MainWrapper>
	);
};

const mapStateToProps = (state: IRootState) => ({
	isSaving: state.app.isSaving,
	isLoading: state.app.isLoading,
	siteID: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
	siteLangs: state.sites.currentSiteLanguages,
	integration: state.integrations.currentIntegration,
});

interface IDispatchProps {
	setHistoryPush: (path: string) => void;
	createIntegration: (data: IIntegration, siteID: number) => Promise<IIntegration | boolean>;
	updateIntegration: (data: IIntegration, siteID: number) => Promise<IIntegration | boolean>;
	setError: (error: IError) => void;
}

const mapDispatchToProps = {
	setHistoryPush: appActions.setHistoryPush,
	createIntegration: integrationsActions.createIntegration,
	updateIntegration: integrationsActions.updateIntegration,
	setError: appActions.setError,
};

interface IIntegrationFromProps {
	isSaving: boolean;
	isLoading: boolean;
	siteID: number | null;
	siteLangs: ILanguage[];
	integration: IIntegration | null;
}

type IProps = IIntegrationFromProps & IDispatchProps;

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