namespace _heimdall.check_kinds.http_request
{
	
	/**
	 */
	function parameters_schema(
	) : _heimdall.helpers.json_schema.type_schema
	{
		return {
			"type": "object",
			"additionalProperties": false,
			"properties": {
				"request": {
					"type": "object",
					"additionalProperties": false,
					"properties": {
						"target": {
							"description": "URL",
							"type": "string"
						},
						"method": {
							"type": "string",
							"enum": [
								"GET",
								"POST"
							],
							"default": "GET"
						}
					},
					"required": [
						"target"
					]
				},
				"timeout": {
					"description": "maximum allowed execution time in seconds",
					"type": "number",
					"default": 5.0
				},
				"follow_redirects": {
					"description": "whether redirect instructions in responses shall be followend instead of being exposed as result",
					"type": "boolean",
					"default": false
				},
				"response": {
					"type": "object",
					"additionalProperties": false,
					"properties": {
						"status_code": {
							"description": "checks whether the response status code is this",
							"anyOf": [
								{
									"type": "null",
								},
								{
									"type": "integer",
								},
							],
							"default": 200
						},
						"headers": {
							"description": "conjunctively checks header key-value pairs",
							"type": "object",
							"additionalProperties": {
								"description": "header value",
								"type": "string"
							},
							"properties": {
							},
							"required": [
							],
							"default": {}
						},
						"body_part": {
							"description": "checks whether the response body contains this string",
							"type": "string"
						}
					},
					"required": [
					]
				},
				"critical": {
					"description": "whether a violation of this check shall be leveled as critical instead of concerning",
					"type": "boolean",
					"default": true
				},
				"strict": {
					"deprecated": true,
					"description": "alias for 'critical'",
					"type": "boolean",
					"default": true
				},
			},
			"required": [
				"request",
			]
		};
	}
	
	
	/**
	 */
	function normalize_order_node(
		node : any
	) : any
	{
		const version : string = (
			(! ("critical" in node))
			? "v1"
			: "v2"
		);
		
		switch (version) {
			default: {
				throw (new Error("unhandled version"));
				break;
			}
			case "v1": {
				const node_ = lib_plankton.object.patched(
					{
						"request": {
							"method": "GET"
						},
						"timeout": 5.0,
						"follow_redirects": false,
						"response": {
							"status_code": 200
						},
						"strict": true,
					},
					node,
					true
				);
				const allowed_methods : Array<string> = ["GET", "POST"];
				if (! allowed_methods.includes(node_["request"]["method"])) {
					throw (new Error("invalid HTTP request method: " + node_["request"]["method"]));
				}
				else {
					return {
						"request": node_["request"],
						"timeout": node_["timeout"],
						"follow_redirects": node_["follow_redirects"],
						"response": node_["response"],
						"critical": node_["strict"],
					};
				}
				break;
			}
			case "v2": {
				const node_ = lib_plankton.object.patched(
					{
						"request": {
							"method": "GET"
						},
						"timeout": 5.0,
						"follow_redirects": false,
						"response": {
							"status_code": 200
						},
						"critical": true,
					},
					node,
					true
				);
				const allowed_methods : Array<string> = ["GET", "POST"];
				if (! allowed_methods.includes(node_["request"]["method"])) {
					throw (new Error("invalid HTTP request method: " + node_["request"]["method"]));
				}
				else {
					return node_;
				}
				break;
			}
		}
	}
	
	
	/**
	 */
	async function run(
		parameters : {
			request : {
				target : string;
				method : string;
			};
			timeout : float;
			follow_redirects : boolean;
			response : {
				status_code : int;
				headers ?: Record<string, string>;
				body_part ?: string;
			};
			critical : boolean;
		}
	) : Promise<_heimdall.type_result>
	{
		let error : (null | Error);
		const url : URL = new URL(parameters.request.target);
		const http_request : lib_plankton.http.type_request = {
			"version": "HTTP/1.1",
			"scheme": (
				(url.protocol.slice(0, -1) === "https")
				? "https"
				: "http"
			),
			"host": url.host,
			"method": {
				"GET": lib_plankton.http.enum_method.get,
				"POST": lib_plankton.http.enum_method.post,
			}[parameters.request.method],
			"path": url.pathname,
			"query": url.search,
			"headers": {
				"Host": url.host,
			},
			"body": null,
		};
		let http_response : (null | lib_plankton.http.type_response);
		try {
			http_response = await lib_plankton.http.call(
				http_request,
				{
					"timeout": parameters.timeout,
					"follow_redirects": parameters.follow_redirects,
					"implementation": "fetch",
				}
			);
			error = null;
		}
		catch (error_) {
			http_response = null;
			error = error_;
		}
		if (http_response === null) {
			return {
				"condition": (
					parameters.critical
					? _heimdall.enum_condition.critical
					: _heimdall.enum_condition.concerning
				),
				"info": {
					"request": parameters.request,
					"faults": [
						lib_plankton.translate.get("checks.http_request.request_failed", {"reason": error.toString()}),
					],
				},
			};
		}
		else {
			let faults : Array<string> = [];
			// status code
			{
				if (
					(! ("status_code" in parameters.response))
					||
					(parameters.response.status_code === null)
				) {
					// do nothing
				}
				else {
					const status_code_expected : int = (parameters.response.status_code as int);
					if (! (http_response.status_code === status_code_expected)) {
						faults.push(
							lib_plankton.translate.get(
								"checks.http_request.status_code_mismatch",
								{
									"status_code_actual": http_response.status_code.toFixed(0),
									"status_code_expected": status_code_expected.toFixed(0),
								}
							)
						);
					}
					else {
						// do nothing
					}
				}
			}
			// headers
			{
				if (
					(! ("headers" in parameters.response))
					||
					(parameters.response.headers === null)
				) {
					// do nothing
				}
				else {
					const headers_expected : Record<string, string> = (parameters.response.headers as Record<string, string>);
					Object.entries(headers_expected).forEach(
						([header_key, header_value]) => {
							if (
								(! (header_key in http_response.headers))
								&&
								(! (header_key.toLowerCase() in http_response.headers))
							) {
								faults.push(
									lib_plankton.translate.get(
										"checks.http_request.header_missing",
										{
											"key": header_key,
											"value_expected": header_value,
										}
									)
								);
							}
							else {
								if (
									(! (http_response.headers[header_key] === header_value))
									&&
									(! (http_response.headers[header_key.toLowerCase()].toLowerCase() === header_value.toLowerCase()))
								) {
									faults.push(
										lib_plankton.translate.get(
											"checks.http_request.header_value_mismatch",
											{
												"key": header_key,
												"value_actual": http_response.headers[header_key],
												"value_expected": header_value,
											}
										)
									);
								}
								else {
									// do nothing
								}
							}
						}
					);
				}
			}
			// body
			{
				if (
					(! ("body_part" in parameters.response))
					||
					(parameters.response.body_part === null)
				) {
					// do nothing
				}
				else {
					const body_part : string = (parameters.response.body_part as string);
					if (! http_response.body.toString().includes(body_part)) {
						faults.push(
							lib_plankton.translate.get(
								"checks.http_request.body_misses_part",
								{
									"part": body_part,
								}
							)
						);
					}
					else {
						// do nothing
					}
				}
			}
			
			return {
				"condition": (
					(faults.length <= 0)
					? _heimdall.enum_condition.ok
					: (
						parameters.critical
						? _heimdall.enum_condition.critical
						: _heimdall.enum_condition.concerning
					)
				),
				"info": {
					"request": parameters.request,
					"response": {
						"status_code": http_response.status_code,
						"headers": http_response.headers,
						// "body": http_response.body,
					},
					"faults": faults,
				}
			};
		}
	}
	
	
	/**
	 */
	register_implementation(
		"http_request",
		{
			"parameters_schema": parameters_schema,
			"normalize_order_node": normalize_order_node,
			"run": run,
		}
	);
	
}
