namespace _espe.service.member
{
	
	/**
	 */
	export type type_id = int;
	
	
	/**
	 */
	export type type_value = {
		membership_number : string;
		enabled : boolean;
		name_real_value : string;
		name_real_extension : (null | string);
		name_display : (null | string);
		name_login : string;
		password_image : (null | string);
		email_address_private_value : (null | string);
		email_address_numberbased_use : boolean;
		email_address_namebased_use : boolean;
		email_redirect_to_private : boolean;
		salutation : (null | string);
	};
	
	
	/**
	 */
	var _hooks_change : Array<() => void> = [];
	
	
	/**
	 */
	export function listen_change(
		procedure : () => void
	) : void
	{
		_hooks_change.push(procedure);
	}
	
	
	/**
	 */
	function notify_change(
	) : void
	{
		_hooks_change.forEach(
			procedure => {
				procedure();
			}
		);
	}
	
	
	/**
	 */
	function store(
	) : lib_plankton.storage.type_store<type_id, Record<string, any>, {}, lib_plankton.storage.type_sql_table_autokey_search_term, Record<string, any>>
	{
		const store : lib_plankton.storage.type_store<type_id, Record<string, any>, {}, lib_plankton.storage.type_sql_table_autokey_search_term, Record<string, any>> = lib_plankton.storage.sql_table_autokey_store(
			{
				"database_implementation": _espe.helpers.database_implementation(),
				"table_name": "members",
				"key_name": "id",
			}
		);
		return store;
	}
	
	
	/**
	 */
	function encode(
		value : type_value
	) : Record<string, any>
	{
		return {
			"membership_number": value.membership_number,
			"enabled": (value.enabled ? 1 : 0),
			"name_real_value": value.name_real_value,
			"name_real_extension": value.name_real_extension,
			"name_display": value.name_display,
			"name_login": value.name_login,
			"password_image": value.password_image,
			"email_address_private_value": value.email_address_private_value,
			"email_address_numberbased_use": (value.email_address_numberbased_use ? 1 : 0),
			"email_address_namebased_use": (value.email_address_namebased_use ? 1 : 0),
			"email_redirect_to_private": (value.email_redirect_to_private ? 1 : 0),
			"salutation": value.salutation,
		};
	}
	
	
	/**
	 */
	function decode(
		row : Record<string, any>
	) : type_value
	{
		return {
			"membership_number": row["membership_number"],
			"enabled": (row["enabled"] > 0),
			"name_real_value": row["name_real_value"],
			"name_real_extension": row["name_real_extension"],
			"name_display": row["name_display"],
			"name_login": row["name_login"],
			"password_image": row["password_image"],
			"email_address_private_value": row["email_address_private_value"],
			"email_address_numberbased_use": (row["email_address_numberbased_use"] > 0),
			"email_address_namebased_use": (row["email_address_namebased_use"] > 0),
			"email_redirect_to_private": (row["email_redirect_to_private"] > 0),
			"salutation": row["salutation"],
		};
	}
	
	
	/**
	 */
	export async function dump(
	) : Promise<Array<{id : type_id; value : type_value;}>>
	{
		const my_store = store();
		
		return (
			(await my_store.search(null))
			.map(
				({"key": key, "preview": preview}) => ({
					"id": key,
					"value": (preview as type_value),
				})
			)
		);
	}
	
	
	/**
	 */
	export async function list(
	) : Promise<Array<{id : type_id; preview : {membership_number : string, name_real_value : string;};}>>
	{
		const my_store = store();
		
		return (
			(await my_store.search(null))
			.map(
				({"key": key, "preview": preview}) => ({
					"id": key,
					"preview": {
						"membership_number": preview["membership_number"],
						"name_real_value": preview["name_real_value"],
					}
				})
			)
		);
	}
	
	
	/**
	 * @todo data
	 */
	export async function get(
		id : type_id
	) : Promise<type_value>
	{
		const my_store = store();
		
		const row : Record<string, any> = await my_store.read(id);
		
		return decode(row);
	}
	
	
	/**
	 */
	export async function add(
		value : type_value
	) : Promise<type_id>
	{
		const my_store = store();
		
		const row : Record<string, any> = encode(value);
		const id : type_id = await my_store.create(row);
		
		notify_change();
		
		return id;
	}
	
	
	/**
	 */
	export async function modify(
		id : type_id,
		value : type_value
	) : Promise<void>
	{
		const my_store = store();
		
		const row : Record<string, any> = encode(value);
		
		await my_store.update(id, row);
		
		notify_change();
	}
	
	
	/**
	 */
	export async function remove(
		id : type_id
	) : Promise<void>
	{
		const my_store = store();
		
		await my_store.delete(id);
		
		notify_change();
	}
	
	
	/**
	 */
	export async function register(
		id : type_id,
		data : {
			name_login : string;
			name_display : string;
			salutation : string;
			email_address_numberbased_use : boolean;
			email_address_namebased_use : boolean;
			email_redirect_to_private : boolean;
			password : string;
		}
	) : Promise<void>
	{
		const member : type_value = await get(id);
		
		if (member.name_login !== null) {
			return Promise.reject(new Error("already registered"));
		}
		else {
			member.name_login = data.name_login;
			member.name_display = data.name_display;
			member.salutation = data.salutation;
			member.email_address_numberbased_use = data.email_address_numberbased_use
			member.email_address_namebased_use = data.email_address_namebased_use
			member.email_redirect_to_private = data.email_redirect_to_private;
			member.password_image = await _espe.helpers.bcrypt_compute(data.password);
			
			await modify(id, member);
		}
	}
	
	
	/**
	 */
	function derive_email_address_namebased(
		value : type_value
	) : string
	{
		return lib_plankton.string.coin(
			"{{value}}{{extension}}@{{domain}}",
			{
				"value": value.name_real_value.toLowerCase().replace(new RegExp(" ", "g"), "."),
				"extension": (
					(
						(value.name_real_extension === null)
						||
						(value.name_real_extension === "")
					)
					? ""
					: ("." + value.name_real_extension)
				),
				"domain": _espe.conf.get().settings.target_domain,
			}
		);
	}
	
	
	/**
	 */
	function derive_email_address_numberbased(
		value : type_value
	) : string
	{
		return lib_plankton.string.coin(
			"{{prefix}}{{membership_number}}@{{domain}}",
			{
				"prefix": _espe.conf.get().settings.prefix_for_numberbased_email_addresses,
				"membership_number": value.membership_number,
				"domain": _espe.conf.get().settings.target_domain,
			}
		);
	}
	
	
	/**
	 */
	function derive_email_address_to_use(
		value : type_value
	) : (null | string)
	{
		return (
			value.email_address_namebased_use
			? derive_email_address_namebased(value)
			: (
				value.email_address_numberbased_use
				? derive_email_address_numberbased(value)
				: value.email_address_private_value
			)
		);
	}
	
	
	/**
	 * @todo check validity (e.g. username characters)
	 */
	export async function export_authelia_user_file(
	) : Promise<string>
	{
		const nm_yaml = require("yaml");
		
		return lib_plankton.call.convey(
			await dump(),
			[
				x => x.map(
					entry => Object.assign(
						entry,
						{"email_address": derive_email_address_to_use(entry.value)}
					)
				),
				x => x.filter(
					entry => (
						(
							(entry.value.name_login !== null)
							&&
							(entry.value.name_login !== "")
						)
						&&
						(
							(entry.value.password_image !== null)
							&&
							(entry.value.password_image !== "")
						)
						&&
						(entry.email_address !== null)
					)
				),
				x => x.map(
					entry => ([
						entry.value.name_login,
						{
							"disabled": (! entry.value.enabled),
							"displayname": (
								(
									(entry.value.name_display !== null)
									&&
									(entry.value.name_display !== "")
								)
								? entry.value.name_display
								: entry.value.name_real_value
							),
							"email": entry.email_address,
							"groups": [],
							"password": entry.value.password_image,
						}
					])
				),
				Object.fromEntries,
				x => ({"users": x}),
				x => nm_yaml.stringify(x),
			]
		);
	}
	
}

