import { Col, Input, Row } from 'antd';
// ** Constants
import {
	initialAggregateAttribute,
	initialQueryBuilderFormValues,
	mapOfFilters,
	mapOfOperators,
} from 'constants/queryBuilder';
// ** Components
import {
	AdditionalFiltersToggler,
	DataSourceDropdown,
	FilterLabel,
	ListMarker,
} from 'container/QueryBuilder/components';
import {
	AggregatorFilter,
	GroupByFilter,
	OperatorsSelect,
	ReduceToFilter,
} from 'container/QueryBuilder/filters';
import LimitFilter from 'container/QueryBuilder/filters/LimitFilter/LimitFilter';
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
import { useQueryBuilder } from 'hooks/useQueryBuilder';
import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator';
// ** Hooks
import React, { memo, useCallback, useMemo } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import { transformToUpperCase } from 'utils/transformToUpperCase';

// ** Types
import { QueryProps } from './Query.interfaces';
// ** Styles
import { StyledDeleteEntity, StyledRow } from './Query.styled';

export const Query = memo(function Query({
	index,
	isAvailableToDisable,
	queryVariant,
	query,
	panelType,
}: QueryProps): JSX.Element {
	const {
		handleSetQueryData,
		removeEntityByIndex,
		initialDataSource,
	} = useQueryBuilder();

	const currentListOfOperators = useMemo(
		() => mapOfOperators[query.dataSource],
		[query],
	);
	const listOfAdditionalFilters = useMemo(() => mapOfFilters[query.dataSource], [
		query,
	]);

	const handleChangeOperator = useCallback(
		(value: string): void => {
			const aggregateDataType: BaseAutocompleteData['dataType'] =
				query.aggregateAttribute.dataType;

			const newQuery: IBuilderQueryForm = {
				...query,
				aggregateOperator: value,
				having: [],
				limit: null,
			};

			if (!aggregateDataType || query.dataSource === DataSource.METRICS) {
				handleSetQueryData(index, newQuery);
				return;
			}

			switch (aggregateDataType) {
				case 'string':
				case 'bool': {
					const typeOfValue = findDataTypeOfOperator(value);

					handleSetQueryData(index, {
						...newQuery,
						...(typeOfValue === 'number'
							? { aggregateAttribute: initialAggregateAttribute }
							: {}),
					});

					break;
				}
				case 'float64':
				case 'int64': {
					handleSetQueryData(index, newQuery);

					break;
				}

				default: {
					handleSetQueryData(index, newQuery);
					break;
				}
			}
		},
		[index, query, handleSetQueryData],
	);

	const handleChangeAggregatorAttribute = useCallback(
		(value: BaseAutocompleteData): void => {
			const newQuery: IBuilderQueryForm = {
				...query,
				aggregateAttribute: value,
			};

			handleSetQueryData(index, newQuery);
		},
		[index, query, handleSetQueryData],
	);

	const handleChangeDataSource = useCallback(
		(nextSource: DataSource): void => {
			let newQuery: IBuilderQueryForm = {
				...query,
				dataSource: nextSource,
			};

			if (nextSource !== query.dataSource) {
				const initCopy = {
					...(initialQueryBuilderFormValues as Partial<IBuilderQueryForm>),
				};
				delete initCopy.queryName;

				newQuery = {
					...newQuery,
					...initCopy,
					dataSource: initialDataSource || nextSource,
					aggregateOperator: mapOfOperators[nextSource][0],
				};
			}

			handleSetQueryData(index, newQuery);
		},
		[index, query, initialDataSource, handleSetQueryData],
	);

	const handleToggleDisableQuery = useCallback((): void => {
		const newQuery: IBuilderQueryForm = {
			...query,
			disabled: !query.disabled,
		};

		handleSetQueryData(index, newQuery);
	}, [index, query, handleSetQueryData]);

	const handleChangeGroupByKeys = useCallback(
		(values: BaseAutocompleteData[]): void => {
			const newQuery: IBuilderQueryForm = {
				...query,
				groupBy: values,
			};

			handleSetQueryData(index, newQuery);
		},
		[index, query, handleSetQueryData],
	);

	const handleChangeQueryLegend = useCallback(
		(e: React.ChangeEvent<HTMLInputElement>): void => {
			const newQuery: IBuilderQueryForm = {
				...query,
				legend: e.target.value,
			};
			handleSetQueryData(index, newQuery);
		},
		[index, query, handleSetQueryData],
	);

	const handleChangeReduceTo = useCallback(
		(value: string): void => {
			const newQuery: IBuilderQueryForm = {
				...query,
				reduceTo: value,
			};
			handleSetQueryData(index, newQuery);
		},
		[index, query, handleSetQueryData],
	);

	const handleDeleteQuery = useCallback(() => {
		removeEntityByIndex('queryData', index);
	}, [removeEntityByIndex, index]);

	const isMatricsDataSource = useMemo(
		() => query.dataSource === DataSource.METRICS,
		[query.dataSource],
	);

	const handleChangeLimit = useCallback(
		(value: number | null): void => {
			const newQuery: IBuilderQueryForm = {
				...query,
				limit: value,
			};
			handleSetQueryData(index, newQuery);
		},
		[index, query, handleSetQueryData],
	);

	return (
		<StyledRow gutter={[0, 15]}>
			<StyledDeleteEntity onClick={handleDeleteQuery} />
			<Col span={24}>
				<Row align="middle" justify="space-between">
					<Col>
						<ListMarker
							isDisabled={query.disabled}
							toggleDisabled={handleToggleDisableQuery}
							labelName={query.queryName}
							index={index}
							isAvailableToDisable={isAvailableToDisable}
						/>
						{queryVariant === 'dropdown' ? (
							<DataSourceDropdown
								onChange={handleChangeDataSource}
								value={query.dataSource}
								style={{ marginRight: '0.5rem' }}
							/>
						) : (
							<FilterLabel label={transformToUpperCase(query.dataSource)} />
						)}
						{isMatricsDataSource && <FilterLabel label="WHERE" />}
					</Col>
					<Col span={isMatricsDataSource ? 17 : 20}>
						<QueryBuilderSearch query={query} />
					</Col>
				</Row>
			</Col>
			<Col span={11}>
				<Row gutter={[11, 5]}>
					<Col flex="5.93rem">
						<OperatorsSelect
							value={query.aggregateOperator || currentListOfOperators[0]}
							onChange={handleChangeOperator}
							operators={currentListOfOperators}
						/>
					</Col>
					<Col flex="1 1 12.5rem">
						<AggregatorFilter
							onChange={handleChangeAggregatorAttribute}
							query={query}
						/>
					</Col>
				</Row>
			</Col>
			<Col span={11} offset={2}>
				<Row gutter={[11, 5]}>
					<Col flex="5.93rem">
						<FilterLabel label={panelType === 'VALUE' ? 'Reduce to' : 'Group by'} />
					</Col>
					<Col flex="1 1 12.5rem">
						{panelType === 'VALUE' ? (
							<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
						) : (
							<GroupByFilter query={query} onChange={handleChangeGroupByKeys} />
						)}
					</Col>
				</Row>
			</Col>
			<Col span={24}>
				<AdditionalFiltersToggler listOfAdditionalFilter={listOfAdditionalFilters}>
					{!isMatricsDataSource && (
						<Row gutter={[11, 5]}>
							<Col span={6}>
								<FilterLabel label="Limit" />
							</Col>
							<Col span={18}>
								<LimitFilter query={query} onChange={handleChangeLimit} />
							</Col>
						</Row>
					)}
				</AdditionalFiltersToggler>
			</Col>
			<Row style={{ width: '100%' }}>
				<Input
					onChange={handleChangeQueryLegend}
					size="middle"
					value={query.legend}
					addonBefore="Legend Format"
				/>
			</Row>
		</StyledRow>
	);
});
