
namespace _zeitbild.service.caldav
{
	
	/**
	 */
	function get_answers(
	) : Record<
		string,
		(stuff : any) => lib_plankton.webdav.type_data_prop_value
	>
	{
		return {
			// RFC2518
			
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.2
			 */
			"displayname": (stuff) => ({
				"kind": "primitive",
				"data": stuff["name"]
			}),
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.4
			 */
			/*
			"getcontentlength": {
				"kind": "none",
				"data": null
			},
			 */
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.5
			 */
			"getcontenttype": (stuff) => ({
				"kind": "primitive",
				"data": stuff["content_type"]
			}),
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.7
			 */
			/*
			"getlastmodified": {
				"kind": "none",
				"data": null
			},
			 */
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc2518#section-13.9
			 */
			"resourcetype": (stuff) => ({
				"kind": "resourcetype",
				"data": {
					"kind": "collection",
					"type": "calendar",
				}
			}),
			
			// RFCP3744
			
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc3744#section-4.2
			 */
			"principal-url": (stuff) => ({
				"kind": "href",
				"data": "/caldav"
			}),
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc3744#section-5.1
			 */
			"owner": (stuff) => ({
				"kind": "primitive",
				"data": ""
			}),
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc3744#section-5.4
			 */
			"current-user-privilege-set": (stuff) => ({
				"kind": "privileges",
				"data": [
					"read"
				]
			}),
			
			// RFC4791
			
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc4791#section-5.2.3
			 */
			"supported-calendar-component-set": (stuff) => ({
				"kind": "component_set",
				"data": {
					"name": "C:supported-calendar-component-set",
					"items": [
						"VCALENDAR"
					]
				}
			}),
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc4791#section-6.2.1
			 */
			"calendar-home-set": (stuff) => ({
				"kind": "href",
				"data": "/caldav/project"
			}),
			
			// RFC4918
			
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc4918#section-15.6
			 */
			"getetag": (stuff) => ({
				"kind": "primitive",
				"data": ""
			}),
			
			// RFC5397
			
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc5397#section-3
			 */
			"current-user-principal": (stuff) => ({
				"kind": "href",
				"data": "/caldav"
			}),
			
			// RFC6638
			
			/**
			 * @see https://datatracker.ietf.org/doc/html/rfc6638#section-2.4.1
			 */
			"calendar-user-address-set": (stuff) => ({
				"kind": "primitive",
				"data": ""
			}),
			
			// unknown
			
			/*
			"calendar-color": {
				"kind": "none",
				"data": null
			},
			"executable": {
				"kind": "none",
				"data": null
			},
			"checked-in": {
				"kind": "none",
				"data": null
			},
			"checked-out": {
				"kind": "none",
				"data": null
			},
			 */
		};
	}
	
	
	/**
	 * @todo use pod for output
	 * @todo get api paths in props from config
	 * @todo consider to outsorce to plankton
	 */
	export function probe(
		input : (null | lib_plankton.xml.type_node_data),
		{
			"force_props": force_props = null,
		} : {
			force_props ?: (null | Array<string>);
		} = {
		}
	) : (null | lib_plankton.xml.type_node_data)
	{
		const http_protocol : string = "HTTP/1.1";
		let props : (null | Array<string>);
		if (force_props) {
			props = force_props;
		}
		else {
			if (
				(input !== null)
				&&
				(input.kind === "complex")
				&&
				(
					(input.data.tag.toLowerCase() === "d:propfind")
					||
					(input.data.tag.toLowerCase() === "propfind")
				)
				&&
				(input.data.children.length === 1)
				&&
				(input.data.children[0].kind === "complex")
				&&
				(
					(input.data.children[0].data.tag.toLowerCase() === "d:prop")
					||
					(input.data.children[0].data.tag.toLowerCase() === "prop")
				)
			) {
				props = input.data.children[0].data.children.map(
					node => {
						switch (node.kind) {
							case "complex": {
								return node.data.tag;
								break;
							}
							default: {
								throw (new Error("unexpected node type for prop"));
								break;
							}
						}
					}
				);
				props.sort();
			}
			else {
				props = null;
			}
		}
		if (props === null) {
			lib_plankton.log.notice(
				"service.caldav.probe.unexpected_input",
				{
					"input": input,
				}
			);
			return null;
		}
		else {
			const answers : Record<
				string,
				(stuff : any) => lib_plankton.webdav.type_data_prop_value
			> = get_answers();
			return {
				"kind": "root",
				"data": {
					"version": "1.0",
					"encoding": "utf-8",
					"content": lib_plankton.webdav.data_multistatus_encode_xml(
						{
							"responses": [
								{
									"href": "/caldav/project",
									"body": {
										/**
										 * @todo maybe propstats needs to be filled with props (.map …)
										 */
										"propstats": (
											false
											?
											[
												{
													"prop": (props ?? []).map(
														(prop) => {
															const prop_parts : Array<string> = prop.toLowerCase().split(":");
															const prop_normalized : string = ((prop_parts.length <= 1) ? prop_parts[0] : prop_parts.slice(1).join(":"));
															if (! (prop_normalized in answers)) {
																lib_plankton.log.error(
																	"api.caldav_probe.unhandled_prop",
																	prop_normalized
																);
																throw (new Error("unhandled prop: " + prop_normalized));
															}
															else {
																return {
																	"name": prop,
																	"value": (
																		answers[prop_normalized]({"name": "projects", "content_type": "text/xml"})
																		??
																		{
																			"kind": "none",
																			"data": null,
																		}
																	),
																};
															}
														}
													),
													"status": (http_protocol + " 200 OK"),
													"description": null,
												},
											]
											:
											props.map(
												(prop) => {
													const prop_parts : Array<string> = prop.toLowerCase().split(":");
													const prop_normalized : string = (
														(prop_parts.length <= 1)
														?
														prop_parts[0]
														:
														prop_parts.slice(1).join(":")
													);
													if (! (prop_normalized in answers)) {
														lib_plankton.log.notice(
															"api.caldav_probe.unhandled_prop",
															prop_normalized
														);
														/*
														throw (new Error("unhandled prop: " + prop_normalized));
														 */
														return {
															"prop": [
																{
																	"name": prop,
																	"value": {
																		"kind": "none",
																		"data": null,
																	}
																},
															],
															"status": (http_protocol + " 404 Not Found"),
															"description": null,
														};
													}
													else {
														return {
															"prop": [
																{
																	"name": prop,
																	"value": answers[prop_normalized]({"name": "projects", "content_type": "text/xml"}),
																},
															],
															"status": (http_protocol + " 200 OK"),
															"description": null,
														};
													};
												}
											)
										)
									},
									"description": null,
								}
							],
							"description": null,
						}
					)
				}
			};
		}
	}
	
	
	/**
	 * @todo heed input properly
	 */
	export function projects(
		input : lib_plankton.xml.type_node_data,
		user_id : type_user_id
	) : Promise<lib_plankton.xml.type_node_data>
	{
		const answers : Record<
			string,
			(stuff : any) => lib_plankton.webdav.type_data_prop_value
		> = get_answers();
		/**
		 * @todo remove
		 */
		lib_plankton.log.info(
			"service.caldav.input",
			input
		);
		/**
		 * @todo check data structure
		 */
		if (
			! (
				(input.kind === "complex")
				&&
				(input.data.tag === "propfind")
				&&
				(input.data.children.length === 1)
				&&
				(input.data.children[0].kind === "complex")
				&&
				(input.data.children[0].data.tag === "prop")
				&&
				input.data.children[0].data.children.every(
					node => (node.kind === "complex")
				)
			)
		) {
			throw (new Error("wrong input structure"));
		}
		else {
			const props : Array<string> = (
				input
				.data.children
				[0]
				.data.children
				.map(
					(node) => node.data.tag
				)
			);
			/**
			 * @todo remove
			 */
			lib_plankton.log.info(
				"service.caldav.props",
				props
			);
			return (
				_zeitbild.service.calendar.overview(user_id)
				.then(
					(data_raw) => Promise.resolve(
						data_raw
						.map(
							(entry) => ({
								"id": entry.id,
								"name": entry.name,
								"access_level": _zeitbild.value_object.access_level.to_string(entry.access_level),
							})
						)
					)
				)
				.then(
					(data) => Promise.resolve(
						{
							"kind": "root",
							"data": {
								"version": "1.0",
								"encoding": "utf-8",
								"content": lib_plankton.webdav.data_multistatus_encode_xml(
									{
										"responses": data.map(
											(entry) => ({
												"href": lib_plankton.string.coin(
													"/caldav/project/{{id}}",
													{
														"id": entry.id.toFixed(0),
													}
												),
												"body": {
													"propstats": (
														lib_plankton.call.wrap<Array<string>>(props)
														.convey<Array<{found : boolean; prop : lib_plankton.webdav.type_data_prop;}>>(
															(props_raw) => props_raw.map(
																(prop_raw) => {
																	const prop_parts : Array<string> = prop_raw.toLowerCase().split(":");
																	const prop_normalized : string = ((prop_parts.length <= 1) ? prop_parts[0] : prop_parts.slice(1).join(":"));
																	return (
																		(prop_normalized in answers)
																		?
																		{
																			"found": true,
																			"prop": {
																				"name": prop_raw,
																				"value": answers[prop_normalized]({"name": entry.name, "content_type": "text/calendar"}),
																			}
																		}
																		:
																		{
																			"found": false,
																			"prop": {
																				"name": prop_raw,
																				"value": {"kind": "none", "data": null}
																			}
																		}
																	);
																}
															)
														)
														.convey<Array<Array<{found : boolean; prop : lib_plankton.webdav.type_data_prop;}>>>(
															(props_transformed) => lib_plankton.list.group(
																props_transformed,
																(x, y) => (x.found === y.found)
															)
														)
														.convey<Array<lib_plankton.webdav.type_data_propstat>>(
															(props_grouped) => props_grouped.map(
																group => (
																	group[0].found
																	?
																	{
																		"prop": group.map(member => member.prop),
																		"status": "HTTP/1.1 200 OK",
																		"description": null,
																	}
																	:
																	{
																		"prop": group.map(member => member.prop),
																		"status": "HTTP/1.1 404 Not Found",
																		"description": null,
																	}
																)
															)
														)
														.cull()
													),
												},
												"description": null,
											})
										),
										"description": null,
									}
								)
							}
						}
					)
				)
			);
		}
	}
	
}
