
namespace _zeitbild.repository.calendar
{
	
	/**
	 */
	type type_dispersal = {
		core_row : Record<
			string,
			any
		>;
		access_attributed_rows : Array<
			Record<
				string,
				any
			>
		>;
	};
	
	
	/**
	 */
	var _core_store : (
		null
		|
		lib_plankton.storage.type_store<
			_zeitbild.type_calendar_id,
			Record<string, any>,
			{},
			lib_plankton.storage.type_sql_table_autokey_search_term,
			Record<string, any>
		>
	) = null;
	
	
	/**
	 */
	var _access_attributed_chest : (
		null
		|
		lib_plankton.storage.type_chest<
			Array<any>,
			Record<string, any>,
			lib_plankton.database.type_description_create_table,
			lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
			Record<string, any>
		>
	) = null;
	
	
	/**
	 */
	function get_core_store(
	) : lib_plankton.storage.type_store<
		_zeitbild.type_calendar_id,
		Record<string, any>,
		{},
		lib_plankton.storage.type_sql_table_autokey_search_term,
		Record<string, any>
	>
	{
		if (_core_store === null) {
			_core_store = lib_plankton.storage.sql_table_autokey_store(
				{
					"database_implementation": _zeitbild.database.get_implementation(),
					"table_name": "calendars",
					"key_name": "id",
				}
			);
		}
		else {
			// do nothing
		}	
		return _core_store;
	}
	
	
	/**
	 */
	function get_access_attributed_chest(
	) : lib_plankton.storage.type_chest<
		Array<any>,
		Record<string, any>,
		lib_plankton.database.type_description_create_table,
		lib_plankton.storage.sql_table_common.type_sql_table_common_search_term,
		Record<string, any>
	>
	{
		if (_access_attributed_chest === null) {
			_access_attributed_chest = lib_plankton.storage.sql_table_common.chest(
				{
					"database_implementation": _zeitbild.database.get_implementation(),
					"table_name": "calendar_access_attributed",
					"key_names": ["calendar_id","user_id"],
				}
			);
		}
		else {
			// do nothing
		}	
		return _access_attributed_chest;
	}
	
	
	/**
	 */
	function encode_access_level(
		access_level : _zeitbild.enum_access_level
	) : int
	{
		return (
			[
				_zeitbild.enum_access_level.none,
				_zeitbild.enum_access_level.view,
				_zeitbild.enum_access_level.edit,
				_zeitbild.enum_access_level.admin,
			].indexOf(access_level)
		);
	}
	
	
	/**
	 */
	function decode_access_level(
		access_level_encoded : int
	) : _zeitbild.enum_access_level
	{
		return (
			[
				_zeitbild.enum_access_level.none,
				_zeitbild.enum_access_level.view,
				_zeitbild.enum_access_level.edit,
				_zeitbild.enum_access_level.admin,
			][access_level_encoded]
		);
	}
	
	
	/**
	 */
	function encode(
		object : _zeitbild.type_calendar_object
	) : type_dispersal
	{
		return {
			"core_row": {
				"name": object.name,
				"access_public": object.access.public,
				"access_level_default": encode_access_level(object.access.default_level),
				"resource_id": object.resource_id,
			},
			"access_attributed_rows": (
				lib_plankton.map.dump(object.access.attributed)
				.map(
					({"key": user_id, "value": level}) => ({
						// "calendar_id": calendar_id,
						"user_id": user_id,
						"level": encode_access_level(level),
					})
				)
			),
		};
	}
	
	
	/**
	 */
	function decode(
		dispersal : type_dispersal
	) : _zeitbild.type_calendar_object
	{
		return {
			"name": dispersal.core_row["name"],
			"access": {
				"public": dispersal.core_row["access_public"],
				"default_level": decode_access_level(dispersal.core_row["access_level_default"]),
				"attributed": lib_plankton.map.hashmap.implementation_map(
					lib_plankton.map.hashmap.make<_zeitbild.type_user_id, _zeitbild.enum_access_level>(
						x => x.toFixed(0),
						{
							"pairs": (
								dispersal.access_attributed_rows
								.map(
									(access_attributed_row) => ({
										// "calendar_id": access_attributed_row["calendar_id"],
										"key": access_attributed_row["preview"]["user_id"],
										"value": decode_access_level(access_attributed_row["preview"]["level"]),
									})
								)
							),
						}
					)
				),
			},
			"resource_id": dispersal.core_row["resource_id"],
		};
	}
	
	
	/**
	 */
	export function read(
		id : _zeitbild.type_calendar_id
	) : Promise<_zeitbild.type_calendar_object>
	{
		return (
			get_core_store().read(id)
			.then(
				(core_row) => (
					get_access_attributed_chest().search(
						{
							"expression": "(calendar_id = $calendar_id)",
							"arguments": {
								"calendar_id": id,
							}
						}
					)
					.then(
						(access_attributed_rows) => Promise.resolve<type_dispersal>(
							{
								"core_row": core_row,
								"access_attributed_rows": access_attributed_rows,
							}
						)
					)
					.then(
						(dispersal) => Promise.resolve<_zeitbild.type_calendar_object>(
							decode(dispersal)
						)
					)
				)
			)
		);
	}
	
	
	/**
	 */
	export async function create(
		calendar_object : _zeitbild.type_calendar_object
	) : Promise<_zeitbild.type_calendar_id>
	{
		const dispersal : type_dispersal = encode(calendar_object);
		const core_store = get_core_store();
		const calendar_id : _zeitbild.type_calendar_id = await core_store.create(
			dispersal.core_row
		);
		for await (const access_attributed_row of dispersal.access_attributed_rows) {
			get_access_attributed_chest().write(
				[calendar_id, access_attributed_row["user_id"]],
				{"level": access_attributed_row["level"]}
			);
		}
		await _zeitbild.cache.clear();
		return Promise.resolve<_zeitbild.type_calendar_id>(calendar_id);
	}
	
	
	/**
	 */
	export async function update(
		calendar_id : _zeitbild.type_calendar_id,
		calendar_object : _zeitbild.type_calendar_object
	) : Promise<void>
	{
		const dispersal : type_dispersal = encode(calendar_object);
		// core
		{
			const core_store = get_core_store();
			await core_store.update(
				calendar_id,
				dispersal.core_row
			);
		}
		// attributed access
		{
			const access_attributed_chest = get_access_attributed_chest();
			const hits : Array<Record<string, any>> = await access_attributed_chest.search(
				{
					"expression": "(calendar_id = $calendar_id)",
					"arguments": {
						"calendar_id": calendar_id,
					}
				}
			);
			const contrast = lib_plankton.list.contrast<
				Record<string, any>,
				Record<string, any>
			>(
				hits,
				hit => hit["user_id"],
				dispersal.access_attributed_rows,
				row => row["user_id"]
			);
			// delete
			for await (const entry of contrast.only_left) {
				await access_attributed_chest.delete(
					[calendar_id, entry.left["user_id"]]
				);
			}
			// update
			for await (const entry of contrast.both) {
				await access_attributed_chest.write(
					[calendar_id, entry.right["user_id"]],
					{"level": entry.right["level"]}
				);
			}
			// create
			for await (const entry of contrast.only_right) {
				await access_attributed_chest.write(
					[calendar_id, entry.right["user_id"]],
					{"level": entry.right["level"]}
				);
			}
		}
		await _zeitbild.cache.clear();
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 * @todo remove events from resource?
	 */
	export async function delete_(
		calendar_id : _zeitbild.type_calendar_id
	) : Promise<void>
	{
		await _zeitbild.cache.clear();
		const core_store = get_core_store();
		const access_attributed_chest = get_access_attributed_chest();
		// attributed access
		{
			const hits : Array<Record<string, any>> = await access_attributed_chest.search(
				{
					"expression": "(calendar_id = $calendar_id)",
					"arguments": {
						"calendar_id": calendar_id,
					}
				}
			);
			for await (const hit of hits) {
				await access_attributed_chest.delete(
					[calendar_id, hit["user_id"]]
				);
			}
		}
		// core
		{
			await core_store.delete(
				calendar_id
			);
		}
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 */
	type type_overview_entry = {
		id : _zeitbild.type_calendar_id;
		name : string;
		access_level : _zeitbild.enum_access_level;
	}
	
	
	/**
	 * @todo caching
	 */
	export async function overview(
		user_id : (null | _zeitbild.type_user_id)
	) : Promise<
		Array<
			type_overview_entry
		>
	>
	{
		return lib_plankton.cache.get_complex<any, Array<type_overview_entry>>(
			_zeitbild.cache,
			"calendar_overview",
			{
				"user_id": user_id,
			},
			() => (
				lib_plankton.file.read("sql/calendar_overview.sql")
				.then(
					(template) => _zeitbild.database.get_implementation().query_free_get(
						{
							"template": template,
							"arguments": {
								"user_id": user_id,
							}
						}
					)
				)
				.then(
					(rows) => Promise.resolve(
						lib_plankton.call.convey(
							rows,
							[
								(x : Array<Record<string, any>>) => x.map(
									(row : Record<string, any>) => ({
										"id": row["id"],
										"name": row["name"],
										/**
										 * @todo unite with _zeitbild.service.calendar.get_access_level
										 */
										"access_level": decode_access_level(
											Math.max(
												(row["access_public"] ? 1 : 0),
												(
													(user_id === null)
													?
													0
													:
													(row["access_level_attributed"] ?? row["access_level_default"])
												)
											)
										),
									})
								),
								(x : Array<type_overview_entry>) => x.filter(
									(row) => (
										! _zeitbild.value_object.access_level.order(
											row.access_level,
											_zeitbild.enum_access_level.none
										)
									)
								),
								(x : Array<type_overview_entry>) => lib_plankton.list.sorted<type_overview_entry>(
									x,
									{
										"compare_element": lib_plankton.order.order_lexicographic_pair_wrapped<type_overview_entry, _zeitbild.enum_access_level, int>(
											row => row.access_level,
											row => row.id,
											{
												"order_first": (a, b) => _zeitbild.value_object.access_level.order(b, a),
												"order_second": (a, b) => (a <= b)
											}
										),
									}
								),
							]
						)
					)
				)
			)
		);
	}
}

