/*
 * Functions that are called when adding new data to wsi4
 *
 * Note: All API calls must conform to DocumentGraphHandler's script engine
 */

import {
	WorkStepType,
} from "qrc:/js/lib/generated/enum";
import {
	isStringIndexedInterface,
} from "qrc:/js/lib/generated/typeguard";
import {
	isSignatureVertex,
} from "qrc:/js/lib/graph_utils";
import {
	isArticleUserDataEntry,
} from "qrc:/js/lib/userdata_config";
import {
	computeInitialArticleUserData,
} from "qrc:/js/lib/userdata_initial_values";
import {
	createUpdatedNodeUserData,
	getNodeUserDataEntryOrThrow,
	insertInitialValuesIfMissing,
	isCompatibleToNodeUserDataEntry,
	UserData,
} from "qrc:/js/lib/userdata_utils";
import {
	getKeysOfObject,
	isBoolean,
	isNumber,
	isString,
} from "qrc:/js/lib/utils";

interface UserDataMapEntry {
	vertex: Vertex;
	userData: UserData;
}

function migrateTestReport(inputVertex: Vertex, inputUserData: Readonly<StringIndexedInterface>): StringIndexedInterface {
	const testReportKey = "testReportRequired";
	if (!isCompatibleToNodeUserDataEntry("testReportRequired", inputVertex) || isBoolean(inputUserData[testReportKey])) {
		// Node is either not relevant or has the respective user data entry already set.
		// As this function is expected to be called *before* insertion of missing user data
		// entries, this sub-section of the graph already is considered up to date with respect
		// to the testReportRequired user data entry.
		return inputUserData;
	} else {
		const sheetVertex = wsi4.graph.reaching(inputVertex)
			.find(vertex => wsi4.node.workStepType(vertex) === WorkStepType.sheet);
		if (sheetVertex === undefined) {
			return inputUserData;
		}

		const sheetNodeUserData = wsi4.node.userData(sheetVertex);
		if (isBoolean(sheetNodeUserData[testReportKey])) {
			const result: StringIndexedInterface = Object.assign({}, inputUserData);
			result[testReportKey] = sheetNodeUserData[testReportKey];
			return result;
		} else {
			return inputUserData;
		}
	}
}

function completeArticleUserData(userData: StringIndexedInterface): StringIndexedInterface {
	const articleUserData = userData["articleUserData"];
	if (isStringIndexedInterface(articleUserData)) {
		const initialValues = computeInitialArticleUserData();
		getKeysOfObject(initialValues)
			.forEach(key => {
				if (!isArticleUserDataEntry(key, articleUserData[key])) {
					articleUserData[key] = initialValues[key];
				}
			});
	}
	return userData;
}

function migrateArticleName(vertex: Vertex, userData: Readonly<StringIndexedInterface>): StringIndexedInterface {
	if (!isSignatureVertex(vertex)) {
		return userData;
	}

	const articleUserData = getNodeUserDataEntryOrThrow("articleUserData", vertex, userData);
	if (articleUserData.name.length > 0) {
		return userData;
	}

	const nameFromAttributes = (() => {
		const articleAttributes = wsi4.node.articleAttributes(vertex);
		return articleAttributes.userData["name"];
	})();

	if (!isString(nameFromAttributes)) {
		return userData;
	}

	articleUserData.name = nameFromAttributes;
	return createUpdatedNodeUserData(
		"articleUserData",
		vertex,
		articleUserData,
		userData,
	);
}

function migrateCalcParams(vertex: Vertex, userData: StringIndexedInterface): StringIndexedInterface {
	const materialCostsPerPiece = userData["materialCosts"];
	if (isNumber(materialCostsPerPiece)) {
		userData = createUpdatedNodeUserData("materialCostsPerPiece", vertex, materialCostsPerPiece, userData);
	}

	const unitTimePerPiece = userData["unitTime"];
	if (isNumber(unitTimePerPiece)) {
		userData = createUpdatedNodeUserData("unitTimePerPiece", vertex, unitTimePerPiece, userData);
	}

	return userData;
}

export function updateUserData(): Array<UserDataMapEntry> {
	return wsi4.graph.vertices()
		.map(vertex => {
			let userData = wsi4.node.userData(vertex);
			// Note:  migrateTestReport() must be called before insertInitialValuesIfMissing() (see implementation).
			userData = migrateTestReport(vertex, userData);
			// Note:  completeArticleUserData() must be called before insertInitialValuesIfMissing()
			userData = completeArticleUserData(userData);
			userData = insertInitialValuesIfMissing(vertex, userData);
			userData = migrateArticleName(vertex, userData);
			userData = migrateCalcParams(vertex, userData);
			return {
				vertex: vertex,
				userData: userData,
			};
		});
}
