/// <reference types="systemjs"/>

import {IS_CLIENT} from "@gongt/ts-stl-library/check-environment";
import {createLogger} from "@gongt/ts-stl-library/debug/create-logger";
import {LOG_LEVEL} from "@gongt/ts-stl-library/debug/levels";
import {PartialPackageDefine, SystemjsConfigFile} from "./defines";

const warn = createLogger(LOG_LEVEL.WARN, 'jspm-pass');
const silly = createLogger(LOG_LEVEL.SILLY, 'jspm-pass');

const cjsConfig = {
	format: 'cjs',
	defaultExtension: 'js',
	main: 'index.js',
};
export type ModuleImport = {
	name: string;
	es6?: boolean;
	config?: object;
}
const resolved: any = {};

function resolveModule(module: string, withDeps: boolean): ModuleImport[]|ModuleImport {
	if (IS_CLIENT) {
		return null;
	}
	if (resolved[module]) {
		return resolved[module];
	}
	let path;
	try {
		path = require.resolve(module + '/package.json')
	} catch (e) {
		warn('can not resolve node_module: %s', module);
		return resolved[module] = {
			name: module,
		};
	}
	const pkg = require(path);
	// silly('module "%s" %s es6', path, pkg.es6? 'IS' : 'is NOT');
	const ret: ModuleImport[] = [{
		name: module,
		es6: !!pkg.es6,
	}];
	
	if (!withDeps) {
		return ret[0];
	}
	
	if (pkg.dependencies) {
		const deps1 = Object.keys(pkg.dependencies || {});
		
		const subRet = deps1.map(m => resolveModule(m, true));
		for (let item of subRet) {
			if (item) {
				if (Array.isArray(item)) {
					ret.push(...item);
				} else {
					ret.push(item);
				}
			}
		}
	}
	return resolved[module] = ret;
}

export class JspmPackagePassing {
	private scripts: string = '';
	private modules: ModuleImport[] = [];
	private configData: Partial<SystemjsConfigFile>;
	
	constructor() {
		this.configData = {
			map: {},
			meta: {},
			packages: {},
			paths: {},
		};
	}
	
	pathMap(from: string, to: string) {
		this.configData.map[from] = to;
	}
	
	registerNodeModules(module: string, withDeps: boolean = false) {
		const ret = resolveModule(module, withDeps);
		if (ret) {
			if (Array.isArray(ret)) {
				this.modules.push(...ret);
			} else {
				this.modules.push(ret);
			}
		}
		if (!this.configData.map[module]) {
			this.pathMap(module, 'node_modules/'+module);
		}
	}
	
	manualRegisterModule(moduleName: string, def: PartialPackageDefine) {
		this.configData.packages[moduleName] = <any>def;
	}
	
	script(script: string) {
		this.scripts += script + '\n\n';
	}
	
	registerExtension(extension: string, loader: string = extension) {
		const tag = '*.' + extension;
		if (!this.configData.meta.hasOwnProperty(tag)) {
			this.configData.meta[tag] = <any>{};
		}
		
		this.configData.meta[tag].loader = loader;
	}
	
	config(): Partial<SystemjsConfigFile> {
		return this.configData;
	}
	
	toJSON() {
		throw new TypeError('jspm config can not encode as json, must use toString()');
	}
	
	static getHeader() {
		return `<script type="text/javascript">window.CLASS_SUPPORT = window.CLASS_SUPPORT || (function(){
		try { eval('"use strict"; class foo {}'); return true; } catch (e) { return false; }
	})();</script>`.replace(/\s*\n\s*/g, '');
	}
	
	toString() {
		return `(function(){
	var isIE = /*@cc_on!@*/false || !!document.documentMode;
	var config = ${JSON.stringify(this.config())};
	var myModuleConfig = ${JSON.stringify(cjsConfig)};
	var modules = ${JSON.stringify(this.modules)};
	
	modules.forEach(function (mdl) {
		var name = mdl.name;
		// config.map[name] = config.paths[name];
		config.packages[name] = myModuleConfig;
	});
	
	config.packageConfigPaths = SystemJS.getConfig().packageConfigPaths;
	if(modules.length){
		config.packageConfigPaths.unshift('node_modules/@*/*/package.json');
		config.packageConfigPaths.unshift('node_modules/*/package.json');
	}
	
	/* scripts */
	${this.scripts}
	
	SystemJS.config(config);
	return config;
})();`;
	}
}
