import {
	isCadFeature,
	isScrewThreadUniqueMembers,
} from "qrc:/js/lib/generated/typeguard";
import {
	getTable,
} from "./table_utils";
import {
	isArray,
	isInstanceOf,
} from "./utils";
import {
	createUpdatedNodeUserDataImpl,
} from "./userdata_config";

function migrateCuttingGasToProcess(importedGraph: ImportedDocumentGraph): SlNodeUpdate[] {
	const sheetCuttingProcessMappings = getTable("sheetCuttingProcessMapping");
	const sheetCuttingProcessToCuttingGas = getTable("sheetCuttingProcessToLaserCuttingGas");

	const nodeUpdates: SlNodeUpdate[] = [];
	wsi4.sl.graph.vertices(importedGraph).forEach(vertex => {
		const wst = wsi4.sl.node.workStepType(vertex, importedGraph);
		if (wst !== "sheetCutting") {
			// Irrelevant node
			return;
		}

		const processId = wsi4.sl.node.processId(vertex, importedGraph);
		if (sheetCuttingProcessMappings.some(row => row.processId === processId)) {
			// The process is part of a known sheet cutting process so nothing left to do
			return;
		}

		const userData = wsi4.sl.node.nodeUserData(vertex, importedGraph);
		const cuttingGasId = userData["laserSheetCuttingGasId"];
		if (typeof cuttingGasId !== "string") {
			// Cannot perform migration; keeping everything as is
			return;
		}

		const processIdFromCuttingGasMapping = (() => {
			const cuttingGasMapping = sheetCuttingProcessToCuttingGas.find(row => row.laserSheetCuttingGasId === cuttingGasId);
			if (cuttingGasMapping === undefined) {
				return undefined;
			}
			return sheetCuttingProcessMappings.find(row => row.sheetCuttingProcessId === cuttingGasMapping.sheetCuttingProcessId)?.processId;
		})();
		if (processIdFromCuttingGasMapping !== undefined) {
			const content: SlNodeUpdateSheetCutting = {
				vertex: vertex,
				processId: processIdFromCuttingGasMapping,
			};
			nodeUpdates.push({
				type: "sheetCutting",
				content: content,
			});
			return;
		}

		const similarProcessId = sheetCuttingProcessMappings.find(row => row.processId.toLowerCase().includes(cuttingGasId.toLowerCase()))?.processId;
		if (similarProcessId !== undefined) {
			const content: SlNodeUpdateSheetCutting = {
				vertex: vertex,
				processId: similarProcessId,
			};
			nodeUpdates.push({
				type: "sheetCutting",
				content: content,
			});
			return;
		}

		// Cannot perform migration; keeping everything as is
	});

	return nodeUpdates;
}

interface LegacySheetTappingDataEntry {
	cadFeature: CadFeature;
	screwThread: ScrewThreadUniqueMembers;
}

function isLegacySheetTappingDataEntry(arg: unknown): arg is LegacySheetTappingDataEntry {
	return isInstanceOf<LegacySheetTappingDataEntry>(arg, {
		cadFeature: isCadFeature,
		screwThread: isScrewThreadUniqueMembers,
	});
}

function migrateSheetTappingUserData(importedGraph: ImportedDocumentGraph): SlNodeUpdate[] {
	const nodeUpdates: SlNodeUpdate[] = [];
	wsi4.sl.graph.vertices(importedGraph)
		.filter(v => wsi4.sl.node.processType(v, importedGraph) === "sheetTapping")
		.forEach(v => {
			const userData = wsi4.sl.node.nodeUserData(v, importedGraph);
			const oldSheetTappingData = userData["sheetTappingData"];
			if (!isArray(oldSheetTappingData, isLegacySheetTappingDataEntry)) {
				return;
			}
			const newSheetTappingData = oldSheetTappingData.map((oldEntry): SheetTappingDataEntry => ({
				cadFeature: oldEntry.cadFeature,
				screwThreadId: oldEntry.screwThread.identifier,
			}));
			nodeUpdates.push({
				type: "userDefined",
				content: {
					vertex: v,
					nodeUserData: createUpdatedNodeUserDataImpl("sheetTappingData", newSheetTappingData, userData),
				},
			});
		});
	return nodeUpdates;
}

export function finalizeImportedGraph(importedGraph: ImportedDocumentGraph, importId?: string): DocumentGraph {
	// This is the place where future graph migrations should be implemented.
	// (I.e. *not* in DocumentGraphHandler.)
	const nodeUpdates = migrateCuttingGasToProcess(importedGraph);
	// There should be no overlap between nodes of migrateCuttingGasToProcess() and sheetTapping nodes so *not* merging
	nodeUpdates.push(...migrateSheetTappingUserData(importedGraph));
	return wsi4.sl.graph.finalizeImportedGraph(importedGraph, nodeUpdates, [], [], [], importId ?? wsi4.util.createUuid());
}
