/*
This file is part of »dali«.

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

»dali« 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.

»dali« 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 »dali«. If not, see <http://www.gnu.org/licenses/>.
 */


namespace _dali.widgets
{
	
	/**
	 */
	type type_action_create_calendar = (
		(
		)
		=>
		Promise<void>
	);
	
	
	/**
	 */
	type type_action_edit_calendar = (
		(
			type_calendar_object_reduced_with_id
		)
		=>
		Promise<void>
	);
	
	
	/**
	 */
	type type_action_create_event = (
		(
			stuff ?: {
				date ?: lib_plankton.pit.type_date;
			}
		)
		=>
		Promise<void>
	);
	
	
	/**
	 */
	type type_action_edit_event = (
		(
			event_key : _dali.type_event_key
		)
		=>
		Promise<void>	
	);
	
	
	/**
	 */
	export class class_widget_overview
		implements lib_plankton.zoo_widget.interface_widget
	{
		
		/**
		 */
		private widget_sources : _dali.widgets.class_widget_sources;
		
		
		/**
		 */
		private widget_multiview : _dali.widgets.class_widget_multiview;

		
		/**
		 */
		private action_create_calendar : type_action_create_calendar;
		
		
		/**
		 */
		private action_edit_calendar : type_action_edit_calendar;
		
		
		/**
		 */
		private action_create_event : type_action_create_event;
		
		
		/**
		 */
		private action_edit_event : type_action_edit_event;
		
		
		/**
		 */
		public constructor(
		)
		{
			this.action_create_calendar = async (
			) => {
				const widget = new _dali.widgets.class_widget_calendar_edit(
					await _dali.model.group_list(),
					await _dali.model.user_list(),
					{
						"name": "",
						"hue": lib_plankton.random.generate_unit(),
						"access": {
							"public": false,
							"default_level": _dali.enum_access_level.none,
							"attributed_group": lib_plankton.map.hashmap.implementation_map<
								_dali.type_group_id,
								_dali.enum_access_level
							>(
								lib_plankton.map.hashmap.make<
									_dali.type_group_id,
									_dali.enum_access_level
								>(
									group_id => group_id.toFixed(0),
								)
							),
							"attributed_user": lib_plankton.map.hashmap.implementation_map<
								_dali.type_user_id,
								_dali.enum_access_level
							>(
								lib_plankton.map.hashmap.make<
									_dali.type_user_id,
									_dali.enum_access_level
								>(
									user_id => user_id.toFixed(0),
								)
							),
						},
						/**
						 * @todo
						 */
						"resource_id": 0,
					},
					{
						"read_only": false,
						"action_cancel": () => {
							_dali.overlay.toggle({"mode": false});
						},
						"action_add": (calendar_object) => {
							_dali.model.calendar_add(
								calendar_object
							)
							.then(
								() => {
									this.update_sources_and_entries();
									_dali.overlay.toggle({"mode": false});
								}
							)
							.catch(
								(reason) => {
									lib_plankton.log.warning(
										"dali.overview.calendar_add_error",
										{"reason": String(reason)}
									);
								}
							);
						},
					}
				);
				_dali.overlay.clear();
				_dali.overlay.toggle({"mode": true});
				await widget.load(_dali.overlay.get_content_element());
			};
			this.action_edit_calendar = async (
				calendar_object_reduced_with_id
			) => {
				const read_only : boolean = (() => {
					switch (calendar_object_reduced_with_id.access_level)
					{
						case _dali.enum_access_level.none:
						{
							throw (new Error("this event should not be visible"));
							break;
						}
						case _dali.enum_access_level.edit:
						case _dali.enum_access_level.view:
						{
							return true;
							break;
						}
						case _dali.enum_access_level.admin:
						{
							return false;
							break;
						}
					}
				}) ();
				if (read_only)
				{
					lib_plankton.log.notice(
						"dali.overview.may_not_edit_calendar",
						{
							"calendar_id": calendar_object_reduced_with_id.id,
						}
					);
				}
				else
				{
					const calendar_id : _dali.type_calendar_id = calendar_object_reduced_with_id.id;
					const calendar_object : _dali.type_calendar_object = await _dali.model.calendar_get(
						calendar_id
					);
					const widget = new _dali.widgets.class_widget_calendar_edit(
						await _dali.model.group_list(),
						await _dali.model.user_list(),
						calendar_object,
						{
							"read_only": read_only,
							"action_cancel": () => {
								_dali.overlay.toggle({"mode": false});
							},
							"action_change": (data) => {
								_dali.model.calendar_change(
									calendar_id,
									data
								)
								.then(
									() => {
										this.update_sources_and_entries();
										_dali.overlay.toggle({"mode": false});
									}
								);
							},
							"action_remove": (data) => {
								_dali.model.calendar_remove(
									calendar_id
								)
								.then(
									() => {
										this.update_sources_and_entries();
										_dali.overlay.toggle({"mode": false});
									}
								);
							},
						}
					);
					_dali.overlay.clear();
					_dali.overlay.toggle({"mode": true});
					await widget.load(_dali.overlay.get_content_element());
				}
			}
			/**
			 * @todo unterschiedliches Verhalten bei Anmeldung?
			 */
			this.action_create_event = async (
				{
					"date": date = lib_plankton.pit.to_datetime(lib_plankton.pit.now()).date,
				}
				:
				{
					date ?: lib_plankton.pit.type_date;
				}
				=
				{
				}
			) => {
				if (! _dali.is_logged_in())
				{
					// do nothing
				}
				else
				{
					const widget = new _dali.widgets.class_widget_event_edit(
						(await this.get_available_calendars()),
						{
							"calendar_id": null,
							"event_name": "",
							"event_begin": lib_plankton.call.convey(
								date,
								[
									x => ({
										"timezone_shift": 0,
										"date": date,
										"time": {"hour": 12, "minute": 0, "second": 0}
									}),
									lib_plankton.pit.from_datetime,
									x => lib_plankton.pit.shift_hour(x, 0),
									lib_plankton.pit.to_datetime,
								]
							),
							"event_end": lib_plankton.call.convey(
								date,
								[
									x => ({
										"timezone_shift": 0,
										"date": date,
										"time": {"hour": 12, "minute": 0, "second": 0}
									}),
									lib_plankton.pit.from_datetime,
									x => lib_plankton.pit.shift_hour(x, +1),
									lib_plankton.pit.to_datetime,
								]
							),
							"event_location": null,
							"event_link": null,
							"event_description": null,
						},
						{
							"read_only": false,
							"action_cancel": () => {
								_dali.overlay.toggle({"mode": false});
							},
							"action_add": (data) => {
								_dali.model.event_add(
									data.calendar_id,
									{
										"name": data.event_name,
										"begin": data.event_begin,
										"end": data.event_end,
										"location": data.event_location,
										"link": data.event_link,
										"description": data.event_description,
									}
								)
								.then(
									() => {
										this.update_entries();
										_dali.overlay.toggle({"mode": false});
									}
								)
								.catch(
									(reason) => {
										// todo
									}
								);
							},
						}
					);
					_dali.overlay.clear();
					_dali.overlay.toggle({"mode": true});
					await widget.load(_dali.overlay.get_content_element());
				}
			}
			/**
			 */
			this.action_edit_event = async (
				event_key
			) => {
				const event_object_extended : _dali.type_event_object_extended = await _dali.model.event_get(event_key);
				const read_only : boolean = (() => {
					switch (event_object_extended.access_level)
					{
						case _dali.enum_access_level.none:
						{
							throw (new Error("this event should not be visible"));
							break;
						}
						case _dali.enum_access_level.view:
						{
							return true;
							break;
						}
						case _dali.enum_access_level.edit:
						case _dali.enum_access_level.admin:
						{
							return false;
							break;
						}
					}
				}) ();
				const widget = new _dali.widgets.class_widget_event_edit(
					(await this.get_available_calendars()),
					{
						"calendar_id": event_object_extended.calendar_id,
						"event_name": event_object_extended.event_object.name,
						"event_begin": event_object_extended.event_object.begin,
						"event_end": event_object_extended.event_object.end,
						"event_location": event_object_extended.event_object.location,
						"event_link": event_object_extended.event_object.link,
						"event_description": event_object_extended.event_object.description,
					},
					{
						"read_only": read_only,
						"action_cancel": () => {
							_dali.overlay.toggle({"mode": false});
						},
						"action_change": (data) => {
							_dali.model.event_change(
								event_key,
								{
									"name": data.event_name,
									"begin": data.event_begin,
									"end": data.event_end,
									"location": data.event_location,
									"link": data.event_link,
									"description": data.event_description,
								}
							)
							.then(
								() => {
									this.update_entries();
									_dali.overlay.toggle({"mode": false});
								}
							)
							.catch(
								(reason) => {
									lib_plankton.log.warning(
										"dali.overview.event_change.error",
										{"reason": String(reason)}
									);
								}
							);
						},
						"action_remove": () => {
							_dali.model.event_remove(
								event_key
							)
							.then(
								() => {
									this.update_entries();
									_dali.overlay.toggle({"mode": false});
								}
							)
							.catch(
								(reason) => {
									lib_plankton.log.warning(
										"dali.overview.event_remove_error",
										{"reason": String(reason)}
									);
								}
							);
						},
					}
				);
				_dali.overlay.clear();
				_dali.overlay.toggle({"mode": true});
				await widget.load(_dali.overlay.get_content_element());
			}
		}
		
		
		/**
		 */
		private async get_available_calendars(
		)
		: Promise<
			Array<
				_dali.type_calendar_object_reduced_with_id
			>
		>
		{
			return (
				(await _dali.model.calendar_list())
				/*
				.filter(
					(entry) => (
						(entry.access_level === _dali.enum_access_level.edit)
						||
						(entry.access_level === _dali.enum_access_level.admin)
					)
				)
				 */
			);
		}
		
		
		/**
		 */
		private async get_entries(
			from_pit : lib_plankton.pit.type_pit,
			to_pit : lib_plankton.pit.type_pit,
			calendar_ids : Array<_dali.type_calendar_id>
		)
		: Promise<Array<_dali.type_event_object_extended>>
		{
			/**
			 * @todo do NOT wait?
			 */
			await _dali.model.sync_events(
				{
					"from": from_pit,
					"to": to_pit,
				},
				{
					"calendar_ids": calendar_ids,
				}
			);
			/**
			 * @todo filter
			 */
			return _dali.model.event_list();
		}
		
		
		/**
		 */
		private async update_sources_and_entries(
			{
				"priviliged": priviliged = null,
			}
			:
			{
				priviliged ?: (null | boolean);
			}
			=
			{
			}
		)
		: Promise<void>
		{
			await this.widget_sources.update({"priviliged": priviliged});
			await this.widget_multiview.update_entries();
		}
		
		
		/**
		 * @todo use priviliged?
		 */
		private async update_entries(
			{
				"priviliged": priviliged = null,
			}
			:
			{
				priviliged ?: (null | boolean);
			}
			=
			{
			}
		)
		: Promise<void>
		{
			await this.widget_multiview.update_entries();
		}
		
		
		
		/**
		 * [implementation]
		 */
		public async load(
			target_element : HTMLElement
		)
		: Promise<void>
		{
			const view_mode : _dali.enum_view_mode = _dali.helpers.view_mode_determine("auto");
			const view_kind : _dali.enum_view_kind = _dali.helpers.view_kind_determine("auto");
			
			this.widget_sources = (
				new _dali.widgets.class_widget_sources(
					_dali.model.calendar_list,
					{
						"initial_priviliged": _dali.is_logged_in(),
						"action_add": this.action_create_calendar,
						"action_select": this.action_edit_calendar,
						"action_toggle": (entry, mode) => {
							this.widget_multiview.toggle_calendar_visibilty(entry.id, {"mode": mode});
						},
					}
				)
			);
			this.widget_multiview = (
				new _dali.widgets.class_widget_multiview(
					this.get_entries,
					{
						"initial_view_mode": view_mode,
						"weekview_initial_vertical": (
							(await _dali.model.settings()).weekview_vertical_default
							||
							(view_kind === _dali.enum_view_kind.touch)
						),
						"action_create_event": this.action_create_event,
						"action_edit_event": this.action_edit_event,
					}
				)
			);
			
			target_element.innerHTML = await _dali.helpers.template_coin(
				"widget-overview",
				"main",
				{
				}
			);

			// head
			{
				// hint
				{
					const dom_hint = target_element.querySelector(".widget-overview-hint");
					dom_hint.textContent = lib_plankton.translate.get("widget.overview.login_hint");
					dom_hint.classList.toggle("widget-overview-hint-hidden", _dali.is_logged_in());
				}
			}
			// body
			{
				let widget_slider : lib_plankton.zoo_widget.interface_widget;
				switch (view_kind)
				{
					case _dali.enum_view_kind.regular:
					{
						widget_slider = new lib_plankton.zoo_widget.class_slider(
							[
								new lib_plankton.zoo_widget.class_bunch(
									[
										this.widget_sources,
										this.widget_multiview,
									]
								),
							],
							{
								"threshold": 100,
								"initial_index": 1,
							}						
						);
						break;
					}
					case _dali.enum_view_kind.touch:
					{
						widget_slider = new lib_plankton.zoo_widget.class_slider(
							[
								this.widget_sources,
								this.widget_multiview,
							],
							{
								"threshold": 100,
								"initial_index": 2,
							}						
						);
						break;
					}
				}
				await widget_slider.load(target_element.querySelector(".widget-overview-body"));
			}
			
			_dali.model.listen_reset(
				async (priviliged) => {
					this.update_sources_and_entries({"priviliged": priviliged});
					target_element.querySelector(".widget-overview-hint").classList.toggle("widget-overview-hint-hidden", priviliged);
				}
			);
			
			return Promise.resolve<void>(undefined);
		}
		
	}
	
}
