/*
Espe | Ein schlichtes Werkzeug zur Mitglieder-Verwaltung | Frontend
Copyright (C) 2024 Christian Fraß

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public 
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later 
version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see 
<https://www.gnu.org/licenses/>. 
 */

namespace _espe.backend
{
	
	/**
	 */
	var _session_key_chest : lib_plankton.storage.type_chest<string, string, void, string, string>;
	
	
	/**
	 * @todo use backend call
	 */
	export async function logged_in(
	) : Promise<boolean>
	{
		let session_key : (null | string);
		try {
			session_key = (await _session_key_chest.read("session_key"));
		}
		catch (error) {
			session_key = null;
		}
		return Promise.resolve<boolean>(session_key !== null);
	}
	
	
	/**
	 */
	export function init(
	) : Promise<void>
	{
		_session_key_chest = lib_plankton.storage.localstorage.implementation_chest(
			{
				"corner": "aum",
			}
		);
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 */
	async function abstract_call<type_input, type_output>(
		http_method : string,
		path : string,
		options : {
			data ?: type_input;
			custom_response_handlers ?: Record<int, ((output_data_raw : any) => Promise<type_output>)>;
		} = {}
	) : Promise<type_output>
	{
		options = Object.assign(
			{
				"data": null,
				"custom_response_handlers": [],
			},
			options
		);
		
		let session_key : (null | string);
		try {
			session_key = (await _session_key_chest.read("session_key"));
		}
		catch (error) {
			session_key = null;
		}
		
		const without_content : boolean = ["HEAD","OPTIONS","GET"].includes(http_method);
		return (
			fetch(
				lib_plankton.string.coin(
					("{{scheme}}://{{host}}:{{port}}{{path_base}}{{path_action}}"),
					{
						"scheme": _espe.conf.get().backend.scheme,
						"host": _espe.conf.get().backend.host,
						"port": _espe.conf.get().backend.port.toFixed(0),
						"path_base": _espe.conf.get().backend.path_base,
						"path_action": path,
					}
				),
				{
					"method": http_method,
					"headers": Object.assign(
						{},
						((! without_content) ? {"Content-Type": "application/json"} : {}),
						((session_key !== null) ? {"X-Session-Key": session_key} : {})
					),
					"body": (
						without_content
						? null
						: JSON.stringify(options.data)
					),
				}
			)
			.then(
				x => {
					if (x.status in options.custom_response_handlers) {
						return x.json().then(output_data_raw => options.custom_response_handlers[x.status](output_data_raw));
					}
					else {
						if ((x.status >= 200) && (x.status < 300)) {
							return x.json();
						}
						else {
							return Promise.reject<type_output>(new Error("irregular response status code"));
						}
					}
				}
			)
		);
	}

	
	/**
	 */
	export async function login(
		name : string,
		password : string
	) : Promise<void>
	{
		const session_key : string = await abstract_call<{name : string; password : string;}, string>(
			"POST",
			"/session/begin",
			{
				"data": {
					"name": name,
					"password": password,
				}
			}
		);
		await _session_key_chest.write("session_key", session_key);
	}
	
	
	/**
	 */
	export async function logout(
	) : Promise<void>
	{
		try {
			await abstract_call<void, null>(
				"DELETE",
				"/session/end"
			);
		}
		catch (error) {
			// do nothing
		}
		await _session_key_chest.delete("session_key");
	}
	
	
	/**
	 */
	export async function email(
		receivers : Array<string>,
		subject : string,
		content : string
	) : Promise<void>
	{
		return (
			abstract_call(
				"POST",
				"/email",
				{
					"data": {
						"receivers": receivers,
						"subject": subject,
						"content": content,
					},
				}
			)
		);
	}
	
	
	/**
	 */
	export async function verification_get(
		data : any
	) : Promise<string>
	{
		return (
			abstract_call(
				"POST",
				"/verification/get",
				{
					"data": {
						"data": data,
					},
				}
			)
		);
	}
	
	
	/**
	 */
	export async function verification_check(
		data : any,
		verification : string
	) : Promise<boolean>
	{
		return (
			abstract_call(
				"POST",
				"/verification/check",
				{
					"data": {
						"data": data,
						"verification": verification,
					}
				}
			)
		);
	}
	
	
	/**
	 */
	export async function group_list(
	) : Promise<
		Array<
			{
				id : int;
				name : string;
			}
		>
	>
	{
		return (
			abstract_call(
				"GET",
				"/group/list"
			)
		);
	}
	
	
	/**
	 */
	export async function group_get(
		group_id : int
	) : Promise<
		{
			name : string;
		}
	>
	{
		return (
			abstract_call(
				"GET",
				("/group/read/" + group_id.toFixed(0))
			)
		);
	}
	
	
	/**
	 */
	export async function group_add(
		group_object : {
			name : string;
		}
	) : Promise<
		int
	>
	{
		return (
			abstract_call(
				"POST",
				"/group/add",
				{
					"data": group_object,
				}
			)
		);
	}
	
	
	/**
	 */
	export async function group_modify(
		group_id : int,
		group_object : {
			name : string;
		}
	) : Promise<
		void
	>
	{
		return (
			abstract_call(
				"PATCH",
				("/group/modify/" + group_id.toFixed(0)),
				{
					"data": group_object,
				}
			)
		);
	}
	
	
	/**
	 */
	export async function member_list(
		search_term : (null | string)
	) : Promise<
		Array<
			{
				id : int;
				preview : {
					name : string;
				};
			}
		>
	>
	{
		return (
			abstract_call(
				"GET",
				("/member/list" + ((search_term === null) ? "" : ("?search_term=" + search_term)))
			)
		);
	}
	
	
	/**
	 */
	export async function member_get(
		id : int
	) : Promise<
		{
			name : string;
			email_address : (null | string);
			groups : Array<int>;
			enabled : boolean;
			password_set : boolean;
		}
	>
	{
		return abstract_call(
			"GET",
			("/member/read/" + id.toFixed(0))
		);
	}
	
	
	/**
	 */
	export async function member_info(
		id : int,
		key : string
	) : Promise<
		{
			name_real_value : string;
			name_real_index : int;
			name_login : string;
			email_address_veiled : string;
			email_address_nominal : string;
		}
	>
	{
		return abstract_call(
			"GET",
			lib_plankton.string.coin(
				"/member/info/{{id}}?key={{key}}",
				{
					"id": id.toFixed(0),
					"key": key,
				}
			)
		);
	}
	
	
	/**
	 */
	export async function member_register(
		id : int,
		verification : string,
		data : {
			email_use_veiled_address : boolean;
			email_use_nominal_address : boolean;
			email_redirect_to_private_address : boolean;
			password : (null | string);
		},
		notification_target_url_template : string
	) : Promise<
		Array<
			{
				incident : string;
				details : Record<string, any>;
			}
		>
	>
	{
		return (
			abstract_call(
				"POST",
				lib_plankton.string.coin(
					"/member/register/{{id}}?key={{key}}",
					{
						"id": id.toFixed(0),
						"key": verification,
					}
				),
				{
					"data": Object.assign(
						data,
						{"notification_target_url_template": notification_target_url_template}
					),
					"custom_response_handlers": {
						409: (output_data_raw) => output_data_raw,
					},
				}
			)
		);
	}
	
	
	/**
	 */
	export async function member_modify(
		id : int,
		data : {
			email_address : (null | string);
			groups : Array<int>;
			enabled : boolean;
		}
	) : Promise<void>
	{
		return abstract_call(
			"PATCH",
			("/member/modify/" + id.toFixed(0)),
			{
				"data": data
			}
		);
	}
	
	
	/**
	 */
	export async function member_summon(
		id : int,
		url_template : string
	) : Promise<string>
	{
		return (
			abstract_call(
				"POST",
				("/member/summon/" + id.toFixed(0)),
				{
					"data": {
						"url_template": url_template,
					},
				}
			)
		);
	}
	
	
	/**
	 */
	export async function member_password_change_initialize(
		identifier : string,
		url_template : string
	) : Promise<string>
	{
		return (
			abstract_call(
				"POST",
				"/member/password_change/initialize",
				{
					"data": {
						"identifier": identifier,
						"url_template": url_template,
					},
				}
			)
		);
	}
	
	
	/**
	 */
	export async function member_password_change_execute(
		id : int,
		token : string,
		password_new : string
	) : Promise<
		Array<
			{
				incident : string;
				details : Record<string, any>;
			}
		>
	>
	{
		return (
			abstract_call(
				"PATCH",
				("/member/password_change/execute/" + id.toFixed(0)),
				{
					"data": {
						"token": token,
						"password_new": password_new,
					},
					"custom_response_handlers": {
						409: (output_data_raw) => output_data_raw,
					},
				}
			)
		);
	}
	
	
	/**
	 */
	export async function invite_list(
	) : Promise<
		Array<
			{
				id : int;
				key : string;
				expiry : (null | int);
				name_value : string;
			}
		>
	>
	{
		return abstract_call(
			"GET",
			lib_plankton.string.coin(
				"/invitation/list",
				{
				}
			)
		);
	}
	
	
	/**
	 */
	export async function invite_read(
		id : int
	) : Promise<
		{
			key : string;
			expiry : (null | int);
			name_changeable : boolean;
			name_value : string;
			email_address_changeable : boolean;
			email_address_value : (null | string);
			groups_changeable : boolean;
			groups_value : Array<int>;
		}
	>
	{
		return abstract_call(
			"GET",
			lib_plankton.string.coin(
				"/invitation/read?id={{id}}",
				{
					"id": id.toFixed(0),
				}
			)
		);
	}
	
	
	/**
	 */
	export async function invite_create(
		data : {
			name_changeable : boolean;
			name_value : string;
			email_address_changeable : boolean;
			email_address_value : (null | string);
			groups_changeable : boolean;
			groups_value : Array<int>;
			expiry : (null | int);
		},
		send_immediatly : boolean,
		notification_target_url_template : (null | string)
	) : Promise<
		{
			id : int;
			key : string;
		}
	>
	{
		return abstract_call(
			"POST",
			"/invitation/create",
			{
				"data": {
					"data": data,
					"notification_target_url_template": notification_target_url_template,
					"send_immediatly": send_immediatly,
				}
			}
		);
	}
	
	
	/**
	 */
	export async function invite_examine(
		key : string
	) : Promise<
		{
			name_changeable : boolean;
			name_value : string;
			email_address_changeable : boolean;
			email_address_value : (null | string);
			groups_changeable : boolean;
			groups_value : Array<int>;
		}
	>
	{
		return abstract_call(
			"GET",
			lib_plankton.string.coin(
				"/invitation/examine?key={{key}}",
				{
					"key": key
				}
			)
		);
	}
	
	
	/**
	 */
	export async function invite_accept(
		key : string,
		data : {
			name : string;
			groups : Array<int>;
			email_address : (null | string);
			password : string;
		}
	) : Promise<
		Array<
			{
				incident : string;
				details : Record<string, any>;
			}
		>
	>
	{
		return abstract_call(
			"POST",
			"/invitation/accept",
			{
				"data": {
					"key": key,
					"data": {
						"name": data.name,
						"groups": data.groups,
						"email_address": data.email_address,
						"password": data.password,
					},
				}
			}
		);
	}
	
}
