"use strict";

const matchValue = require("match-value");

const operations = require("../operations");
const internalOperations = require("../internal-operations");
const typeOf = require("../type-of");
const unreachable = require("../unreachable");
const NoChange = require("./util/no-change");

// FIXME: anyOf(not(placeholder)) should be disallowed, but probably currently is allowed
// not(anyOf(placeholder)) is fine though

let unprocessableTypes = new Set([ "sqlExpression", "placeholder" ]);

module.exports = {
	name: "conditions-to-expressions",
	category: [ "normalization" ],
	visitors: {
		expression: (rootNode) => {
			if (rootNode.condition.type === "condition") { // FIXME: typeOf
				return NoChange;
			} else {
				// anyOfConditions, allOfConditions, notCondition

				function convertNode(node) {
					let listOperation = matchValue.literal(typeOf(node), {
						anyOfConditions: operations.anyOf,
						allOfConditions: operations.allOf,
						notCondition: null,
						condition: null
					});

					if (listOperation != null) {
						if (Array.isArray(node.items)) {
							return listOperation(node.items.map((item) => convertNode(item)));
						} else if (unprocessableTypes.has(typeOf(node.items))) {
							// Placeholders / unsafe SQL expressions are not representable as a series of expressions, as we don't know the value of them upfront. Therefore we immediately convert it to an internal ANY/ALL expression here, so that it doesn't accidentally get touched by anything else afterwards.
							// FIXME: Check the relevance here of node.items.parameterizable for placeholders
		
							return operations.expression({
								left: rootNode.left,
								condition: internalOperations._condition({
									type: "equals",
									expression: internalOperations._arrayOf({
										type: matchValue(typeOf(node), {
											anyOfConditions: "anyOf",
											allOfConditions: "allOf"
										}),
										canBeParameterized: true,
										items: node.items
									})
								})
							});
						} else {
							unreachable("Condition item was not an array");
						}
					} else if (typeOf(node) === "notCondition") {
						return operations.not(convertNode(node.condition));
					} else if (typeOf(node) === "condition") {
						return operations.expression({
							left: rootNode.left,
							condition: node
						});
					} else {
						unreachable(`Encountered node type '${node.type}' within condition modifier`);
					}
				}

				return convertNode(rootNode.condition);
			}
		}
	}
};
