// External dependencies.
import React, { ReactElement, useEffect, useState } from 'react';

// WordPress dependencies.
import { dispatch, select } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

// Divi dependencies.
import {
	AdminLabelGroup,
	FieldContainer,
} from '@divi/module';
import { GroupContainer } from '@divi/modal';
import {
	SelectContainer,
	TextContainer,
	ToggleContainer,
} from '@divi/field-library';
import { type Module } from '@divi/types';

// Local dependencies.
import { SortingAttrs } from './types';
import {
	buildDefaultAttrsPatch,
	buildDefaultInnerContent,
	convertObjectOptions,
	convertSimpleOptions,
	getBlockDefaultsFromSchema,
	getEffectiveAttrValue,
	isToggleEnabled as isToggleEnabledValue,
} from '../../utils';

declare global {
	interface Window {
		JSFDiviViews: any;
		divi?: {
			data?: {
				select?: ( storeName: string ) => any;
				dispatch?: ( storeName: string ) => any;
			};
		};
	}
}

type SortingListItem = {
	title?: string;
	orderby?: string;
	order?: string;
	meta_key?: string;
};

const getAvailableProviders = () => {
	const providers = window.JSFDiviViews?.providers || {};
	return convertObjectOptions(providers);
};

const getSortingOrderbyOptions = () => {
	const orderby = window.JSFDiviViews?.sorting_orderby || {};
	return convertObjectOptions(orderby);
};

const getSortingOrderbySelectOptions = () => {
	const options = getSortingOrderbyOptions();

	return Object.keys( options ).map( ( value ) => ( {
		label: options[ value ]?.label || value,
		value,
	} ) );
};

const sortingOrderSelectOptions = [
	{ label: __('ASC', 'jet-engine-divi-views'), value: 'ASC' },
	{ label: __('DESC', 'jet-engine-divi-views'), value: 'DESC' },
];

const sortingOrderOptions = convertSimpleOptions([
	{ value: 'ASC', label: __('ASC', 'jet-engine-divi-views') },
	{ value: 'DESC', label: __('DESC', 'jet-engine-divi-views') },
]);

const SORTING_DEFAULTS_FALLBACK: Record<string, any> = {
	filter_id: 0,
	content_provider: 'not-selected',
	apply_type: 'ajax',
	apply_on: 'value',
	apply_button: true,
	apply_button_text: __( 'Apply filter', 'jet-engine-divi-views' ),
	sorting_label: '',
	sorting_placeholder: __( 'Sort...', 'jet-engine-divi-views' ),
	sorting_list: [
		{ title: __( 'By title from lowest to highest', 'jet-engine-divi-views' ), orderby: 'title', order: 'ASC' },
		{ title: __( 'By title from highest to lowest', 'jet-engine-divi-views' ), orderby: 'title', order: 'DESC' },
		{ title: __( 'By date from lowest to highest', 'jet-engine-divi-views' ), orderby: 'date', order: 'ASC' },
		{ title: __( 'By date from highest to lowest', 'jet-engine-divi-views' ), orderby: 'date', order: 'DESC' },
	],
	query_id: '',
	additional_providers_enabled: false,
	additional_providers_list: [
		{
			additional_provider: '',
			additional_providers_list: '',
		},
	],
};

const SORTING_SELECT_ATTRS = new Set( [
	'content_provider',
	'apply_type',
	'apply_on',
] );

const SORTING_TOGGLE_ATTRS = new Set( [
	'apply_button',
	'additional_providers_enabled',
] );

const getModalSelectors = (): any => {
	return window.divi?.data?.select?.( 'divi/modal-library' ) || select( 'divi/modal-library' );
};

const getModalDispatch = (): any => {
	return window.divi?.data?.dispatch?.( 'divi/modal-library' ) || dispatch( 'divi/modal-library' );
};

const getEditPostDispatch = (): any => {
	return window.divi?.data?.dispatch?.( 'divi/edit-post' ) || dispatch( 'divi/edit-post' );
};

const getEditPostSelectors = (): any => {
	return window.divi?.data?.select?.( 'divi/edit-post' ) || select( 'divi/edit-post' );
};

type SortingListRepeaterProps = {
	attrs: Module.Settings.Panel.Props<SortingAttrs>['attrs'];
	updateAttrs: (next: Partial<SortingAttrs>) => void;
	sortingDefaults: Record<string, any>;
};

const getActiveModalName = (): string => {
	const modalSelectors = getModalSelectors();

	if ( ! modalSelectors ) {
		return '';
	}

	const activeMultiModal = modalSelectors.getActiveModal?.( 'multi' );

	if ( activeMultiModal?.name ) {
		return activeMultiModal.name;
	}

	const modals = modalSelectors.getModals?.() || {};
	const activeModalEntry = Object.entries( modals ).find( ( [ , modal ] ) => ( modal as any )?.isActive );

	if ( activeModalEntry?.[ 0 ] ) {
		return activeModalEntry[ 0 ];
	}

	const names = Object.keys( modals );

	if ( modalSelectors.isModalActive?.( 'divi/module' ) ) {
		return 'divi/module';
	}

	return names[ names.length - 1 ] || 'divi/module';
};

const createAttrsUpdater = (
	setAttrs?: (next: Partial<SortingAttrs>) => void,
): ( ( next: Partial<SortingAttrs> ) => void ) => {
	if ( setAttrs ) {
		return ( next: Partial<SortingAttrs> ) => {
			setAttrs( next );
		};
	}

	return ( next: Partial<SortingAttrs> ) => {
		const modalName = getActiveModalName();

		if ( ! modalName ) {
			return;
		}

		const modalSelectors = getModalSelectors();
		const currentAttributes = modalSelectors?.getModal?.( modalName )?.attributes || {};
		const modalOwner = modalSelectors?.getModalOwner?.( modalName ) || '';

		if ( modalOwner ) {
			const editPostSelectors = getEditPostSelectors();
			const editPostDispatch = getEditPostDispatch();
			const currentModuleAttrs = editPostSelectors?.getModuleAttrs?.( modalOwner ) || {};
			const mergedModuleAttrs = {
				...currentModuleAttrs,
				...next,
			};

			editPostDispatch?.replaceModuleAttributes?.( modalOwner, mergedModuleAttrs, 'user' );

			getModalDispatch()?.setAttributes( {
				name: modalName,
				attributes: {
					...currentAttributes,
					...next,
				},
				merge: false,
			} );

			return;
		}

		getModalDispatch()?.setAttributes( {
			name: modalName,
			attributes: {
				...currentAttributes,
				...next,
			},
			merge: false,
		} );
	};
};

const SortingListRepeater = ( { attrs, updateAttrs, sortingDefaults }: SortingListRepeaterProps ): ReactElement => {
	const cardStyle: React.CSSProperties = {
		border: '1px solid #d9dee8',
		padding: '16px',
		marginBottom: '12px',
		borderRadius: '8px',
		background: '#fff',
		boxShadow: '0 1px 2px rgba(16, 24, 40, 0.04)',
	};

	const fieldWrapStyle: React.CSSProperties = {
		marginBottom: '12px',
	};

	const labelStyle: React.CSSProperties = {
		display: 'block',
		marginBottom: '6px',
		fontWeight: 500,
		color: '#344054',
	};

	const inputStyle: React.CSSProperties = {
		width: '100%',
		minHeight: '36px',
	};

	const removeButtonStyle: React.CSSProperties = {
		marginTop: '4px',
		color: '#b42318',
	};

	const addButtonStyle: React.CSSProperties = {
		marginTop: '4px',
	};

	const sortingListValue = getEffectiveAttrValue( {
		attrs,
		key: 'sorting_list',
		defaults: sortingDefaults,
		toggleKeys: SORTING_TOGGLE_ATTRS,
		selectKeys: SORTING_SELECT_ATTRS,
	} );
	const normalizedSortingList: SortingListItem[] = Array.isArray( sortingListValue )
		? sortingListValue
		: [];
	const [ sortingList, setSortingList ] = useState<SortingListItem[]>( normalizedSortingList );

	useEffect( () => {
		setSortingList( normalizedSortingList );
	}, [ JSON.stringify( normalizedSortingList ) ] );

	const updateSortingList = ( updated: SortingListItem[] ) => {
		setSortingList( updated );

		const next = {
			sorting_list: {
				innerContent: {
					desktop: {
						value: updated,
					},
				},
			},
		};

		updateAttrs( next );
	};

	const updateItem = (index: number, key: keyof SortingListItem, value: string) => {
		const updated = [...sortingList];
		updated[index] = {
			...updated[index],
			[key]: value,
		};
		updateSortingList(updated);
	};

	const addItem = () => {
		updateSortingList([
			...sortingList,
			{
				title: '',
				orderby: 'none',
				order: 'ASC',
				meta_key: '',
			},
		]);
	};

	const removeItem = (index: number) => {
		const updated = [...sortingList];
		updated.splice(index, 1);
		updateSortingList(updated);
	};

	return (
		<>
			{sortingList.map((item, index) => {
				const orderby = item?.orderby ?? 'none';
				const showMetaKey = ['meta_value', 'meta_value_num', 'clause_value'].includes(orderby);
				const showOrder = !['none', 'rand'].includes(orderby);

				return (
					<div
						key={index}
						style={{
							border: '1px solid #ddd',
							padding: '12px',
							marginBottom: '12px',
							borderRadius: '6px',
							background: '#f9f9f9',
						}}
					>
						<div style={fieldWrapStyle}>
							<label style={labelStyle}>{__('Title', 'jet-engine-divi-views')}</label>
							<input
								type="text"
								value={item?.title ?? ''}
								onChange={(event) => updateItem(index, 'title', event.target.value)}
								style={inputStyle}
							/>
						</div>

						<div style={fieldWrapStyle}>
							<label style={labelStyle}>{__('Order By', 'jet-engine-divi-views')}</label>
							<select value={orderby} onChange={(event) => updateItem(index, 'orderby', event.target.value)} style={inputStyle}>
								{getSortingOrderbySelectOptions().map((option) => (
									<option key={option.value} value={option.value}>{option.label}</option>
								))}
							</select>
						</div>

						{showMetaKey && (
							<div style={fieldWrapStyle}>
								<label style={labelStyle}>{__('Meta key', 'jet-engine-divi-views')}</label>
								<input
									type="text"
									value={item?.meta_key ?? ''}
									onChange={(event) => updateItem(index, 'meta_key', event.target.value)}
									style={inputStyle}
								/>
							</div>
						)}

						{showOrder && (
							<div style={fieldWrapStyle}>
								<label style={labelStyle}>{__('Order', 'jet-engine-divi-views')}</label>
								<select value={item?.order ?? 'ASC'} onChange={(event) => updateItem(index, 'order', event.target.value)} style={inputStyle}>
									{sortingOrderSelectOptions.map((option) => (
										<option key={option.value} value={option.value}>{option.label}</option>
									))}
								</select>
							</div>
						)}

						<button onClick={() => removeItem(index)} type="button" style={removeButtonStyle}>
							{__('Remove', 'jet-engine-divi-views')}
						</button>
					</div>
				);
			})}

			<button onClick={addItem} type="button" style={addButtonStyle}>
				{__('Add Item', 'jet-engine-divi-views')}
			</button>
		</>
	);
};

export const SettingsContent = (props: Module.Settings.Panel.Props<SortingAttrs>): ReactElement => {
	const {
		defaultSettingsAttrs,
		attrs,
	} = props;
	const setAttrs = (
		(props as unknown as { setAttrs?: (next: Partial<SortingAttrs>) => void; setAttributes?: (next: Partial<SortingAttrs>) => void }).setAttrs
		|| (props as unknown as { setAttributes?: (next: Partial<SortingAttrs>) => void; onChange?: (next: Partial<SortingAttrs>) => void }).setAttributes
		|| (props as unknown as { onChange?: (next: Partial<SortingAttrs>) => void }).onChange
	);

	const sortingDefaults = getBlockDefaultsFromSchema( 'sorting', SORTING_DEFAULTS_FALLBACK );
	const updateAttrs = createAttrsUpdater( setAttrs );

	useEffect( () => {
		if ( ! setAttrs ) {
			return;
		}

		const next = buildDefaultAttrsPatch( {
			attrs,
			defaults: sortingDefaults,
			toggleKeys: SORTING_TOGGLE_ATTRS,
			selectKeys: SORTING_SELECT_ATTRS,
		} );

		if ( Object.keys( next ).length ) {
			setAttrs( next );
		}
	}, [ attrs, setAttrs, sortingDefaults ] );

	const getToggleDefaultAttr = ( key: string ) => buildDefaultInnerContent( {
		key,
		value: sortingDefaults[ key ],
		toggleKeys: SORTING_TOGGLE_ATTRS,
		selectKeys: SORTING_SELECT_ATTRS,
	} );

	const getValue = ( key: string ) => getEffectiveAttrValue( {
		attrs,
		key,
		defaults: sortingDefaults,
		toggleKeys: SORTING_TOGGLE_ATTRS,
		selectKeys: SORTING_SELECT_ATTRS,
	} );

	const applyOn = getValue( 'apply_on' );
	const applyButton = isToggleEnabledValue( getValue( 'apply_button' ) );


	return (
		<React.Fragment>
			<GroupContainer
				id="general"
				title={__('General', 'jet-engine-divi-views')}
			>
				<FieldContainer
					attrName="content_provider.innerContent"
					label={__('This filter for', 'jet-engine-divi-views')}
					description=""
				>
					<SelectContainer options={getAvailableProviders()} />
				</FieldContainer>

				<FieldContainer
					attrName="apply_type.innerContent"
					label={__('Apply type', 'jet-engine-divi-views')}
					description=""
				>
					<SelectContainer
						options={convertSimpleOptions([
							{ value: 'ajax', label: __('AJAX', 'jet-engine-divi-views') },
							{ value: 'mixed', label: __('AJAX + page reload', 'jet-engine-divi-views') },
							{ value: 'reload', label: __('Page reload', 'jet-engine-divi-views') },
						])}
					/>
				</FieldContainer>

				<FieldContainer
					attrName="apply_on.innerContent"
					label={__('Apply on', 'jet-engine-divi-views')}
					description=""
				>
					<SelectContainer
						options={convertSimpleOptions([
							{ value: 'value', label: __('Value change', 'jet-engine-divi-views') },
							{ value: 'submit', label: __('Click on apply button', 'jet-engine-divi-views') },
						])}
					/>
				</FieldContainer>

				{applyOn === 'submit' && (
					<FieldContainer
					attrName="apply_button.innerContent"
					label={__('Show apply button', 'jet-engine-divi-views')}
					description=""
				>
						<ToggleContainer defaultAttr={ getToggleDefaultAttr( 'apply_button' ) } />
					</FieldContainer>
				)}

				{applyOn === 'submit' && applyButton && (
					<FieldContainer
						attrName="apply_button_text.innerContent"
						label={__('Apply button text', 'jet-engine-divi-views')}
						description=""
					>
						<TextContainer />
					</FieldContainer>
				)}

				<FieldContainer
					attrName="sorting_label.innerContent"
					label={__('Label', 'jet-engine-divi-views')}
					description=""
				>
					<TextContainer />
				</FieldContainer>

				<FieldContainer
					attrName="sorting_placeholder.innerContent"
					label={__('Placeholder', 'jet-engine-divi-views')}
					description=""
				>
					<TextContainer />
				</FieldContainer>

				<FieldContainer
					attrName="query_id.innerContent"
					label={__('Query ID', 'jet-engine-divi-views')}
					description={__(
						'Set unique query ID if you use multiple blocks of same provider on the page.',
						'jet-engine-divi-views'
					)}
				>
					<TextContainer />
				</FieldContainer>
			</GroupContainer>

			<GroupContainer
				id="sorting-list"
				title={__('Sorting List', 'jet-engine-divi-views')}
			>
				<SortingListRepeater attrs={attrs} updateAttrs={updateAttrs} sortingDefaults={sortingDefaults} />
			</GroupContainer>

			<AdminLabelGroup
				defaultGroupAttr={defaultSettingsAttrs?.module?.meta?.adminLabel}
			/>
		</React.Fragment>
	);
};
