"use strict";

const NoChange = require("./util/no-change");
const deriveNode = require("../derive-node");
const operations = require("../operations");

module.exports = {
	name: "test-context",
	category: [ "testing" ],
	visitors: {
		field: (node, { setState }) => {
			setState("seenField", node.name);

			return NoChange;
		},
		select: (node, { registerStateHandler, defer }) => {
			let seenFields = new Set();

			registerStateHandler("seenFieldsInWhere", (names) => {
				for (let name of names) {
					seenFields = seenFields.add(name);
				}
			});
			
			// FIXME: Definitely need better AST modification/derivation tools... probably some sort of deep-modifying utility, for starters. Maybe merge-by-template can be of use here? With a custom AST node merger? It probably doesn't support non-enumerable properties correctly right now, though...

			return defer(() => {
				// console.log("Seen fields in WHERE in SELECT:", seenFields);

				let onlyFieldsClause = node.clauses.find((clause) => clause.type === "onlyFields");

				let fieldsAlreadyAdded = onlyFieldsClause != null && Array.from(seenFields).every((field) => {
					return onlyFieldsClause.fields.some((existingField) => existingField.name === field);
				});

				if (!fieldsAlreadyAdded) {
					// NOTE: This is a good test case for optimizer stability! Just returning a derived node in every case.
					let newOnlyFieldsClause = (onlyFieldsClause == null)
						? operations.onlyFields(Array.from(seenFields))
						: deriveNode(onlyFieldsClause, {
							fields: onlyFieldsClause.fields.concat(Array.from(seenFields).map((fieldName) => {
								return operations.field(fieldName);
							}))
						});

					return deriveNode(node, {
						clauses: node.clauses
							.filter((clause) => clause.type !== "onlyFields")
							.concat([ newOnlyFieldsClause ])
					});
				} else {
					return NoChange;
				}
			});
		},
		where: (node, { registerStateHandler, defer, setState }) => {
			let seenFields = [];

			registerStateHandler("seenField", (name) => seenFields.push(name));

			return defer(() => {
				setState("seenFieldsInWhere", seenFields);
				return NoChange;
			});

			// let seenFields = [];
			// let id = Math.random();

			// registerStateHandler("seenField", (name) => {
			// 	seenFields.push(name);
			// });
			
			// console.log("Scheduling defer", id);
			
			// return defer(() => {
			// 	console.log("Defer called", id);
				
			// 	// MARKER: This gets called twice, but should only be called once!
			// 	// console.log("Seen fields in WHERE:", seenFields, require("util").inspect(node, {colors:true,depth:null}));
			// 	console.log("Seen fields in WHERE:", seenFields);
			// 	return NoChange;
			// });
		}
	}
};
