import { useState } from "react";
import { connect } from "react-redux";

import type { IDataSource, ILanguage, IRootState, ISite, IStructuredData } from "@ax/types";
import {
	Button,
	FloatingMenu,
	IconAction,
	RadioGroup,
	Select,
	FieldsBehavior,
	AsyncSelect,
	ToggleField,
	UniqueCheck,
} from "@ax/components";

import type { IRefField, ISource, OrderDirection, SiteValue } from "../Context";
import { useReference } from "../Context";
import AutoItem from "./AutoItem";

import * as S from "./style";

const AutoPanel = (props: IProps) => {
	const { onChange, site, structuredData, validators, categories, globalLangs, lang } = props;

	const { state, setState } = useReference();

	const init = state.quantity ? "number" : "all";
	const [allItems, setAllItems] = useState<string | boolean>(init);
	const initConfig = {
		isConfigOpen: false,
		isContentOpen: true,
		isDataOpen: true,
		isAllLangOpen: true,
		isCustomLangOpen: true,
		isGlobalOpen: true,
	};
	const [configState, setConfigState] = useState<IConfigState>(initConfig);
	const initToggle = {
		isSiteActive: !!(state.site && state.site !== "global"),
		isLangActive: !!state.lang,
		isGlobalActive: !!(state.site && state.site === "global"),
		errorSite: false,
		errorLang: false,
		errorMsg: "",
	};
	const [toggleState, setToggleState] = useState<IToggleState>(initToggle);

	const langOptions = globalLangs
		.filter((language) => lang.id !== language.id)
		.map((language) => ({ label: language.label, value: language.id.toString() }));

	const currentLang = globalLangs.find((language) => lang.id === language.id)?.language;

	const hasGlobalContent = structuredData.global.length > 0;

	const structuredDataValues: IStructuredData[] = [];
	for (const [, dataType] of Object.entries(structuredData)) {
		structuredDataValues.push(...dataType);
	}

	const options = [
		{
			title: "Most recent",
			value: "recent",
			name: "recent",
		},
		{
			title: "Alphabetical order",
			value: "alpha",
			name: "alpha",
		},
	];

	const sourcesFields = state.sources.map((source) => {
		const foundData = structuredDataValues.find((value) => value.id === source.structuredData);
		const indexableFields = foundData?.schema.fields.filter((field) => field.indexable) || [];
		return indexableFields;
	});

	const indexableFields =
		!!sourcesFields.length &&
		sourcesFields[0].filter((field) => sourcesFields.every((f) => f.some((_f) => _f.key === field.key)));

	if (indexableFields) {
		indexableFields.forEach((field) => {
			const newField = {
				title: field.title,
				value: field.key,
				name: field.key,
			};
			options.push(newField);
		});
	}

	const orderOptions = [
		{ label: "Ascendent", value: "ASC" },
		{ label: "Descendent", value: "DESC" },
	];

	const handleOrderDirection = (orderDirection: OrderDirection) => setState((state) => ({ ...state, orderDirection }));

	const optionsOnSelect = (
		<S.SelectWrapper>
			<Select
				type="inline"
				name="orderDirection"
				value={state.orderDirection}
				options={orderOptions}
				onChange={(value: string) => handleOrderDirection(value as OrderDirection)}
				mandatory
				alignRight
			/>
		</S.SelectWrapper>
	);

	const radioGroupProps = {
		name: "Sort",
		options,
		value: state.order,
		onChange: (order: string | boolean) => setState((state) => ({ ...state, order: order as string })),
		optionsOnSelect,
	};

	const numberFieldProps = {
		fieldType: "NumberField",
		title: "",
		value: state.quantity,
		onChange: (quantity: number) => setState((state) => ({ ...state, quantity })),
		maxValue: validators?.maxValue ? validators.maxValue : undefined,
		minValue: validators?.minValue ? validators.minValue : undefined,
	};

	const checkErrors = (): boolean => {
		let errorSite = false;
		let errorLang = false;
		let errorMsg = "";
		if (toggleState.isSiteActive && !state.site) {
			errorSite = true;
			errorMsg = "You need to select site before apply.";
		}
		if (toggleState.isLangActive && !state.lang) {
			errorLang = true;
			errorMsg = "You need to select language before apply.";
		}
		if (errorLang && errorSite) {
			errorMsg = "You need to define some settings before apply.";
		}
		setToggleState({ ...toggleState, errorLang, errorSite, errorMsg });
		return errorSite || errorLang;
	};

	const handleApply = () => {
		if (!checkErrors()) {
			const order = state.order.concat("-", state.orderDirection);
			const newValue = {
				mode: "auto" as const,
				quantity: state.quantity,
				order,
				sources: state.sources,
				fullRelations: state.fullRelations,
				allLanguages: state.allLanguages,
				preferenceLanguage: state.preferenceLanguage,
				lang: state.lang,
				site: state.site,
			};

			onChange(newValue);
		}
	};

	const handleAddSource = (value: string) => {
		if (!state.sources.find((source) => source.structuredData === value)) {
			const newSource: ISource = { structuredData: value, filters: [], filterOperator: "OR", globalOperator: "AND" };
			const updatedSources = [...state.sources, newSource];
			setState((state) => ({ ...state, sources: updatedSources }));
		}
	};

	const sourceMenuOption = (option: IDataSource) => {
		const handleAddOption = () => handleAddSource(option.id);
		return (
			<S.SourceItem key={option.id} onClick={handleAddOption}>
				{option.title}
			</S.SourceItem>
		);
	};

	const sourceMenu = (menu: IDataSource[]) => (
		<S.SourceMenu>{menu.map((option) => sourceMenuOption(option))}</S.SourceMenu>
	);

	const addSourceButton = () => (
		<S.IconWrapper>
			<IconAction icon="add" />
		</S.IconWrapper>
	);

	const addSource =
		state.sourceTitles.length > 1 ? (
			<FloatingMenu Button={addSourceButton}>{sourceMenu(state.sourceTitles)}</FloatingMenu>
		) : null;

	const removeItem = (item: string) => {
		const newSources = state.sources.filter((e) => e.structuredData !== item);
		setState((state) => ({ ...state, sources: newSources }));
	};

	const handleAddFilter = (source: ISource) => {
		const updatedSources = [...state.sources];
		updatedSources.forEach((s, index) => {
			if (s.structuredData === source.structuredData) {
				updatedSources[index] = source;
			}
		});
		setState((state) => ({ ...state, sources: updatedSources }));
	};

	const quantityOptions = [
		{
			title: "Choose number of items",
			name: "number",
			value: "number",
		},
		{
			title: "All items",
			name: "all",
			value: "all",
		},
	];

	const handleItemsChange = (value: string | boolean) => {
		setAllItems(value);
		if (value === "all") {
			setState((state) => ({ ...state, quantity: 0 }));
		}
	};

	const handleAllLanguagesChange = (value: boolean) => {
		const preferenceLanguage = value ? state.preferenceLanguage : false;
		setState((state) => ({ ...state, allLanguages: value, preferenceLanguage }));
	};

	const handlePreferenceLanguageChange = (value: boolean) => {
		setState((state) => ({ ...state, preferenceLanguage: value }));
	};

	const toggleConfig = (value: keyof IConfigState) => setConfigState({ ...configState, [value]: !configState[value] });

	const handleSiteChange = (value: string | number | null) => {
		setState((state) => ({ ...state, site: (value as SiteValue) || undefined }));
		if (value) {
			setToggleState({ ...toggleState, isSiteActive: true, errorSite: false, errorMsg: "" });
		}
	};

	const handleLangChange = (value: string) => {
		const lang = value.length ? parseInt(value, 10) : undefined;
		setState((state) => ({ ...state, lang }));
		if (value) {
			setToggleState({ ...toggleState, isLangActive: true, errorLang: false, errorMsg: "" });
		}
	};

	const handleLangToggleChange = (value: boolean) => {
		setToggleState({ ...toggleState, isLangActive: value });
		if (!value) {
			setState((state) => ({ ...state, lang: undefined }));
		}
	};

	const handleSiteToggleChange = (value: boolean) => {
		setToggleState({ ...toggleState, isSiteActive: value, isGlobalActive: value ? false : toggleState.isGlobalActive });
		setState((state) => ({ ...state, site: value ? null : undefined }));
	};

	const handleGlobalToggleChange = (value: boolean) => {
		setToggleState({ ...toggleState, isGlobalActive: value, isSiteActive: value ? false : toggleState.isSiteActive });
		setState((state) => ({ ...state, site: value ? "global" : null }));
	};

	return (
		<S.Wrapper data-testid="auto-panel-wrapper">
			<S.FormWrapper>
				<S.DataLabel>Data source</S.DataLabel>
				<S.SourcesWrapper>
					<S.SourceActions>
						{state.sources && `${state.sources.length} items`} {addSource}
					</S.SourceActions>
					{state.sources &&
						state.sources.length > 0 &&
						state.sources.map((singleSource) => {
							const source = state.sourceTitles.find((el) => el.id === singleSource.structuredData);
							const { filters = [], filterOperator, globalOperator } = singleSource;
							if (!source) return null;
							return (
								<AutoItem
									key={singleSource.structuredData}
									source={source}
									canDelete={state.sourceTitles.length > 1}
									handleDelete={removeItem}
									filter={filters}
									addFilter={handleAddFilter}
									currentSite={site}
									structuredDataSite={[...structuredData.site, ...categories.site]}
									filterOperator={filterOperator}
									globalOperator={globalOperator}
									siteID={state.site || site?.id}
								/>
							);
						})}
				</S.SourcesWrapper>
				<S.RadioWrapper>
					<S.FieldLabel>Sort</S.FieldLabel>
					<RadioGroup {...radioGroupProps} />
				</S.RadioWrapper>
				<S.FieldLabel>Number of items</S.FieldLabel>
				<RadioGroup name="quantityGroup" options={quantityOptions} value={allItems} onChange={handleItemsChange} />
				{allItems !== "all" && <FieldsBehavior {...numberFieldProps} />}

				<S.AdvancedWrapper>
					<S.ConfigLabel
						onClick={() => toggleConfig("isConfigOpen")}
						isOpen={configState.isConfigOpen}
						data-testid="advanced-config-label"
					>
						Advanced Settings
					</S.ConfigLabel>

					<S.ConfigWrapper isOpen={configState.isConfigOpen}>
						<S.OptionLabel
							onClick={() => toggleConfig("isContentOpen")}
							isOpen={configState.isContentOpen}
							data-testid="advanced-config-label"
						>
							<S.LabelContent>
								<S.OptionText>Content options</S.OptionText>
							</S.LabelContent>
						</S.OptionLabel>

						<S.ConfigWrapper isOpen={configState.isContentOpen}>
							<S.ConfigContent smallMargin={toggleState.isLangActive}>
								{langOptions.length > 0 ? (
									<>
										<S.OptionLabel
											onClick={() => toggleConfig("isCustomLangOpen")}
											isOpen={configState.isCustomLangOpen}
											data-testid="advanced-config-label"
										>
											<S.LabelContent>
												<S.SubOptionText>Change distributor language</S.SubOptionText>
												<ToggleField
													name="customLang"
													value={toggleState.isLangActive}
													onChange={handleLangToggleChange}
													size="s"
												/>
											</S.LabelContent>
										</S.OptionLabel>

										<S.ConfigWrapper>
											<S.SubConfigContent hasMargin={true}>
												<S.OptionDescription isOpen={configState.isCustomLangOpen}>
													By default, content is shown in the page`&apos;`s language. Activate this option to{" "}
													<strong>specifically set a different language</strong> for the content shown in this
													distributor.
												</S.OptionDescription>
												{toggleState.isLangActive && (
													<Select
														name="select"
														options={langOptions}
														onChange={handleLangChange}
														value={state.lang?.toString() || ""}
														type="inline"
														placeholder="Select language"
														error={toggleState.errorLang}
													/>
												)}
											</S.SubConfigContent>
										</S.ConfigWrapper>
									</>
								) : null}

								<S.OptionLabel
									onClick={() => toggleConfig("isDataOpen")}
									isOpen={configState.isDataOpen}
									data-testid="advanced-config-label"
								>
									<S.LabelContent>
										<S.SubOptionText>Data from a different site</S.SubOptionText>
										<ToggleField
											name="site-toggle"
											value={toggleState.isSiteActive}
											onChange={handleSiteToggleChange}
											size="s"
										/>
									</S.LabelContent>
								</S.OptionLabel>

								<S.ConfigWrapper>
									<S.SubConfigContent hasMargin={true}>
										<S.OptionDescription isOpen={configState.isDataOpen}>
											Data is retrieved from the site you are on.{" "}
											<strong>If you prefer to display content from another site, </strong>
											enable this option to select the specific site.
										</S.OptionDescription>
										{toggleState.isSiteActive && (
											<AsyncSelect
												name="select"
												entity="sites"
												onChange={handleSiteChange}
												value={state.site || null}
												type="inline"
												placeholder="Select site"
												error={toggleState.errorSite}
											/>
										)}
									</S.SubConfigContent>
								</S.ConfigWrapper>

								{hasGlobalContent && (
									<>
										<S.OptionLabel
											onClick={() => toggleConfig("isGlobalOpen")}
											isOpen={configState.isGlobalOpen}
											data-testid="advanced-config-label"
										>
											<S.LabelContent>
												<S.SubOptionText>Data from Global Content</S.SubOptionText>
												<ToggleField
													name="global-toggle"
													value={toggleState.isGlobalActive}
													onChange={handleGlobalToggleChange}
													size="s"
												/>
											</S.LabelContent>
										</S.OptionLabel>

										<S.ConfigWrapper>
											<S.SubConfigContent hasMargin={toggleState.isSiteActive}>
												<S.OptionDescription isOpen={configState.isGlobalOpen}>
													Data is retrieved from the site you are on.{" "}
													<strong>If you prefer to display Global content,</strong> enable this option.
												</S.OptionDescription>
											</S.SubConfigContent>
										</S.ConfigWrapper>
									</>
								)}
							</S.ConfigContent>
						</S.ConfigWrapper>

						<S.OptionLabel
							onClick={() => toggleConfig("isAllLangOpen")}
							isOpen={configState.isAllLangOpen}
							data-testid="advanced-config-label"
						>
							<S.LabelContent>
								<S.OptionText>Include content in multiple languages</S.OptionText>
								<ToggleField
									name="allLanguages"
									value={state.allLanguages}
									onChange={handleAllLanguagesChange}
									size="s"
								/>
							</S.LabelContent>
						</S.OptionLabel>

						<S.ConfigWrapper>
							<S.ConfigContent smallMargin={toggleState.isSiteActive} isLast={true}>
								<S.OptionDescription isOpen={configState.isAllLangOpen}>
									By default, the imported data in this distributor is ONLY in the{" "}
									<strong>distributor’s language</strong>. Enable this option to{" "}
									<strong>import data in ALL languages available.</strong>
								</S.OptionDescription>
								{state.allLanguages && (
									<S.CheckWrapper>
										<UniqueCheck
											name="preferenceLanguageCheck"
											options={[{ title: `${currentLang} content shown first` }]}
											value={`${state.preferenceLanguage}`}
											onChange={handlePreferenceLanguageChange}
										/>
									</S.CheckWrapper>
								)}
							</S.ConfigContent>
						</S.ConfigWrapper>
					</S.ConfigWrapper>
				</S.AdvancedWrapper>
			</S.FormWrapper>
			<S.ActionsWrapper>
				<S.ErrorWrapper>{toggleState.errorMsg.length > 0 && toggleState.errorMsg}</S.ErrorWrapper>
				<Button type="button" onClick={handleApply}>
					Apply
				</Button>
			</S.ActionsWrapper>
		</S.Wrapper>
	);
};

interface IProps {
	structuredData: { global: IStructuredData[]; site: IStructuredData[] };
	categories: { global: IStructuredData[]; site: IStructuredData[] };
	onChange: (value: IRefField) => void;
	site: ISite | null;
	validators?: Record<string, unknown>;
	globalLangs: ILanguage[];
	lang: { locale: string; id: number };
}

interface IConfigState {
	isConfigOpen: boolean;
	isContentOpen: boolean;
	isDataOpen: boolean;
	isAllLangOpen: boolean;
	isCustomLangOpen: boolean;
	isGlobalOpen: boolean;
}

interface IToggleState {
	isSiteActive: boolean;
	isLangActive: boolean;
	isGlobalActive: boolean;
	errorSite: boolean;
	errorLang: boolean;
	errorMsg: string;
}

const mapStateToProps = (state: IRootState) => ({
	globalLangs: state.app.globalLangs,
	lang: state.app.lang,
});

export default connect(mapStateToProps)(AutoPanel);
