/*
This file is part of »zeitbild«.

Copyright 2025 'kcf' <fenris@folksprak.org>

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

»zeitbild« 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with »zeitbild«. If not, see <http://www.gnu.org/licenses/>.
 */


namespace _zeitbild.repository.user
{
	
	/**
	 */
	type type_core_row = {
		name : string;
		email_address : (null | string);
		dav_token : (null | string);
	};
	
	
	/**
	 */
	type type_group_row_slim = {
		group_id : int;
	};
	
	
	/**
	 */
	type type_group_row_fat = {
		user_id : int;
		group_id : int;
	};
	
	
	/**
	 */
	type type_preview = {
		name : string;
	};
	
	
	/**
	 */
	type type_dispersal = {
		core : type_core_row;
		groups : Array<type_group_row_slim>;
	};
	
	
	/**
	 */
	var _store_core : (
		null
		|
		lib_plankton.storage.type_store<
			_zeitbild.type_user_id,
			/*type_core_row*/Record<string, any>,
			{},
			lib_plankton.storage.type_sql_table_autokey_search_term,
			/*type_preview*/Record<string, any>
		>
	) = null;
	
	
	/**
	 */
	var _store_groups : (
		null
		|
		lib_plankton.storage.type_store<
			int,
			/*type_group_row_fat*/Record<string, any>,
			{},
			lib_plankton.storage.type_sql_table_autokey_search_term,
			Record<string, any>
		>
	) = null;
	
	
	/**
	 */
	function get_store_core(
	)
	: lib_plankton.storage.type_store<
		_zeitbild.type_user_id,
		/*type_core_row*/Record<string, any>,
		{},
		lib_plankton.storage.type_sql_table_autokey_search_term,
		/*type_preview*/Record<string, any>
	>
	{
		if (_store_core === null)
		{
			_store_core = lib_plankton.storage.sql_table_autokey_store(
				{
					"database_implementation": _zeitbild.database.get_implementation(),
					"table_name": "users",
					"key_name": "id",
				}
			);
		}
		else
		{
			// do nothing
		}
		return _store_core;
	}
	
	
	/**
	 */
	function get_store_groups(
	)
	: lib_plankton.storage.type_store<
		int,
		/*type_group_row_fat*/Record<string, any>,
		{},
		lib_plankton.storage.type_sql_table_autokey_search_term,
		Record<string, any>
	>
	{
		if (_store_groups === null)
		{
			_store_groups = lib_plankton.storage.sql_table_autokey_store(
				{
					"database_implementation": _zeitbild.database.get_implementation(),
					"table_name": "user_groups",
					"key_name": "id",
				}
			);
		}
		else
		{
			// do nothing
		}
		return _store_groups;
	}
	
	
	/**
	 */
	function encode(
		user_object : _zeitbild.type_user_object
	)
	: type_dispersal
	{
		return {
			"core": {
				"name": user_object.name,
				"email_address": user_object.email_address,
				"dav_token": user_object.dav_token,
			},
			"groups": (
				user_object.groups.map(
					group_id => (
						{
							"group_id": group_id,
						}
					)
				)
			),
		};
	}
	
	
	/**
	 */
	function decode(
		dispersal : type_dispersal
	)
	: _zeitbild.type_user_object
	{
		return {
			"name": dispersal.core.name,
			"groups": (
				dispersal.groups.map(
					group_row => group_row.group_id,
				)
			),
			"email_address": dispersal.core.email_address,
			"dav_token": dispersal.core.dav_token,
		};
	}
	
	
	/**
	 */
	export async function list(
	)
	: Promise<
		Array<
			{
				id : _zeitbild.type_user_id;
				name : string;
			}
		>
	>
	{
		const hits : Array<{key : int; preview : /*type_preview*/Record<string, any>;}> = await get_store_core().search(
			{
				"expression": "TRUE",
				"arguments": {}
			}
		);
		return Promise.resolve(
			hits
			.map(
				(hit) => ({
					"id": hit.key,
					"name": hit.preview.name,
				})
			)
		);
	}
	
	
	/**
	 */
	export async function read(
		user_id : _zeitbild.type_user_id
	)
	: Promise<_zeitbild.type_user_object>
	{
		const core_row : type_core_row = ((await get_store_core().read(user_id)) as type_core_row);
		const group_rows : Array<type_group_row_fat> = (
			(
				await get_store_groups().search(
					{
						"expression": "(user_id = $user_id)",
						"arguments": {
							"user_id": user_id,
						}
					}
				)
			)
			.map(
				hit => (
					{
						"user_id": hit.preview.user_id,
						"group_id": hit.preview.group_id,
					}
				)
			)
		);
		const dispersal : type_dispersal = {
			"core": core_row,
			"groups": (
				group_rows.map(
					group_row_fat => (
						{
							"group_id": group_row_fat.group_id,
						}
					)
				)
			)
		};
		const user_object : _zeitbild.type_user_object = decode(dispersal);
		return Promise.resolve<_zeitbild.type_user_object>(user_object);
	}
	
	
	/**
	 */
	export async function create(
		user_object : _zeitbild.type_user_object
	)
	: Promise<_zeitbild.type_user_id>
	{
		const dispersal : type_dispersal = encode(user_object);
		// core
		const user_id : _zeitbild.type_user_id = await (() => {
			return get_store_core().create(dispersal.core);
		}) ();
		// groups
		{
			for (const group_row_slim of dispersal.groups)
			{
				const group_row_fat : type_group_row_fat = {
					"user_id": user_id,
					"group_id": group_row_slim.group_id,
				};
				await get_store_groups().create(group_row_fat);
			}
		}
		return Promise.resolve<_zeitbild.type_user_id>(user_id);
	}
	
	
	/**
	 */
	export async function update(
		user_id : _zeitbild.type_user_id,
		user_object : _zeitbild.type_user_object
	)
	: Promise<void>
	{
		const dispersal : type_dispersal = encode(user_object);
		// core
		{
			await get_store_core().update(user_id, dispersal.core);
		}
		// groups
		{
			const hits : Array<{key : int; preview : Record<string, any>;}> = await get_store_groups().search(
				{
					"expression": "(user_id = $user_id)",
					"arguments": {
						"user_id": user_id,
					}
				}
			);
			const contrast = lib_plankton.list.contrast(
				hits,
				hit => hit.preview.group_id.toFixed(0),
				dispersal.groups,
				group_row_slim => group_row_slim.group_id.toFixed(0)
			);
			// delete
			{
				for (const entry of contrast.only_left)
				{
					await get_store_groups().delete(entry.left.key);
				}
			}
			// update
			{
				for (const entry of contrast.both)
				{
					const row_group_fat : type_group_row_fat = {
						"user_id": user_id,
						"group_id": entry.right.group_id,
					};
					await get_store_groups().update(entry.left.key, row_group_fat);
				}
			}
			// create
			{
				for (const entry of contrast.only_right)
				{
					const row_group_fat : type_group_row_fat = {
						"user_id": user_id,
						"group_id": entry.right.group_id,
					};
					await get_store_groups().create(row_group_fat);
				}
			}
		}
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 */
	export async function identify(
		name : string
	)
	: Promise<_zeitbild.type_user_id>
	{
		const hits : Array<{key : _zeitbild.type_user_id; preview : /*type_preview*/Record<string, any>;}> = await get_store_core().search(
			{
				"expression": "(name = $name)",
				"arguments": {
					"name": name,
				}
			}
		);
		if (hits.length <= 0)
		{
			return Promise.reject<_zeitbild.type_user_id>(new Error("not found"));
		}
		else
		{
			return Promise.resolve<_zeitbild.type_user_id>(hits[0].key);
		}
	}
	
}
