'use strict';
import http from 'http';
import https from 'https';
import extendMime from './extendMime';
import defCfg from './config/defCfg';
import configInit, * as config from './config/config';
import merge from 'merge';
import Promise from 'promise';
import log from './log';
import {requestHandler, requestConnectHandler, requestUpgradeHandler} from './requestSerives';
import EventEmitter from 'events';
import {getCert, getCertDir} from './cert/cert.js';
import {SNICallback} from './httpsProxySer';
import ui from './web/app';
import {localIps} from './getLocalIps';
import {error as errFun, getPort, openCmd} from './tools';
import * as requestMiddleware from './requestMiddleware';
import configProps from './config/configProps';
import util from 'util';
import * as rule from './config/rule';
import express from 'express';
import webCfg from './config/webCfg';
import path from 'path';
import ws from './ws/ws';
//	process.env.NODE_ENV
const getLocalUiReg = (port) => {
	let ips = localIps.slice(0);
	ips.push('localhost');
	let l = ips.length;
	return ips.reduce((result, cur, index) => {
		if (index > 0) {
			result += "|";
		} 
		result += `(?:${cur}`;
		if (port !== 80 || port !== 443) {
			result += `:${port})`;
		} else {
			result += ")";
		}
		// 最后一次
		if (index === l - 1) {
			return new RegExp(result, "i");
		}
		return result;
	}, "^(?:http|ws)(?:s?)://");
};
/**
 * 按顺序调用数组，每个步骤返回promise
 * @arr 表示要执行的数据
 * @result表示执行的结果，结果会进行合并，结果必须是一个object
 * @context 表示执行的上下文
 */
const execArrByStep = async function (arr, result, context) {
	result = result || {};
	if (!arr || !arr.length) {
		return result;
	}
	for(let cur of arr) {
		// 这里调用如果出错，最后直接抛出 -- 也可以考虑哪一步出错，哪一步单独抛出
		// 检测cur是够是一个函数？？
		let newRes = await cur.call(context, result);
		// 修改了引用
		if (newRes !== result) {
			result = merge(result, newRes);
		}
	}
	return result;
};
class CatProxy{
	/**
	 * 
	 * @param  {[type]} option 
	 *  {
	 *  	type: "当前服务器类型"
	 *		port: "当前http端口",
	 *    httpsPort: "当前https服务器端口",
	 *		certHost: "https证书生成默认host代理",
	 *		crackHttps 是否解开 https请求，在 http代理模式下,
	 *		log: '日志级别',
	 *		uiPort 端口
	 *	}
	 *	@param servers 自定义服务器,最多同时开启2个服务器，一个http一个https, 2个服务器的时候顺序是http,https  	如果只有一个则没有顺序问题
	 *
	 *  @param saveProps 要同步到文件的字段 为空则全部同步
	 *
	 */
	constructor(opt, saveProps) {
		this.option = {};
		// 初始化配置文件
		configInit();
		let certDir = getCertDir();
		log.info(`当前证书目录： ${certDir}`);
		// 读取缓存配置文件
		let fileCfg = {};
		configProps
		.forEach(current => {
			let val = config.get(current);
			if ( val !== undefined && val !== null) {
				fileCfg[current] = val;
			}
		});
		// 混合三种配置
		let cfg = merge.recursive({}, defCfg, fileCfg, opt);
		if (saveProps && saveProps.length) {
			this.option.saveProps = saveProps;
			// 配置了保存字段，则只保存这些字段
			config.setSaveProp(...saveProps);
		}
		// 将用户当前设置保存到缓存配置文件
		configProps
		.forEach(current => {
			if (cfg[current] !== null && cfg[current] !== undefined) {
				// 为‘’表示要删除这个字段
				if (cfg[current] === '' && config.get(current)) {
					config.del(current);
				} else {
					config.set(current, cfg[current]);
				}
			}
		});
		config.save();
		this._beforeReqEvt = [];
		this._beforeResEvt = [];
		this._afterResEvt = [];
		this._pipeRequestEvt = [];
	}
	init() {
		let com = this;
		// 'hosts', "log", 'breakHttps', 'excludeHttps', 'sni' 可以通过 进程修改的字段
		// 别的进程发送的消息
		process.on('message', function(message) {
			if (!message.result || !typeof message.result === 'object') {
				return;
			}
			log.debug('receive message');
			if (message.type) {
				switch(message.type) {
				case "config":
					let data = {};
					config.set(message.result);
					// 所有配置均不保存
					config.save([]);
					com.setLogLevel();
					break;
				default:
					log.error('收到未知的消息', message);
				}
			}
		});
		this.setLogLevel();
		// dangerous options
		process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
		return Promise.resolve()
		.then(this.createCache.bind(this))
		.then(this.checkParam.bind(this))
		.then(this.checkEnv.bind(this))
		.then(this.createServer.bind(this))
		.then(this.uiInit.bind(this))
		.then(null, (err) => {
			this.errorHandle(err);
			process.exit(1);
		});
	}
	// 创建缓存，创建请求保存
	createCache() {
	}
	checkParam() {
	}
	// 设置 日志级别
	setLogLevel(logLevel) {
		if (logLevel) {
			config.set('log', logLevel);
			log.transports.console.level = config.get('log');
			config.save('log');
		} else {
			log.transports.console.level = config.get('log');
		}
	}
	// 设置服务器类别
	setServerType(type) {
		config.set('type', type);
		config.save('type');
	}
	// 设置服务器端口
	setHttpPort(port) {
		port = + port;
		config.set('port', port);
		config.save('port');
	}
	setHttpsPort(port) {
		port = + port;
		config.set('httpsPort', port);
		config.save('httpsPort');
	}
	// 设置ui端口
	setUiPort(port) {
		port = + port;
		config.set('uiPort', port);
		config.save('uiPort');
	}
	// 设置sni类型
	setSniType(type) {
		config.set("sni", type);
		config.save('sni');
	}
	// 设置破解https
	setBreakHttps(list) {
		config.set('breakHttps', list);
		config.save('breakHttps');
	}
	// 设置排除https列表
	setExcludeHttps(list) {
		config.set('excludeHttps', list);
		config.save('excludeHttps');
	}
	setRules(rules) {
		rule.saveRules(rules);
	}
	// 获取配置
	getConfig(key) {
		if (typeof key === 'string') {
			return config.get(key);
		}
		return config.get();
	}
	setConfig(...arg) {
		config.set(...arg);	
		config.save();
	}
	// 环境检测
	checkEnv() {
	}
	uiInit() {
		let port = config.get('uiPort');
		let isAutoOpen = config.get('autoOpen');
		let p = port;
		// 如果port是0 则只提供下载链接的server
		return Promise.resolve(p || getPort())
		.then((p) => {
			// 内置服务器初始化
			let host = `http://${localIps[0]}:${p}`;
			let uiOption = {
				port : p,
				hostname: localIps[0],
				host: host,
				wsServerUrl: host + webCfg.wsPath,
				cdnBasePath: path.join('/c', webCfg.cdnBasePath),
				env: webCfg.env
			};
			// 写成正则，判断是否是ui的一个访问地址
			this.localUiReg = getLocalUiReg(p);
			let uiApp = ui(!!port);
			let app = express();
			let uiServer = app.listen(p, function() {
				log.info('catproxy 规则配置地址：' + host +"/c/index");
				log.info('catproxy 监控界面地址：' + host +"/c/m");
				if(port && isAutoOpen) {
					openCmd(host + "/c/index");
				}
			});
			uiApp.locals.uiOption = uiOption;
			uiServer.on('error', (err) => {
				errFun(err);
				process.exit(1);
			});
			// 字app
			app.use("/c", uiApp);
			this.ui = {
				app,
				uiServer
			};
		})
		.then(() => {
			if (port) {
				return ws(this.ui.uiServer, this);
			}
		})
		.then(wsServer => {
			if (wsServer) {
				this.ui.wsServer = wsServer;
			}
		});
	}
	// 出错处理
	errorHandle(err) {
		if (err) {
			log.error(err);
		}
		return Promise.reject(err);
	}
	// 根据配置创建服务器
	createServer() {
		let opt = config.get();
		let servers = this.servers || [];
		let com = this;
		// 可以自定义server或者用系统内置的server
		if (opt.type === 'http' && !servers[0]) {
			servers[0] = http.createServer();
		} else if (opt.type === 'https' && !servers[0]){
			// 找到证书，创建https的服务器
			let {privateKey: key, cert} = getCert(opt.certHost);
			servers[0] = https.createServer({key,cert, rejectUnauthorized: false, SNICallback});
		} else if (opt.type === 'all' && !servers[0]  && !servers[1]) {
			servers[0] = http.createServer();
			let {privateKey: key, cert} = getCert(opt.certHost);
			servers[1] = https.createServer({key,cert, rejectUnauthorized: false, SNICallback});
		}
		let requestFun = requestMiddleware.middleWare(requestHandler);
		servers.forEach(server => {
			server.catProxy = com;
			// 如果在http下代理https，则需要过度下请求
			if (server instanceof  http.Server) {
				server.on('connect', requestConnectHandler);
			}
			server.on('upgrade', requestUpgradeHandler);
			server.on('request', function(req, res) {
				if (req.headers.upgrade) {
					return;
				}
				requestFun.call(this, req, res);
			});
			server.on('clientError', function(err, con) {
				log.error('ser-clientError' + err);
			});
			let serverType = server instanceof  http.Server ? 'http' : 'https';
			let port = serverType === 'http' ? opt.port : opt.httpsPort;
			// 如果server没有被监听，则调用默认端口监听
			if (!server.listening) {
				// 根据server的类型调用不同的端口
				server.listen(port, function () {
					log.info('代理服务器启动于：' + `${serverType}://${localIps[0]}:${port}`);
				});
			}
			server.on('error', function(err) {
				errFun(err);
				process.exit(1);
			});
		});
		this.servers = servers;
	}
	// 想服务器添加request事件
	use (fun) {
		requestMiddleware.use(fun);
		return this;
	}
	// 在中转请求前，可以用于修改reqInfo
	onBeforeReq(...fun) {
		fun.forEach(f => util.isFunction(f) && this._beforeReqEvt.push(f));
	}
	// 请求结束，可以用于产看请求结果
	onAfterRes(...fun) {
		fun.forEach(f => util.isFunction(f) && this._afterResEvt.push(f));
	}
	// 获得中转请求前，可以用于修改resInfo
	onBeforeRes(...fun) {
		fun.forEach(f => util.isFunction(f) && this._beforeResEvt.push(f));
	}
	onPipeRequest(...fun) {
		fun.forEach(f => util.isFunction(f) && this._pipeRequestEvt.push(f));
	}
	/**
	 * 触发req事件，result表示参数，context表示上下文
	 * result 格式看evt中的格式
	 */
	triggerBeforeReq (result, context) {
		return execArrByStep(this._beforeReqEvt, result, context);
	}
	/**
	 * 触发 请求前事件
	 *  result 格式看evt中的格式
	 *  context为上下文
	 */
	triggerBeforeRes (result, context) {
		return execArrByStep(this._beforeResEvt, result, context);
	}
	/**
	 * 触发请求后事件
	 *  result 格式看evt中的格式
	 *  context为上下文
	 */
	triggerAfterRes (result, context) {
		if (this._afterResEvt.length) {
			this._afterResEvt.forEach(current => {
				try{ 
					current.call(context, result);
				} catch (e) {
					log.error(e);
				}
			});
		}
	}
	/**
	 * 触发穿过请求
	 *  result 格式看evt中的格式
	 *  context为上下文
	 */
	triggerPipeReq (result, context) {
		if (this._pipeRequestEvt.length) {
			this._pipeRequestEvt.forEach(current => {
				try{ 
					current.call(context, result);
				} catch (e) {
					log.error(e);
				}
			});
		}
	}
}
export default CatProxy;
export {CatProxy};
