import {
	readCalcSettings,
} from "qrc:/js/lib/calc_settings";
import {
	getFutureResult,
} from "qrc:/js/lib/future";
import {
	DocXImageType,
	DocXTableCellType,
	Feature,
	TableType,
	WorkStepType,
} from "qrc:/js/lib/generated/enum";
import {
	getArticleName,
	getAssociatedSheetMaterialId,
	getAssociatedTubeMaterialId,
} from "qrc:/js/lib/graph_utils";
import {
	asyncRenderDefaultPng,
	getKeysOfObject,
} from "qrc:/js/lib/utils";
import {
	front,
} from "qrc:/js/lib/array_util";
import {
	getTable,
} from "qrc:/js/lib/table_utils";
import {
	collectNodeUserDataEntries,
	userDataAccess,
} from "qrc:/js/lib/userdata_utils";

import {
	computeProjectSellingPrice,
	computeRecursiveArticleScaleSellingPrice,
	computeScaleValues,
} from "./export_calc_costs";
import {
	getFixedMultiplicity,
} from "./export_calc_times";
import {
	createCalcCache,
} from "./export_calc_cache";
import {
	docXImageCell,
	DocXImageContent,
} from "./export_utils";

interface ScalePrice {
	scaleValue: number;
	pricePerPiece: number;
	overallPrice: number;
}

interface QuotationArticleRow {
	multiplicity: number;
	articleName: string;
	pricePerPiece: number;
	overallPrice: number;
	image: DocXImageContent | undefined;
	material: string;
	scalePrices: ScalePrice[];
}

interface QuotationInput {
	articles: QuotationArticleRow[];
	netPrice: number;
}

export function createQuotationInput(): QuotationInput | undefined {
	const calcCache = createCalcCache();
	const articles = wsi4.graph.vertices().filter(v => wsi4.graph.targets(v).length === 0)
		.sort((a, b) => wsi4.util.toNumber(wsi4.node.rootId(a)) - wsi4.util.toNumber(wsi4.node.rootId(b)))
		.map(v => wsi4.graph.article(v));
	const quotationArticles: QuotationArticleRow[] = [];
	const sheetMaterialTable = getTable(TableType.sheetMaterial);
	const tubeMaterialTable = getTable(TableType.tubeMaterial);
	for (const article of articles) {
		const fr = front(article);
		const multiplicity = getFixedMultiplicity(fr);
		const overallPrice = computeRecursiveArticleScaleSellingPrice(article, multiplicity, calcCache);
		if (overallPrice === undefined) {
			return undefined;
		}
		const assembly = wsi4.node.inputAssembly(fr);
		const material = (() => {
			const workStepType = wsi4.node.workStepType(fr);
			if (workStepType === WorkStepType.joining) {
				const materialIds = collectNodeUserDataEntries("sheetMaterialId", fr, userDataAccess.article | userDataAccess.reaching)
					.filter((lhs, index, self) => lhs !== undefined && self.findIndex(rhs => rhs !== undefined && lhs === rhs) === index);
				if (materialIds.length === 0) {
					return "";
				}
				const materials: string[] = [];
				for (const materialId of materialIds) {
					const sheetMaterial = sheetMaterialTable.find(row => row.identifier === materialId);
					if (sheetMaterial !== undefined) {
						materials.push(sheetMaterial.name);
					} else {
						const m = tubeMaterialTable.find(row => row.identifier === materialId);
						if (m === undefined) {
							return undefined;
						}
						materials.push(m.name);
					}
				}
				return materials.sort((a, b) => a.localeCompare(b))
					.filter((value: string, index, self) => self.findIndex(v => v === value) === index)
					.join(", ");
			} else if (workStepType === WorkStepType.tube || workStepType === WorkStepType.tubeCutting) {
				//
				const materialId = getAssociatedTubeMaterialId(fr);
				if (materialId === undefined) {
					return "";
				}
				const m = tubeMaterialTable.find(row => row.identifier === materialId);
				if (m === undefined) {
					return undefined;
				}
				return m.name;
			} else {
				const materialId = getAssociatedSheetMaterialId(fr);
				if (materialId === undefined) {
					return "";
				}
				const m = sheetMaterialTable.find(row => row.identifier === materialId);
				if (m === undefined) {
					return undefined;
				}
				return m.name;
			}
		})();
		if (material === undefined) {
			wsi4.util.error("Material not found.");
			return undefined;
		}
		const scalePrices = (() => {
			const calcConfig = readCalcSettings();
			const scaleValues = computeScaleValues(multiplicity, calcConfig);
			const scalePrices: ScalePrice[] = [];
			for (const scaleValue of scaleValues) {
				const sellingPrice = computeRecursiveArticleScaleSellingPrice(article, scaleValue, calcCache);
				if (sellingPrice === undefined) {
					continue;
				}
				scalePrices.push({
					scaleValue: scaleValue,
					overallPrice: sellingPrice,
					pricePerPiece: sellingPrice / scaleValue,
				});
			}
			return scalePrices;
		})();
		if (scalePrices.length === 0) {
			wsi4.util.error("Error computing scale prices.");
			return undefined;
		}
		quotationArticles.push({
			multiplicity: multiplicity,
			articleName: getArticleName(fr),
			pricePerPiece: overallPrice / multiplicity,
			overallPrice: overallPrice,
			image: assembly === undefined ? undefined : {
				content: getFutureResult<"arrayBuffer">(asyncRenderDefaultPng(assembly)),
				imageType: DocXImageType.png,
			},
			material: material,
			scalePrices: scalePrices,
		});
	}
	const price = computeProjectSellingPrice(calcCache);
	if (price === undefined) {
		return undefined;
	}
	return {
		articles: quotationArticles,
		netPrice: price,
	};
}

const demoString = "DEMO";
function scalePriceTable(scalePrices: ScalePrice[], quotationEnabled: boolean) {
	return [
		scalePrices.map((scalePrice, scalePriceIndex) => [
			{
				type: DocXTableCellType.text,
				content: {
					placeholder: "$scalepricequantity$",
					text: scalePrice.scaleValue.toFixed(0),
				},
			},
			{
				type: DocXTableCellType.text,
				content: {
					placeholder: "$scalepriceoverallarticleprice$",
					text: quotationEnabled || scalePriceIndex % 2 === 1 ? wsi4.util.toCurrencyString(scalePrice.overallPrice, 2) : demoString,
				},
			},
			{
				type: DocXTableCellType.text,
				content: {
					placeholder: "$scalepricepriceperpiece$",
					text: quotationEnabled || scalePriceIndex % 2 === 1 ? wsi4.util.toCurrencyString(scalePrice.pricePerPiece, 2) : demoString,
				},
			},
		]),
	];
}

const tableCellFunctionMap: {[index in keyof QuotationArticleRow]: (article: QuotationArticleRow, index: number, quotationEnabled: boolean) => DocXTableCell} = {
	multiplicity: (article: QuotationArticleRow) => (
		{
			type: DocXTableCellType.text,
			content: {
				placeholder: "$quantity$",
				text: article.multiplicity.toFixed(0),
			},
		}),
	articleName: (article: QuotationArticleRow) => (
		{
			type: DocXTableCellType.text,
			content: {
				placeholder: "$articlename$",
				text: article.articleName,
			},
		}),
	pricePerPiece: (article: QuotationArticleRow, index: number, quotationEnabled: boolean) => (
		{
			type: DocXTableCellType.text,
			content: {
				placeholder: "$priceperpiece$",
				text: quotationEnabled || index % 2 === 1 ? wsi4.util.toCurrencyString(article.pricePerPiece, 2) : demoString,
			},
		}),
	overallPrice: (article: QuotationArticleRow, index: number, quotationEnabled: boolean) => (
		{
			type: DocXTableCellType.text,
			content: {
				placeholder: "$overallarticleprice$",
				text: quotationEnabled || index % 2 === 1 ? wsi4.util.toCurrencyString(article.overallPrice, 2) : demoString,
			},
		}),
	image: (article: QuotationArticleRow) => docXImageCell("$image$", article.image),
	material: (article: QuotationArticleRow) => ({
		type: DocXTableCellType.text,
		content: {
			placeholder: "$material$",
			text: article.material,
		},
	}),
	scalePrices: (article: QuotationArticleRow, _index: number, quotationEnabled: boolean) => ({
		type: DocXTableCellType.tables,
		content: {
			tables: scalePriceTable(article.scalePrices, quotationEnabled),
		},
	}),
};

export function createQuotationDocx(templ: ArrayBuffer, quotationInput: QuotationInput): ArrayBuffer | undefined {
	const quotationEnabled = wsi4.isFeatureEnabled(Feature.quotationExport);
	const articleTable: DocXTableCell[][] = [];
	for (const article of quotationInput.articles) {
		const row: DocXTableCell[] = [
			{
				type: DocXTableCellType.text,
				content: {
					placeholder: "$itemnumber$",
					// positions in quotations start at 1
					text: (articleTable.length + 1).toFixed(0) + ".",
				},
			},
		];
		for (const key of getKeysOfObject(article)) {
			row.push(tableCellFunctionMap[key](article, articleTable.length, quotationEnabled));
		}
		articleTable.push(row);
	}
	const priceTable =
		[
			[
				{
					type: DocXTableCellType.text,
					content: {
						placeholder: "$netprice$",
						text: quotationEnabled ? wsi4.util.toCurrencyString(quotationInput.netPrice, 2) : demoString,
					},
				},
			],
		];
	const d = wsi4.documentCreator.generateDocX(templ,
		[
			articleTable,
			priceTable,
		]);
	return d.byteLength === 0 ? undefined : d;
}
