import {
	ProcessType,
	WorkStepType,
} from "qrc:/js/lib/generated/enum";

import {
	getKeysOfObject,
} from "./utils";

/**
 * Axioms that have to be valid for an consistent documentgraph
 */
type GraphAxiom = "mergeNodesAreJoining" | "userDefinedNotTargetOfNodeWithMoreThanOneTarget" | "deburringSubProcessNodeHasTwoDimRep" | "sheetArticlesConsistOfSheetNodesOnly" | "tubeArticlesConsistOfTubeNodeOnly";
type GraphAxiomMap = {
	readonly [index in GraphAxiom]: () => boolean;
};

/**
 * True if all nodes that merge assemblies together, are of WorkStepType joining
 */
function mergeNodesAreJoining(): boolean {
	return wsi4.graph.vertices()
		.filter(vertex => (wsi4.graph.sources(vertex).length > 1 || wsi4.graph.sources(vertex)
			.some(sourceVertex => (wsi4.graph.sourceMultiplicity(sourceVertex, vertex) > 1))))
		// FIXME The axiom does not hold for the way WorkStepType.packaging is used right now
		// This is related to #1918
		.every(vertex => (wsi4.node.workStepType(vertex) === WorkStepType.joining || wsi4.node.workStepType(vertex) === WorkStepType.packaging));
}

/**
 * True if for all userDefined nodes the source of it has only one target
 */
function userDefinedNotTargetOfNodeWithMoreThanOneTarget(): boolean {
	return wsi4.graph.vertices()
		.filter(vertex => wsi4.node.workStepType(vertex) === WorkStepType.userDefined)
		.filter(vertex => {
			assumeGraphAxioms([ "mergeNodesAreJoining" ]);
			if (wsi4.graph.sources(vertex).length > 1) {
				return wsi4.throwError("More than one source");
			}
			return wsi4.graph.sources(vertex).length === 1;
		})
		.map(vertex => wsi4.graph.sources(vertex)[0])
		.every(source => wsi4.graph.targets(source).length === 1);
}

/**
 * Deburring sub-processes are automaticMechanicalDeburring and manualMechanicalDeburring
 */
function deburringSubProcessNodeHasTwoDimRep(): boolean {
	return wsi4.graph.vertices()
		.filter(vertex => wsi4.node.workStepType(vertex) === WorkStepType.userDefined)
		.filter(vertex => wsi4.node.processType(vertex) === ProcessType.automaticMechanicalDeburring || wsi4.node.processType(vertex) === ProcessType.manualMechanicalDeburring)
		.every(vertex => wsi4.node.twoDimRep(vertex) !== undefined);
}

/**
 * An article is considered a sheet article if it is a dedicated article that consists of at least one sheet node
 */
function sheetArticlesConsistOfSheetNodesOnly(): boolean {
	return wsi4.graph.vertices()
		.filter(vertex => wsi4.node.workStepType(vertex) === WorkStepType.sheet)
		.map(vertex => wsi4.graph.article(vertex))
		.every(article => article.length === 1 || article.some(vertex => wsi4.node.workStepType(vertex) === WorkStepType.sheetCutting));
}

/**
 * An article is considered a sheet article if it is a dedicated article that consists of at least one sheet node
 */
function tubeArticlesConsistOfTubeNodeOnly(): boolean {
	return wsi4.graph.vertices()
		.filter(vertex => wsi4.node.workStepType(vertex) === WorkStepType.sheet)
		.map(vertex => wsi4.graph.article(vertex))
		.every(article => article.length === 1);
}

/**
 * Map to add test functions for axioms
 */
const graphAxiomMap: GraphAxiomMap = {
	mergeNodesAreJoining: mergeNodesAreJoining,
	userDefinedNotTargetOfNodeWithMoreThanOneTarget: userDefinedNotTargetOfNodeWithMoreThanOneTarget,
	deburringSubProcessNodeHasTwoDimRep: deburringSubProcessNodeHasTwoDimRep,
	sheetArticlesConsistOfSheetNodesOnly: sheetArticlesConsistOfSheetNodesOnly,
	tubeArticlesConsistOfTubeNodeOnly: tubeArticlesConsistOfTubeNodeOnly,
};

/**
 * Check that axioms are fulfilled
 */
export function checkGraphAxioms(): void {
	const violatedAxioms = getKeysOfObject(graphAxiomMap)
		.filter(key => !graphAxiomMap[key]());
	if (violatedAxioms.length > 0) {
		// there are violated axioms
		return wsi4.throwError("Violated axioms: " + violatedAxioms.join(", "));
	}
}

/**
 * Check that axioms are still valid (this check is done via syntax check)
 */
export function assumeGraphAxioms(_axioms: Array<GraphAxiom>): void {}
