"use strict";

// FIXME: Validation for all methods
// FIXME: For validation of things where there are defaults/implicits, make sure to strictly validate either the allowed operations OR the allowed implicit values (eg. `columnName` implicits should only accept strings) - these validations can be abstracted out per type of implicit
// FIXME: Upon query compilation, keep a 'stack' of operation types, to track what context we are in (eg. because values can only be parameterized in some contexts)

// FIXME: Verify that all wrapWith calls have a corresponding isObjectType arm

require("array.prototype.flat").shim();
const node = require("../ast-node");

const evaluateCyclicalModulesOnto = require("../evaluate-cyclical-modules-onto");

// NOTE: withDerived has been replaced with compute

// FIXME: All of the below need to be refactored, and moved into operation modules
let operations = {
	withRelations: function (relations) {
		// FIXME: Default relation types for literal column names and table.column syntax
		// FIXME: Flesh this out further
		// Main types: hasMany, belongsTo; hasOne is just hasMany with a 'single' constraint; simplify hasMany to 'has'?
		// through relation is actually hasMany(through(...)) -- how to make `through` itself composable?
		// test case for composable through: user -> membership -> usergroup -> community
		// should fold composed throughs in compiler step

		return node({
			type: "withRelations",
			relations: Object.entries(relations).map(([ key, relation ]) => {
				return {
					key: key,
					relation: normalizeRelation(relation)
				};
			})
		});
	}
};

let operationModules = {
	// Base operations
	select: require("./select"),

	// Field selection
	addFields: require("./add-fields"),
	onlyFields: require("./only-fields"),
	alias: require("./alias"),

	// Reference/scalar types
	field: require("./field"),
	remoteField: require("./remote-field"),
	collection: require("./collection"),
	value: require("./value"),

	// Filtering
	where: require("./where"),
	expression: require("./expression"),

	// Predicate lists/combinators
	allOf: require("./all-of"),
	anyOf: require("./any-of"),
	
	// Conditions
	equals: require("./equals"),
	lessThan: require("./less-than"),
	moreThan: require("./more-than"),
	not: require("./not"),

	// Collapsing/grouping
	collapseBy: require("./collapse-by"),
	hierarchical: require("./hierarchical"),

	// Computation
	compute: require("./compute"),
	
	// Aggregrate functions
	count: require("./count"),
	sum: require("./sum"),
	
	// Relations and subqueries
	belongsTo: require("./relations/belongs-to"),
	has: require("./relations/has"),
	through: require("./relations/through"),
	withRelations: require("./relations/with-relations"),
	define: require("./relations/define"),
	resultExists: require("./relations/result-exists"), // FIXME: Better name?
	linkTo: require("./relations/link-to"),
	
	// Misc.
	parameter: require("./parameter"),
	postProcess: require("./post-process"),
	unsafeSQL: require("./unsafe-sql"),
	now: require("./functions/now"),

	// Schema definitions
	createCollection: require("./schema/create-collection"),
	changeCollection: require("./schema/change-collection"),
	deleteCollection: require("./schema/delete-collection"),
	fields: require("./schema/fields"),
	indexes: require("./schema/indexes"),
	deleteField: require("./schema/delete-field"),
	restoreAs: require("./schema/restore-as"),
	optional: require("./schema/optional"),
	defaultTo: require("./schema/default-to"),
	
	// Field types
	autoID: require("./types/auto-id"),
	boolean: require("./types/boolean"),
	string: require("./types/string"),
	uuid: require("./types/uuid"),
	timestamp: require("./types/timestamp"),
	
	// Index types
	primaryKey: require("./indexes/primary-key"),
	index: require("./indexes/index"),
	indexWhere: require("./indexes/index-where"),
	unique: require("./indexes/unique"),
	uniqueWhere: require("./indexes/unique-where"),
};

Object.assign(module.exports, operations);
evaluateCyclicalModulesOnto(module.exports, operationModules);

// NOTE: normalizeExpression should sometimes only accept sql/literal, but sometimes also sql/literal/condition?
