import { useEffect, useState, useCallback, useRef } from '@wordpress/element';
import { ComboboxControl, TextControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useSelect, useDispatch, select } from '@wordpress/data';
import { isObject, debounce } from 'lodash';
import { Fragment } from "react";

import { defaultOptions } from "./default-options";
import { loadOptions } from './load-options.js';
import { prepareFormData } from "./prepare-form-data";

const PICKUP_POINT_HANDLERS_KEY = '__octolizePickupPointHandlers';
const PICKUP_POINT_SETTER_NAME = 'setOctolizePickupPoint';

const getPickupPointGlobal = () => {
	if ( typeof window !== 'undefined' ) {
		return window;
	}

	if ( typeof globalThis !== 'undefined' ) {
		return globalThis;
	}

	return null;
};

const getPickupPointHandlers = () => {
	const globalObject = getPickupPointGlobal();

	if ( ! globalObject ) {
		return {};
	}

	if ( ! globalObject[ PICKUP_POINT_HANDLERS_KEY ] ) {
		globalObject[ PICKUP_POINT_HANDLERS_KEY ] = {};
	}

	return globalObject[ PICKUP_POINT_HANDLERS_KEY ];
};

const registerPickupPointSetter = () => {
	const globalObject = getPickupPointGlobal();

	if ( ! globalObject || globalObject[ PICKUP_POINT_SETTER_NAME ] ) {
		return;
	}

	globalObject[ PICKUP_POINT_SETTER_NAME ] = ( pointId, pointLabel, integration ) => {
		const handler = getPickupPointHandlers()[ integration ];

		if ( ! handler ) {
			return false;
		}

		handler.setSelectedPoint( pointId, pointLabel );

		return true;
	};
};

registerPickupPointSetter();

const PickupPointBlock = (
	{
		checkoutExtensionData,
		cart,
		metadata,
		showFieldCallback,
		requiredFieldCallback,
		formDataCallback,
		selectPointFromMapCallback,
		pickup_point_data_renderer
	} ) => {

	const controlRef = useRef();

	const { extensionCartUpdate } = wc.blocksCheckout;

	const settings = wcSettings[ metadata.settingsKey ];

	const autoloadOptions = metadata.autoloadOptions ?? false;

	const { extensionData, setExtensionData } = checkoutExtensionData;

	const validationErrorId = settings.integrationName + '-' + settings.fieldName;

	const { setValidationErrors, clearValidationError } = useDispatch(
		'wc/store/validation'
	);

	const { __internalIncrementCalculating, __internalDecrementCalculating } = useDispatch( 'wc/store/checkout' );

	const [ showField, setShowField ] = useState( false );

	const getSelectedFlexibleShippingMethods = ( selectedRates ) => {
		let selectedFlexibleShippingMethods = [];
		selectedRates.map( ( rate ) => {
			rate.meta_data.some( ( meta ) => {
				if ( meta.key === '_fs_method' && meta.value.method_integration === settings.flexibleShippingIntegration ) {
					selectedFlexibleShippingMethods.push( meta.value );
				}
				return false;
			} );
		} )

		return selectedFlexibleShippingMethods;
	}

	const getSelectedRates = () => {
		let selectedRates = [];
		const shippingRates = cart.shippingRates;

		shippingRates.map( ( shipping_package ) => {
			shipping_package.shipping_rates.filter( ( rate ) => {
				if ( rate.selected ) {
					selectedRates.push( rate );
				}
			} );
		} );

		return selectedRates;
	}

	const [ selectedMethods, setSelectedMethods ] = useState( [] );

	const [ requiredField, setRequiredField ] = useState( false );

	const [
		pointId,
		setPointId,
	] = useState( wcSettings.checkoutData.extensions[ settings.integrationName ][ settings.fieldName ] ?? '' );

	const disableListSelection = wcSettings.checkoutData.extensions[ settings.integrationName ]['disable_list_selection'] ?? false;
	const disableMapSelection = wcSettings.checkoutData.extensions[ settings.integrationName ]['disable_map_selection'] ?? false;
	const hideSelectedPointField = wcSettings.checkoutData.extensions[ settings.integrationName ]['hide_selected_point_field'] ?? false;

	useEffect( () => {
		let selectedRates = getSelectedRates();
		let selectedFlexibleShippingMethods = getSelectedFlexibleShippingMethods(selectedRates);

		setSelectedMethods( selectedFlexibleShippingMethods );

		setShowField( showFieldCallback( selectedFlexibleShippingMethods, selectedRates ) );
		setRequiredField( requiredFieldCallback( selectedFlexibleShippingMethods, selectedRates ) );

	}, [ ...cart.shippingRates ] );

	const validationError = useSelect( ( select ) => {
		const store = select( 'wc/store/validation' );

		return store.getValidationError( validationErrorId );
	} );

	const fieldLabel = wcSettings.checkoutData.extensions[ settings.integrationName ]['field_label'] ?? __( 'Selected pickup point', 'octolize-pickup-point-checkout-blocks' );

	const selectFromMapLabel = wcSettings.checkoutData.extensions[ settings.integrationName ]['button_label'] ?? __( 'Select from map', 'octolize-pickup-point-checkout-blocks' );
	const showComboboxField = ! disableListSelection;
	const showReadonlyTextField = disableListSelection && ! hideSelectedPointField;
	const showMapSelectionAction = Boolean( selectPointFromMapCallback ) && ! disableMapSelection;
	const showMapActionValidationError = disableListSelection && hideSelectedPointField && validationError?.hidden === false;
	const PickupPointDataRenderer = pickup_point_data_renderer;
	const showPickupPointDataRenderer = Boolean( PickupPointDataRenderer ) && showMapSelectionAction;

	useEffect( () => {
		setExtensionData( settings.integrationName, settings.fieldName, pointId );

		if ( showField && requiredField && pointId.length === 0 ) {
			setValidationErrors( {
				[ validationErrorId ]: {
					message: fieldLabel,
					hidden: false,
				},
			} );
		} else {
			clearValidationError( validationErrorId );
		}

	}, [ pointId ] );

	useEffect( () => {
		if ( ! showField || ! requiredField ) {
			clearValidationError( validationErrorId );
		}

	}, [ ...cart.shippingRates, requiredField  ] );

	const [ filteredValue, setFilteredValue ] = useState( '' );

	const loadOptionsSettings = ( searchString, setPointId, pointId ) => {
		return {
			searchString: searchString,
			ajaxUrl: settings.ajaxUrl,
			formData: formDataCallback(
				prepareFormData( {
					searchString: searchString,
					ajaxAction: settings.ajaxAction,
					nonce: settings.nonce
				} ),
				selectedMethods,
				cart.shippingAddress
			),
			setFilteredOptions: setFilteredOptions,
			setFilteredValue: setFilteredValue,
			setPointId: setPointId,
			pointId: pointId,
		}
	};

	const [ filteredOptions, setFilteredOptions ] = useState( autoloadOptions ? [] : ( wcSettings.checkoutData.extensions[ settings.integrationName ]['default_options'] ?? defaultOptions ) );

	const pointIdChanged = useCallback( ( pointId ) => {
		setPointId( pointId );
		setExtensionData( settings.integrationName, settings.fieldName, pointId );
		__internalIncrementCalculating();
		extensionCartUpdate( {
			namespace: settings.integrationName,
			data: {
				point_id: pointId,
			},
		} ).finally( () => { __internalDecrementCalculating() } );
	}, [
		__internalDecrementCalculating,
		__internalIncrementCalculating,
		extensionCartUpdate,
		setExtensionData,
		settings.fieldName,
		settings.integrationName,
	] );

	const setPickupPointFromExternal = useCallback( ( externalPointId, externalPointLabel ) => {
		const normalizedPointId = String( externalPointId ?? '' );
		const normalizedPointLabel = String( externalPointLabel ?? '' );

		setFilteredOptions( [
			{
				label: normalizedPointLabel,
				value: normalizedPointId,
			},
		] );
		setFilteredValue( normalizedPointLabel );
		pointIdChanged( normalizedPointId );
	}, [ pointIdChanged ] );

	useEffect( () => {
		const handlers = getPickupPointHandlers();
		const handler = {
			setSelectedPoint: setPickupPointFromExternal,
		};

		handlers[ settings.integrationName ] = handler;

		return () => {
			if ( handlers[ settings.integrationName ] === handler ) {
				delete handlers[ settings.integrationName ];
			}
		};
	}, [ setPickupPointFromExternal, settings.integrationName ] );

	const debouncedLoadOptions = useCallback(
		debounce( ( loadOptionsSettings ) => {
			loadOptions( loadOptionsSettings );
		}, 500 ),
		[ loadOptions ]
	);

	useEffect( () => {
		if ( showField && autoloadOptions ) {
			debouncedLoadOptions( loadOptionsSettings( '' ) );
		}
	}, [ ...cart.shippingRates ] );

	if ( showField && autoloadOptions && filteredOptions.length === 0 ) {
		debouncedLoadOptions( loadOptionsSettings( '', setPointId, pointId ) );
	}

	const setPointSelectedFromMap = ( point ) => {
		setPickupPointFromExternal( point.id, point.label );
	}

	const selectPontFromMapAction = (e) => {
		e.preventDefault();
		selectPointFromMapCallback(
			{
				pointId: pointId,
				address: cart.shippingAddress,
				setPointSelectedFromMap: setPointSelectedFromMap,
				cart: cart,
			}
		);
	}

	return (
		<>
			{ showField && <>
				{ showComboboxField &&
					<div className="wc-block-components-combobox is-active">
						<ComboboxControl
							className={ 'wc-block-components-combobox-control' +
								( validationError?.hidden === false
									? ' has-error'
									: '' ) }
							label={ fieldLabel }
							value={ pointId }
							required={ requiredField }
							options={ filteredOptions }
							filteredValue={ filteredValue }
							onFilterValueChange={ ( filterValue ) => {
								if ( filterValue.length ) {
									const activeElement = isObject( controlRef.current )
										? controlRef.current.ownerDocument.activeElement
										: undefined;

									if (
										activeElement &&
										isObject( controlRef.current ) &&
										controlRef.current.contains( activeElement )
									) {
										return;
									}

									if ( ! autoloadOptions ) {
										if ( filterValue.length < 3 ) {
											setFilteredOptions( defaultOptions );
											return;
										}
										debouncedLoadOptions( loadOptionsSettings( filterValue ) );
									}
								}
							} }
							onChange={ pointIdChanged }
							allowReset={ false }
							aria-invalid={ validationError?.message && ! validationError?.hidden }
						/>
						{ validationError?.hidden === false &&
							<div className="wc-block-components-validation-error" role="alert"
								 style={ { 'marginTop': '20px' } }>
								<p id={ validationErrorId }>{ validationError?.message }</p>
							</div>
						}
					</div>
				}
				{ showReadonlyTextField &&
					<div className="wc-block-components-text-input is-active">
						<TextControl
							className={ 'wc-block-components-text-input' +
								( validationError?.hidden === false
									? ' has-error'
									: '' ) }
							value={ filteredOptions[0]?.label }
							label={ fieldLabel }
							readOnly={ true }
							onClick={ (e) => {selectPontFromMapAction(e) } }
							aria-invalid={ validationError?.message && ! validationError?.hidden }
						/>
						{ validationError?.hidden === false &&
							<div className="wc-block-components-validation-error" role="alert"
								 style={ { 'marginTop': '20px' } }>
								<p id={ validationErrorId }>{ validationError?.message }</p>
							</div>
						}
					</div>
				}
				{ showMapSelectionAction && <>
					<div className="wc-block-checkout__actions_row" style={ { 'marginTop': showReadonlyTextField ? '20px' : '40px', 'marginLeft': '10px' } }>
						<button className="wc-block-components-button wp-element-button is-link" onClick={ selectPontFromMapAction }>{selectFromMapLabel}</button>
					</div>
				</> }
				{ showPickupPointDataRenderer &&
					<div style={ { 'marginTop': '12px', 'marginLeft': '10px' } }>
						<PickupPointDataRenderer
							pointId={ pointId }
						/>
					</div>
				}
				{ showMapActionValidationError &&
					<div className="wc-block-components-validation-error" role="alert"
						 style={ { 'marginTop': '20px', 'marginLeft': '10px' } }>
						<p id={ validationErrorId }>{ validationError?.message }</p>
					</div>
				}
			</> }
		</>
	);
};

export { PickupPointBlock };
