import { Deferred } from "./deferred";
import { has } from "lodash";

/**
 * The task which is completed once the client is ready
 */
export const clientReadyTask = new Deferred<void>();

/**
 * Create a logger instance that prepends all messages
 * with the specified label.
 * 
 * @param label The label
 * @param debug Whether the logger is debug
 * @returns Logging function
 */
export function logger(label: string, debug = false): (...args: any[]) => void {
	return (...args: any) => {
		(debug ? console.debug : console.log)(`[${label}]`, ...args);
	};
}

/**
 * Parse the given string to a number, and simply
 * return any number passed in.
 * 
 * @param value The input
 * @returns The number
 */
export function cleanInt(value: string|number): number {
	return typeof value == 'number' ? value : parseInt(value);
}

/**
 * Format the given time in milliseconds for human readability
 * 
 * @param value The time in milliseconds
 * @param format The time format
 * @returns Formatted string
 */
export function formatTime(value: number|string, format: string): string {
	throw new Error("Not implemented");
	// return dayjs(parseInt(value as any)).format(vue.$t(format) as string);
}

/**
 * Parse the given slug string into a numeric id.
 * 
 * Slugs follow the format of a hyphen separated string of
 * words, with the first segment being a number.
 * 
 * @param slug The slug string
 * @returns decoded id
 */
export function parseSlug(slug: string): number|undefined {
	return parseInt(slug.split('-')[0]) || undefined;
}

/**
 * Check if the current environment is loaded into Electron
 * 
 * Inspired by https://github.com/cheton/is-electron
 * 
 * @returns The result
 */
export function isElectron(): boolean {
	if(typeof window !== 'undefined' && typeof window.process === 'object' && (window.process as any).type === 'renderer') {
		return true;
	}

	if(typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {
		return true;
	}

	return false;
}

/**
 * Expect the given object to contain the specified
 * set of fields, and if not, an error is thrown.
 * 
 * @param obj The instance
 * @param fields The fields to check
 */
export function expect(obj: any, fields: string[]) {
	if(typeof obj != 'object') return new Error('Instance is not an object');

	for(const field of fields) {
		if(!has(obj, field)) {
			throw new Error(`Field ${field} is missing in instance`);
		}
	}
}

/**
 * Prop validating version of "expect", returns the validation
 * function when called.
 * 
 * @param fields The fields to check
 */
export function expectProp(fields: string[]) : (value: any) => boolean {
	return (value) => {
		if(typeof value != 'object') return false;

		for(const field of fields) {
			if(!has(value, field)) {
				console.error(`Field ${field} is missing in instance`);
				return false;
			}
		}

		return true;
	};
}

/**
 * Create a sorting function which adopts the order of
 * the given proxy array
 * 
 * @param proxy The proxy array
 * @param key An optional key extractor
 * @returns 
 */
export function proxyComparator(proxy: any[], key?: string): (left: any, right: any) => number {
	return (left, right) => {
		const lk = key ? (left as any)[key] : left;
		const rk = key ? (right as any)[key] : right;

		return proxy.indexOf(lk) - proxy.indexOf(rk);
	};
}

/**
 * Build a path from the given parts
 * 
 * @param parts The parts
 * @returns The path
 */
export function buildPath(...parts: string[]): string {
	let builder = '';
	let i = 0;

	for(const part of parts) {
		builder += part;

		if(!builder.endsWith('/') && i < parts.length - 1) {
			builder += '/';
		}

		i++;
	}

	return builder;
}

/**
 * Returns the first occurence of the given key in the
 * specified list of objects.
 * 
 * @param key The key to search for
 * @param objects The objects to search
 * @returns The value
 */
export function getFirstValue(key: string, objects: any[]): any {
	for(const obj of objects) {
		if(has(obj, key)) {
			return obj[key];
		}
	}

	return undefined;
} 