import {
	getKeysOfObject,
	hasPropertyT,
	isNumber,
	isObject,
	isString,
} from "./utils";

/**
 * Project configuration
 *
 * Note: relevant external API type is [[ProjectConfig]]
 */
export interface ProjectConfigComplete {
	/**
	 * Identifier for a project
	 *
	 * Value is part of the resulting [[GraphRepresentation]]
	 */
	id: string;
}

/**
 * Project configuration
 */
export type ProjectConfig = Partial<ProjectConfigComplete>;

/**
 * ERP-Export configuration
 *
 * Note: relevant external API type is [[ErpExportConfig]]
 */
export interface ErpExportConfigComplete {
	/**
	 * If set, the ERP-Export sends [[GraphRepresentation]] to this url
	 *
	 * The data is sent via a POST request.
	 */
	serverUrl: string;

	/**
	 * If set, the ERP-Export writes [[GraphRepresentation]] to this file
	 */
	filePath: string;

	/**
	 * If set, the article name length is limited to the respective value
	 */
	articleNameMaxLength: number;
}

/**
 * ERP-Export configuration
 */
export type ErpExportConfig = Partial<ErpExportConfigComplete>;

/**
 * Maps script-arg-keys to the respective types
 *
 * Note: this interface is not intended to be instantiated.
 */
export interface ScriptArgs {
	projectConfig: ProjectConfig;
	erpExportConfig: ErpExportConfig;
}

/**
 * Script argument object
 *
 * Example:
 *
 * To submit this [[ProjectConfig]]:
 *
 * ```
 * {
 * 	"id" : "9084290-rev-42"
 * }
 * ```
 *
 * WSi4 needs to be called like this:
 *
 * ```
 * wsi4.exe --script-arg "{\"type\":\"projectConfig\",\"content\":{\"id\":\"9084290-rev-42\"}}"
 * ```
 */
export interface ScriptArg {
	/**
	 * Type of the arg value
	 */
	type: keyof ScriptArgs;

	/**
	 * Content of the arg value
	 *
	 * Note: Union-type is defined by [[type]]
	 */
	content: ProjectConfig|ErpExportConfig;
}

function isProjectConfig(arg: unknown): arg is ProjectConfig {
	const typeGuardMap: {[index in keyof ProjectConfigComplete]: (arg: unknown) => arg is ProjectConfig[index]} = {
		id: (arg: unknown) : arg is string | undefined => arg === undefined || isString(arg),
	};
	return isObject(arg) && getKeysOfObject(typeGuardMap)
		.every(key => typeGuardMap[key]((arg as any)[key]));
}

function isErpExportConfig(arg: unknown): arg is ErpExportConfig {
	const typeGuardMap: {[index in keyof ErpExportConfigComplete]: (arg: unknown) => arg is ErpExportConfig[index]} = {
		serverUrl: (arg: unknown) : arg is string | undefined => arg === undefined || isString(arg),
		filePath: (arg: unknown) : arg is string | undefined => arg === undefined || isString(arg),
		articleNameMaxLength: (arg: unknown) : arg is number | undefined => arg === undefined || isNumber(arg),
	};
	return isObject(arg) && getKeysOfObject(typeGuardMap)
		.every(key => typeGuardMap[key]((arg as any)[key]));
}

const scriptArgTypeGuardMap: {[index in keyof ScriptArgs]: (arg: unknown) => arg is ScriptArgs[index]} = {
	projectConfig: isProjectConfig,
	erpExportConfig: isErpExportConfig,
};

function isScriptArg<Key extends keyof ScriptArgs>(key: Key, arg: unknown): arg is ScriptArgs[Key] {
	return scriptArgTypeGuardMap[key](arg);
}

export function getScriptArg<Key extends keyof ScriptArgs>(key: Key): ScriptArgs[Key]|undefined {
	const typeAndContent = wsi4.sharedData.scriptArgs()
		.map(arg => JSON.parse(arg) as unknown)
		.find(arg => isObject(arg) && hasPropertyT(arg, "type", isString) &&
			(arg as any).type === key &&
			hasPropertyT(arg, "content", isObject) &&
			isScriptArg(key, (arg as any).content));
	return typeAndContent === undefined ? undefined : (typeAndContent as any).content as ScriptArgs[Key];
}
