/**
 * WordPress provides React during the page load. This is for TypeScript type checking only.
 *
 * Importing React from @wordpress/element causes "Cannot read property 'createElement' of undefined"
 */
import React, { useState, Fragment } from 'react';
import { Button, Modal, BaseControl } from '@wordpress/components';
import { plusCircle } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import produce from 'immer';
import { availableOperators } from './FilterControl/FilterOperatorControl';
import { FilterControlGroup } from './FilterControlGroup';
import { FilterControl } from './FilterControl/FilterControl';

export type FilterOperator = keyof typeof availableOperators;

export interface Filter {
	property: string;
	operator: FilterOperator;
	value: string;
}

export type FilterGroup = Filter[];

export type Filters = FilterGroup[];

export interface FilterProperty {
	label: string;
	value: string;
	group?: string;
	orderby: boolean;
}

export interface FilterPropertyGroups {
	[ groupId: string ]: {
		label: string;
	};
}

export interface FilterProperties {
	[ propertyId: string ]: FilterProperty;
}

export type FilterPropertyValues = string[];

export const FiltersControlContext = React.createContext(
	{}
) as React.Context< {
	operators: FilterOperator[];
	addFilter: ( index: number, groupIndex: number ) => void;
	removeFilter: ( index: number, groupIndex: number ) => void;
	updateFilter: ( index: number, groupIndex: number, filter: Filter ) => void;
	properties: FilterProperties;
	getPropertyValues: ( propertyId: string ) => FilterPropertyValues;
	propertyGroups: FilterPropertyGroups;
} >;

export interface FiltersControlProps {
	label: string;
	help?: string;
	value: Filters;
	onChange: ( value ) => void;
	operators?: ( keyof typeof availableOperators )[];
	properties: FilterProperties;
	getPropertyValues: ( propertyId: string ) => FilterPropertyValues;
	propertyGroups: FilterPropertyGroups;
}

export function FiltersControl( {
	value,
	onChange,
	operators,
	help,
	label,
	properties,
	getPropertyValues,
	propertyGroups,
}: FiltersControlProps ): JSX.Element {
	const [ isOpen, setOpen ] = useState( false );
	const openModal = () => setOpen( true );
	const closeModal = () => setOpen( false );

	if ( ! operators ) {
		operators = Object.keys(
			availableOperators
		) as FiltersControlProps[ 'operators' ];
	}

	const filterTemplate: Filter = {
		property: '',
		operator: operators[ 0 ],
		value: '',
	};
	const filterGroupTemplate: FilterGroup = [ filterTemplate ];

	const addFilterGroup = () => {
		const nextValue = produce( value, ( draft ) => {
			draft.push( filterGroupTemplate );
		} );

		onChange( nextValue );
	};

	const addFilter = ( index: number, groupIndex: number ) => {
		const nextValue = produce( value, ( draft ) => {
			draft[ groupIndex ].splice( index + 1, 0, filterTemplate );
		} );

		onChange( nextValue );
	};

	const removeFilter = ( index: number, groupIndex: number ) => {
		const nextValue = produce( value, ( draft ) => {
			draft[ groupIndex ].splice( index, 1 );

			if ( draft[ groupIndex ].length === 0 ) {
				draft.splice( groupIndex, 1 );
			}
		} );

		onChange( nextValue );
	};

	const updateFilter = (
		index: number,
		groupIndex: number,
		filter: Filter
	) => {
		const nextValue = produce( value, ( draft ) => {
			draft[ groupIndex ][ index ] = filter;
		} );

		onChange( nextValue );
	};

	const filterCount = value?.reduce<number>( ( count, filterGroup ) => {
		count = count + filterGroup.length;

		return count;
	}, 0 );

	return (
		<BaseControl id={ label } label={ label } help={ help }>
			<FiltersControlContext.Provider
				value={ {
					operators,
					properties,
					getPropertyValues,
					propertyGroups,
					addFilter,
					removeFilter,
					updateFilter,
				} }
			>
				<div style={ { margin: '.75rem 0 0' } }>
					<Button
						variation="secondary"
						isSecondary={ true }
						onClick={ openModal }
					>
						{ value?.length ? __( 'Edit Filters', 'gp-entry-blocks' ) : __( 'Add Filters', 'gp-entry-blocks' ) }
						{ value?.length ? <span className="gpeb-badge">{ filterCount }</span> : undefined }
					</Button>
				</div>

				{ isOpen && (
					<Modal title="Filters" onRequestClose={ closeModal }>
						{ value &&
							value.map( ( filterGroup, filterGroupIndex ) => (
								<Fragment key={ filterGroupIndex }>
									<FilterControlGroup>
										{ filterGroup.map(
											( filter, filterIndex ) => (
												<Fragment key={ filterIndex }>
													<FilterControl
														index={ filterIndex }
														groupIndex={
															filterGroupIndex
														}
														property={
															filter.property
														}
														operator={
															filter.operator
														}
														value={ filter.value }
													/>

													{ filterGroup.length > 1 && filterIndex < filterGroup.length - 1 && (
														<div className="gpeb-filter-and">
															{ __( 'AND', 'gp-entry-blocks' ) }
														</div>
													) }
												</Fragment>
											)
										) }
									</FilterControlGroup>

									{ value.length > 1 &&
										filterGroupIndex < value.length - 1 && (
										<div className="gpeb-filter-group-or">
											— { __( 'OR', 'gp-entry-blocks' ) } —
										</div>
									) }
								</Fragment>
							) ) }

						<Button
							onClick={ addFilterGroup }
							variant="secondary"
							isSecondary={ true }
							icon={ plusCircle }
							style={ { marginTop: '1rem' } }
						>
							{ __( 'Add Filter Group', 'gp-entry-blocks' ) }
						</Button>
					</Modal>
				) }
			</FiltersControlContext.Provider>
		</BaseControl>
	);
}
