
/** 默认头信息 */
const DEFAULT_HEAD = {
	'Content-Type': 'application/json',
	'Accept-Charset': 'utf-8',
	/** 为解决跨域问题 */
	'Origin': '*'
};

type HTTPRequestMethod = "CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE";

const HREQM: { [key in HTTPRequestMethod]: string } = {
	CONNECT: 'lightslategray',
	HEAD: 'cadetblue',
	OPTIONS: 'thistle',
	GET: 'lightgreen',
	PUT: 'darkcyan',
	DELETE: 'lightcoral',
	POST: 'deepskyblue',
	PATCH: 'silver',
	TRACE: 'steelblue',
}

/**
 * 创建请求头
 * @param conf
 */
function createHeader(conf?: { [field: string]: string }) {
	let header = new Headers();
	[DEFAULT_HEAD, conf].forEach(list => {
		if (!list || list == null) return void 0;
		for (let field in list) header.append(field, Reflect.get(list, field));
	});
	return header;
}

/**
 * 格式化请求数据
 * @param param 请求数据
 * @param type 格式类型
 */
function FormateData(param: { [name: string]: string | number }, type: 'json' | 'form' | 'search') {
	let data: string | FormData;
	if (type === 'json') data = JSON.stringify(param);
	else if (type === 'search') {
		let arr = [];
		for (let name in param) arr.push(`${name}=${param[name]}`);
		data = arr.join('&');
	} else if (type === 'form') {
		data = new FormData();
		for (let name in param) data.set(name, String(param[name]));
	}

	return data;
}

/**
 * 创建一个请求对象
 * @param url 请求路径
 * @param data 请求参数
 * @param method 请求方法类型
 * @param head 请求头配置
 */
function createRequest(url: string, data: any, method?: HTTPRequestMethod, head?: { [name: string]: string }) {

	let body = FormateData(data, 'json');
	let options: RequestInit = {
		method: method ?? "GET",
		headers: createHeader(head)
	}

	if (options.method == 'POST') options.body = body;

	let request = new Request(url, options);

	return request;
}

/** 向服务器发送请求 */
export async function http_send(...args: Parameters<typeof createRequest>) {
	args[2] = args[2] ?? 'GET';
	let req_token = mtec.string.randomToken(8, 36, t => !tokan_map.has(t));
	tokan_map.set(req_token, tokan_map.size);

	// mtec.log.tag([
	// 	['SERVICE', 'slategray'],
	// 	[args[2], Reflect.get(HREQM, args[2])],
	// 	['>>', 'dodgerblue']
	// ].map(_=>_.join(':')).join(';'), args[0].split('/').slice(3).join('/'), '\n--------------\nHEADERS:', args[3], '\nDATA:', args[1], '\n--------------\n');

	let start_date = new Date();
	let response: Response;
	let delay = 0;
	let data: any;
	let try_count = 0;

	while (!response?.ok && try_count < 3) {
		response = await fetch(createRequest(...args))
			.then(res => res)
			.catch(err => {
				// console.log('请求失败信息', JSON.stringify(err));
				log_response('单次请求失败', req_token, args[0], start_date, delay, try_count, data, -100, undefined);
				return void 0;
			});

		delay = Date.now() - start_date.valueOf();

		data = response?.ok ? await response.json() : undefined;

		try_count++;
		if (response && response.ok) {

		} else {
			__exception_call_list__.forEach(call => call(req_token, try_count, delay, response?.status ?? -101));
		}
	}

	if (try_count > 1) {
		log_response('触发接口重试', req_token, args[0], start_date, delay, try_count, data, response?.status ?? -102, response);
		__exception_call_list__.forEach(call => call(req_token, try_count, delay, response?.status ?? -102));
	} else if (delay > 5000) {
		log_response('请求延迟过高', req_token, args[0], start_date, delay, try_count, data, response?.status ?? -103, response);
		__exception_call_list__.forEach(call => call(req_token, try_count, delay, response?.status ?? -103));
	}

	if (!response?.ok) {
		log_response('异常接口最终信息', req_token, args[0], start_date, delay, try_count, data, response?.status ?? -104, response);
		__exception_call_list__.forEach(call => call(req_token, try_count, delay, response?.status ?? -104));
	}

	mtec.log.tag([
		['SERVICE', 'slategray'],
		[args[2], Reflect.get(HREQM, args[2])],
		['<<', 'seagreen'],
		data ? ['OK', 'springgreen'] : ['ERR', 'red'],
		[delay + ' ms', 'silver']
	].map(_ => _.join(':')).join(';'), args[0].split('/').slice(3).join('/'), '\n--------------\nRESPONSE:', response, '\nDATA:', data, '\n--------------\n');

	tokan_map.delete(req_token);

	return data;
}

type __exception_call__ = (token: string, try_count: number, delay: number, status: number) => void;

const __exception_call_list__: Array<__exception_call__> = [];
const tokan_map: Map<string, number> = new Map();

export function on_network_exception(call: __exception_call__) {
	__exception_call_list__.push(call);
}

function log_response(tag: string, token: string, url: string, start_date: Date, delay: number, try_count: number, data: any, status: number, response: Response) {
	// console.log(tag,
	// 	'\n请求接口: '+url,
	// 	'\n请求Token: '+token,
	// 	'\n请求时间: '+start_date.toLocaleString(),
	// 	'\n请求延迟: '+delay+' ms',
	// 	'\n请求次数: '+try_count,
	// 	'\n请求数据: '+JSON.stringify(data),
	// 	'\n请求状态: '+status,
	// 	'\n请求结果[Response]: '+JSON.stringify(response)
	// );
}