/*
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.backend
{
	
	/**
	 */
	type type_conf = {
		scheme : string;
		host : string;
		port : int;
		path : string;
	};
	
	
	/**
	 */
	type type_request = {
		method : lib_plankton.http.enum_method;
		action : string;
		input : (null | any);
	};
	
	
	/**
	 */
	type type_event_object = {
		name : string;
		begin : {
			timezone_shift: int;
			date: {
				year: int;
				month: int;
				day: int;
			};
			time: (
				null
				|
				{
					hour: int;
					minute: int;
					second: int;
				}
			);
		};
		end : (
			null
			|
			{
				timezone_shift: int;
				date: {
					year: int;
					month: int;
					day: int;
				};
				time: (
					null
					|
					{
						hour: int;
						minute: int;
						second: int;
					}
				);
			}
		);
		location : (
			null
			|
			string
		);
		link : (
			null
			|
			string
		);
		description : (
			null
			|
			string
		);
	};
	
	
	/**
	 */
	var _conf : type_conf;
	
	
	/**
	 */
	var _data_chest : (
		null
		|
		lib_plankton.storage.type_chest<string, string, void, string, string>
	) = null;
	
	
	/**
	 */
	var _cache : (
		null
		|
		lib_plankton.cache.type_subject<boolean>
	);
	
	
	/**
	 */
	var _queue : lib_plankton.call.type_queue<type_request, any>;
	
	
	/**
	 */
	export async function initialize(
		conf : type_conf
	)
	: Promise<void>
	{
		_conf = conf;
		_data_chest = lib_plankton.storage.localstorage.implementation_chest(
			{
				"corner": "zeitbild",
			}
		);
		_cache = lib_plankton.cache.make<boolean>(
			/*
			lib_plankton.storage.memory.implementation_chest(
				{
				}
			)
			 */
		);
		_queue = lib_plankton.call.queue_make<type_request, any>(
			call_real
		);
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 */
	async function get_session_key(
	)
	: Promise<(null | string)>
	{
		try
		{
			return (await _data_chest.read("session_key"));
		}
		catch (error)
		{
			return null;
		}
	}
	
	
	/**
	 */
	async function call_real(
		request : type_request
	)
	: Promise<any>
	{
		const with_body : boolean = (
			[
				lib_plankton.http.enum_method.post,
				lib_plankton.http.enum_method.put,
				lib_plankton.http.enum_method.patch,
			].includes(request.method)
		);
		const session_key : (null | string) = await get_session_key();
		const http_request : lib_plankton.http.type_request = {
			"version": "HTTP/2",
			"scheme": (
				(_conf.scheme === "http")
				?
				"http"
				:
				"https"
			),
			"host": lib_plankton.string.coin(
				"{{host}}:{{port}}",
				{
					"host": _conf.host,
					"port": _conf.port.toFixed(0),
				}
			),
			"path": lib_plankton.string.coin(
				"{{base}}{{action}}",
				{
					"base": _conf.path,
					"action": request.action,
				}
			),
			"method": request.method,
			"query": (
				(with_body || (request.input === null))
				?
				null
				:
				("?" + lib_plankton.www_form.encode(request.input))
			),
			"headers": Object.assign(
				{},
				(
					(! with_body)
					?
					{}
					:
					{"Content-Type": "application/json"}
				),
				(
					(session_key === null)
					?
					{}
					:
					{"X-Session-Key": session_key}
				)
			),
			"body": (
				((! with_body) || (request.input === null))
				?
				null
				:
				/*Buffer.from*/(lib_plankton.json.encode(request.input))
			),
		};
		const http_response : lib_plankton.http.type_response = await lib_plankton.http.call(http_request);
		if (
			! (
				(http_response.status_code >= 200)
				&&
				(http_response.status_code < 300)
			)
		)
		{
			return Promise.reject<any>(http_response.body.toString());
		}
		else
		{
			const output : any = lib_plankton.json.decode(http_response.body.toString());
			return Promise.resolve<any>(output);
		}
	}
	
		
	/**
	 */
	async function call(
		method : lib_plankton.http.enum_method,
		action : string,
		input : (null | any)
	)
	: Promise<any>
	{
		const request : type_request = {
			"method": method,
			"action": action,
			"input": input,
		};
		return (
			new Promise<any>(
				(resolve, reject) => {
					lib_plankton.call.queue_add<type_request, any>(
						_queue,
						request,
						resolve,
						reject
					);
				}
			)
		);
	}
	
	
	/**
	 */
	export function status(
	)
	: Promise<
		{
			logged_in : boolean;
			name : (null | string);
		}
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			"/session/status",
			null
		)
	}
	
	
	/**
	 */
	export async function session_prepare(
		input : any
	)
	: Promise<{kind : string; data : any;}>
	{
		return call(
			lib_plankton.http.enum_method.post,
			"/session/prepare",
			input
		);
	}
	
	
	/**
	 */
	export function set_session_key(
		session_key : string
	)
	: Promise<void>
	{
		return (
			_data_chest.write("session_key", session_key)
			.then<void>(() => Promise.resolve<void>(undefined))
		);
	}
	
	/**
	 */
	export async function session_begin(
		name : string,
		password : string
	)
	: Promise<void>
	{
		const session_key : string = await call(
			lib_plankton.http.enum_method.post,
			"/session/begin",
			{
				"name": name,
				"password": password,
			}
		);
		await _data_chest.write("session_key", session_key);
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 */
	export async function session_end(
	)
	: Promise<void>
	{
		await call(
			lib_plankton.http.enum_method.delete,
			"/session/end",
			null
		);
		await _data_chest.delete("session_key");
		return Promise.resolve<void>(undefined);
	}
	
	
	/**
	 */
	export function group_list(
	)
	: Promise<
		Array<
			{
				id : int;
				name : string;
				label : string;
			}
		>
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			"/groups",
			null
		);
	}
	
	
	/**
	 */
	export function user_list(
	)
	: Promise<
		Array<
			{
				id : int;
				name : string;
			}
		>
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			"/users",
			null
		);
	}
	
	
	/**
	 */
	export function user_get(
	)
	: Promise<
		{
			name : string;
			groups : Array<
				int
			>;
			email_address : (
				null
				|
				string
			);
			dav_token : (
				null
				|
				string
			);
			settings : {
				use_fallback_for_date_and_time_inputs : boolean;
				weekview_vertical_default : boolean;
			};
		}
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			"/user",
			null
		);
	}
	
	
	/**
	 */
	export function user_dav_conf(
	)
	: Promise<
		(
			null
			|
			{
				address : string;
				username : string;
				password : string;
				setup_hints : Array<
					{
						label : string;
						link : string;
						remark : (null | string);
					}
				>;
			}
		)
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			"/user_dav_conf",
			null
		);
	}
	
	
	/**
	 */
	export function user_dav_token(
	)
	: Promise<void>
	{
		return call(
			lib_plankton.http.enum_method.patch,
			"/user_dav_token",
			null
		);
	}
	
	
	/**
	 */
	export async function calendar_list(
	)
	: Promise<
		Array<
			{
				id : int;
				name : string;
				hue : float;
				access_level : string;
			}
		>
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			"/calendar",
			null
		);
	}
	
	
	/**
	 */
	export async function calendar_get(
		calendar_id : int
	)
	: Promise<
		{
			name : string;
			hue : float;
			access : {
				public : boolean;
				default_level : string;
				attributed_group : Array<
					{
						group_id : int;
						level : string;
					}
				>;
				attributed_user : Array<
					{
						user_id : int;
						level : string;
					}
				>;
			};
			resource_id : int;
		}			
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}",
				{
					"calendar_id": calendar_id.toFixed(0),
				}
			),
			null
		);
	}
	
	
	/**
	 */
	export async function calendar_add(
		data : {
			name : string;
			access : {
				public : boolean;
				default_level : string;
				attributed_group : Array<
					{
						group_id : int;
						level : string;
					}
				>;
				attributed_user : Array<
					{
						user_id : int;
						level : string;
					}
				>;
			};
			resource : (
				{
					kind : "local";
					data : {
					};
				}
				|
				{
					kind : "ics_feed";
					data : {
						url : string;
						from_fucked_up_wordpress : boolean;
					};
				}
			);
			hue : float;
		}
	)
	: Promise<
		int
	>
	{
		return call(
			lib_plankton.http.enum_method.post,
			lib_plankton.string.coin(
				"/calendar",
				{
				}
			),
			data
		);
	}
	
	
	/**
	 */
	export async function calendar_change(
		id : int,
		data : {
			name : string;
			hue : float;
			access : {
				public : boolean;
				default_level : string;
				attributed_group : Array<
					{
						group_id : int;
						level : string;
					}
				>;
				attributed_user : Array<
					{
						user_id : int;
						level : string;
					}
				>;
			};
		}
	)
	: Promise<
		void
	>
	{
		return call(
			lib_plankton.http.enum_method.put,
			lib_plankton.string.coin(
				"/calendar/{{id}}",
				{
					"id": id.toFixed(0),
				}
			),
			data
		);
	}
	
	
	/**
	 */
	export async function calendar_remove(
		id : int
	)
	: Promise<
		void
	>
	{
		return call(
			lib_plankton.http.enum_method.delete,
			lib_plankton.string.coin(
				"/calendar/{{id}}",
				{
					"id": id.toFixed(0),
				}
			),
			null
		);
	}
	
    
	/**
	 */
	export async function calendar_event_get(
		calendar_id : int,
		event_id : int
	)
	: Promise<
		type_event_object
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}/event/{{event_id}}",
				{
					"calendar_id": calendar_id.toFixed(0),
					"event_id": event_id.toFixed(0),
				}
			),
			null
		);
	}
	
	
	/**
	 */
	export async function calendar_event_add(
		calendar_id : int,
		event_data : type_event_object
	)
	: Promise<
		{
			local_resource_event_id : (null | int);
			hash : string;
		}
	>
	{
		return call(
			lib_plankton.http.enum_method.post,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}/event",
				{
					"calendar_id": calendar_id.toFixed(0),
				}
			),
			event_data
		);
	}
	
	
	/**
	 * @todo Möglichkeit den Kalender zu ändern
	 */
	export async function calendar_event_change(
		calendar_id : int,
		event_id : int,
		event_object : type_event_object
	)
	: Promise<void>
	{
		return call(
			lib_plankton.http.enum_method.put,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}/event/{{event_id}}",
				{
					"calendar_id": calendar_id.toFixed(0),
					"event_id": event_id.toFixed(0),
				}
			),
			event_object
		);
	}
	
	
	/**
	 */
	export async function calendar_event_remove(
		calendar_id : int,
		event_id : int
	)
	: Promise<void>
	{
		return call(
			lib_plankton.http.enum_method.delete,
			lib_plankton.string.coin(
				"/calendar/{{calendar_id}}/event/{{event_id}}",
				{
					"calendar_id": calendar_id.toFixed(0),
					"event_id": event_id.toFixed(0),
				}
			),
			null
		);
	}
	
	
	/**
	 * @todo prevent loops
	 */
	export async function events(
		from_timestamp : int,
		to_timestamp : int,
		{
			"calendar_ids": calendar_ids = null,
		}
		:
		{
			calendar_ids ?: (null | Array<int>);
		}
		=
		{
		}
	)
	: Promise<
		Array<
			{
				hash : string;
				calendar_id : int;
				calendar_name : string;
				hue : float;
				access_level : string;
				event_id : (null | int);
				event_object : type_event_object;
			}
		>
	>
	{
		return call(
			lib_plankton.http.enum_method.get,
			"/events",
			Object.assign(
				{
					"from": from_timestamp,
					"to": to_timestamp,
				},
				(
					(calendar_ids === null)
					?
					{}
					:
					{"calendar_ids": calendar_ids.join(",")}
				)
			)
		);
	}

}
