import {
	exhaustiveStringTuple,
	isArray,
	isInstanceOf,
	isNumber,
	isString,
} from "qrc:/js/lib/utils";

/**
 * Defines an input file that should be read from the local file system.
 *
 * An invalid file path (e.g. non-existing or not readable) is considered an error.
 */
export interface DataSourceFilePath {
	tag: "filePath";
	path: string;
}

export function isDataSourceFilePath(arg: unknown): arg is DataSourceFilePath {
	return isInstanceOf<DataSourceFilePath>(arg, {
		tag: (arg): arg is "filePath" => arg === "filePath",
		path: isString,
	});
}

/**
 * Defines an input file's content
 */
export interface DataSourceFileContent {
	tag: "fileContent";
	type: "plainText" | "base64";
	data: string;
}

export function isDataSourceFileContent(arg: unknown): arg is DataSourceFileContent {
	return isInstanceOf<DataSourceFileContent>(arg, {
		tag: (arg): arg is "fileContent" => arg === "fileContent",
		type: (arg): arg is "plainText" | "base64" => exhaustiveStringTuple<DataSourceFileContent["type"]>()("plainText", "base64").some(t => t === arg),
		data: isString,
	});
}

/**
 * Defines an input's actual data
 */
export type DataSource = DataSourceFilePath | DataSourceFileContent;

export function isDataSource(arg: unknown): arg is DataSource {
	return exhaustiveStringTuple<DataSource["tag"]>()(
		"filePath",
		"fileContent",
	// eslint-disable-next-line array-callback-return
	).some(tag => {
		switch (tag) {
			case "filePath": return isDataSourceFilePath(arg);
			case "fileContent": return isDataSourceFileContent(arg);
		}
	});
}

/**
 * Corresponds to one input file (e.g. STEP, DXF, or .wsi4)
 */
export interface ExternalInputEntry {
	/**
	 * The data source
	 *
	 * This can be any supported input file (or its content).
	 *
	 * Supported formats are STEP, DXF, GEO, and .wsi4.
	 */
	dataSource: DataSource;

	/**
	 * If set, the ID should be unique among all entries of one `Input` instance.
	 *
	 * Defaults to an internally generated UUID.
	 */
	importId?: string | undefined;

	/**
	 * Multiplicity for the associated terminal component.
	 *
	 * In case of .wsi4 input this value has no effect.
	 */
	multiplicity?: number | undefined;

	/**
	 * Name for the initial article.
	 *
	 * In case of STEP this defaults to the name of the main assembly if unset.
	 *
	 * In case of .wsi4 input this value has no effect.
	 */
	articleName?: string | undefined;

	/**
	 * Sheet thickness to use for 2D input.
	 *
	 * Note: Currently this value has no effect when importing data via the GUI.
	 *
	 * Defaults to 1. if unset.
	 *
	 * In case of STEP input this value has no effect.
	 *
	 * In case of .wsi4 input this value has no effect.
	 */
	sheetThickness?: number | undefined;
}

export function isExternalInputEntry(arg: unknown): arg is ExternalInputEntry {
	return isInstanceOf<ExternalInputEntry>(arg, {
		dataSource: isDataSource,
		importId: (arg): arg is string | undefined => arg === undefined || isString(arg),
		articleName: (arg): arg is string | undefined => arg === undefined || isString(arg),
		multiplicity: (arg): arg is number | undefined => arg === undefined || isNumber(arg),
		sheetThickness: (arg): arg is number | undefined => arg === undefined || isNumber(arg),
	});
}

/**
 * Versions of the ExternalInput interface
 */
export type InputVersion = "v0";

/**
 * The current version of the ExternalInput interface
 */
export const currentInputVersion = Object.freeze<InputVersion>("v0");

/**
 * General definition of input data for the application
 */
export interface ExternalInput {
	/**
	 * Each entry corresponds one input file or its content.
	 */
	entries: ExternalInputEntry[];

	/**
	 * The version of the input
	 *
	 * The current version is [[currentInputVersion]].
	 */
	version?: "v0";
}

export function isExternalInput(arg: unknown): arg is ExternalInput {
	return isInstanceOf<ExternalInput>(arg, {
		entries: (arg): arg is ExternalInputEntry[] => isArray(arg, isExternalInputEntry),
		version: (arg): arg is InputVersion | undefined => arg === undefined || exhaustiveStringTuple<InputVersion>()("v0").some(other => other === arg),
	});
}
