import type { Query } from '@directus/types';
import { isEqual, uniqWith } from 'lodash-es';
import type { FieldKey } from '../types.js';
import { flattenFilter } from './flatten-filter.js';

/**
 * Converts the passed Query object into a unique list of path arrays, for example:
 *
 * ```
 * [
 * 	['author', 'age'],
 * 	['category']
 * ]
 * ```
 */
export function extractPathsFromQuery(query: Query) {
	/**
	 * All nested paths used in the current query scope.
	 * This is generated by flattening the filters and adding in the used sort/aggregate fields.
	 */
	const paths: FieldKey[][] = [];
	const readOnlyPaths: FieldKey[][] = [];

	if (query.filter) {
		flattenFilter(readOnlyPaths, query.filter);
	}

	if (query.sort) {
		for (const field of query.sort) {
			// Sort can have dot notation fields for sorting on m2o values Sort fields can start with
			// `-` to indicate descending order, which should be dropped for permissions checks

			const parts = field.split('.').map((field) => (field.startsWith('-') ? field.substring(1) : field));

			if (query.aggregate && parts.length > 0 && parts[0]! in query.aggregate) {
				// If query is an aggregate query and the first part is a requested aggregate operation, ignore the whole field.
				// The correct field is extracted into the field map when processing the `query.aggregate` fields.
				continue;
			}

			readOnlyPaths.push(parts);
		}
	}

	if (query.aggregate) {
		for (const fields of Object.values(query.aggregate)) {
			for (const field of fields) {
				if (field === '*') {
					// Don't add wildcard field to the paths
					continue;
				}

				// Aggregate doesn't currently support aggregating on nested fields, but it doesn't hurt
				// to standardize it in the validation layer
				paths.push(field.split('.'));
			}
		}
	}

	if (query.group) {
		for (const field of query.group) {
			// Grouping doesn't currently support grouping on nested fields, but it doesn't hurt to
			// standardize it in the validation layer
			paths.push(field.split('.'));
		}
	}

	return {
		paths: uniqWith(paths, isEqual),
		readOnlyPaths: uniqWith(readOnlyPaths, isEqual),
	};
}
