"use strict";

const { validateArguments } = require("@validatem/core");
const required = require("@validatem/required");
const either = require("@validatem/either");

const node = require("../ast-node");
const unreachable = require("../unreachable");
const tagAsType = require("../validators/tag-as-type");

module.exports = function (operations) {
	const isExpression = require("../validators/operations/is-expression")(operations);
	const isCondition = require("../validators/operations/is-condition")(operations);
	const isWhereObject = require("../validators/operations/is-where-object")(operations);
	
	return function not(_expression) {
		let [ expression ] = validateArguments(arguments, {
			expression: [ required, either([
				[ isExpression, tagAsType("expression") ],
				[ isCondition, tagAsType("condition") ],
				// FIXME: Clearly document that the below means not(allOf(...)), rather than not(anyOf(...))! This makes sense both logically and pragmatically (eg. it is analogous to "does not match composite key", and the behaviour is consistent with `where({ ... })`), but it may mismatch initial expectations. For not(anyOf(...)), suggest explicit use of `anyOf` and multiple objects.
				[ isWhereObject, tagAsType("expression") ],
			])]
		});

		// FIXME: rename expression to something clearer, that indicates its relationship to conditions?

		if (expression.type === "expression") {
			return node({ type: "notExpression", expression: expression.value });
		} else if (expression.type === "condition") {
			return node({ type: "notCondition", condition: expression.value });
		} else {
			unreachable(`Invalid tagged type '${expression.type}'`);
		}
	};
};
