/*
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.sample
{
	
    /**
     */
    type type_date_absolute = {
        year : int;
        month : int;
        day : int;
    };
    
	
    /**
     */
    type type_date_relative = Array<
		{
			action : "trunc_week";
			args : [int];
		}
		|
		{
			action : "shift_week";
			args : [int];
		}
		|
		{
			action : "shift_day";
			args : [int];
		}
    >;
    
    
    /**
     */
    type type_ywd = {
        year : int;
        week : int;
        day : int;
    };
    
    
    /**
     */
    type type_time = {
        hour : int;
        minute : int;
        second : int;
    };
    
    
    /**
     */
    type type_datetime = (
		{
			timezone_shift : int;
			time : (null | type_time);
		}
		&
		(
			{
				date_absolute : type_date_absolute;
			}
			|
			{
				date_relative : type_date_relative;
			}
		)
    );
    
    
	/**
	 */
	type type_data = {
		groups : Array<
			{
				id : int;
				name : string;
				label ?: string;
			}
		>;
		users : Array<
			{
				id : int;
				name : string;
				groups ?: Array<int>;
				email_address : string;
				dav_token ?: (null | string);
				password : string;
			}
		>;
		calendars : Array<
			{
				id : int;
				name : string;
				access : {
					public ?: boolean;
					default_level : ("none" | "view" | "edit" | "admin");
					attributed_group ?: Array<
						{
							group_id : int;
							level : ("none" | "view" | "edit" | "admin");
						}
					>;
					attributed_user ?: Array<
						{
							user_id : int;
							level : ("none" | "view" | "edit" | "admin");
						}
					>;
				};
				resource : (
					{
						kind : "local";
						data : {
							events : Array<
								{
									/**
									 * @todo rename to "title"
									 */
									name : string;
									begin : type_datetime;
									end : (
										null
										|
										type_datetime
									);
									location : (
										null
										|
										string
									);
									link : (
										null
										|
										string
									);
									description : (
										null
										|
										string
									);
								}
							>
						};
					}
					|
					{
						kind : "ics_feed";
						data : {
							url : string;
							from_fucked_up_wordpress ?: boolean;
						};
					}
				);
				hue ?: (null | float);
			}
		>;
	};
	
	
	/**
	 */
	const phi : float = ((Math.sqrt(5) - 1) / 2);
	
	
	/**
	 */
	function decode_datetime(
		datetime : type_datetime
	) : lib_plankton.pit.type_datetime
	{
		if ("date_relative" in datetime)
		{
			return {
				"timezone_shift": datetime.timezone_shift,
				"date": lib_plankton.call.convey(
					lib_plankton.pit.now(),
					(
						datetime.date_relative.map<(x : any) => any>(
							entry => {
								switch (entry.action)
								{
									// default: {throw (new Error("unhandled action: " + entry.action));}
									case "trunc_week": {return (x => lib_plankton.pit.trunc_week(x));}
									case "shift_week": {return (x => lib_plankton.pit.shift_week(x, entry.args[0]));}
									case "shift_day": {return (x => lib_plankton.pit.shift_day(x, entry.args[0]));}
								}
							}
						)
						.concat(
							[
								lib_plankton.pit.to_datetime,
								x => x.date,
							]
						)
					)
				),
				"time": datetime.time,
			};
		}
		else
		{
			return {
				"timezone_shift": datetime.timezone_shift,
				"date": datetime.date_absolute,
				"time": datetime.time,
			};
		}
	}
	
	
	/**
	 */
	export async function init(
		data : type_data
	) : Promise<void>
	{
		let track : {
			group : Record<
				int,
				_zeitbild.type_group_id
			>;
			user : Record<
				int,
				_zeitbild.type_user_id
			>;
			calendar : Record<
				int,
				_zeitbild.type_user_id
			>;
		} = {
			"group": {},
			"user": {},
			"calendar": {},
		};
		// groups
		{
			for await (const group_raw of data.groups)
			{
				const group_object : _zeitbild.type_group_object = {
					"name": group_raw.name,
					"label": (group_raw.label ?? group_raw.name),
				};
				const group_id : _zeitbild.type_group_id = await _zeitbild.service.group.add(
					group_object
				);
				track.group[group_raw.id] = group_id;
			}
		}
		// users
		{
			for await (const user_raw of data.users)
			{
				const user_object : _zeitbild.type_user_object = {
					"name": user_raw.name,
					"groups": (user_raw.groups ?? []),
					"email_address": user_raw.email_address,
					"dav_token": (user_raw.dav_token ?? null),
				};
				const user_id : _zeitbild.type_user_id = await _zeitbild.service.user.add(
					user_object
				);
				await _zeitbild.service.auth_internal.set(
					user_raw.name,
					user_raw.password
				);
				track.user[user_raw.id] = user_id;
			}
		}
		// calendars
		{
			for await (const calendar_raw of data.calendars)
			{
				let resource_object : _zeitbild.type_resource_object;
				let resource_id : _zeitbild.type_resource_id;
				switch (calendar_raw.resource.kind)
				{
					case "local":
					{
						resource_object = {
							"kind": "local",
							"data": {
								"event_ids": [],
							}
						};
						resource_id = await _zeitbild.service.resource.add(
							resource_object
						);
						/*const event_ids : Array<_zeitbild.type_local_resource_event_id> = */await Promise.all(
							calendar_raw.resource.data.events
							.map(
								(event_raw) => {
									const event : _zeitbild.type_event_object = {
										"name": event_raw.name,
										"begin": decode_datetime(event_raw.begin),
										"end": (
											(event_raw.end === null)
											?
											null
											:
											decode_datetime(event_raw.end)
										),
										"location": event_raw.location,
										"link": event_raw.link,
										"description": event_raw.description,
									};
									return _zeitbild.service.resource.event_add(resource_id, event);
								}
							)
						);
						break;
					}
					case "ics_feed":
					{
						resource_object = {
							"kind": "ics_feed",
							"data": {
								"url": calendar_raw.resource.data.url,
								"from_fucked_up_wordpress": (calendar_raw.resource.data.from_fucked_up_wordpress ?? false),
							}
						};
						resource_id = await _zeitbild.service.resource.add(
							resource_object
						);
						break;
					}
				}
				const calendar_object : _zeitbild.type_calendar_object =
				{
					"name": calendar_raw.name,
					"hue": (
						calendar_raw.hue
						??
						((calendar_raw.id * phi) % 1)
					),
					"access": {
						"public": (calendar_raw.access.public ?? false),
						"default_level": _zeitbild.access_level_from_string(calendar_raw.access.default_level),
						"attributed_group": lib_plankton.map.hashmap.implementation_map(
							lib_plankton.map.hashmap.make(
								x => x.toFixed(0),
								{
									"pairs": (
										(calendar_raw.access.attributed_group ?? [])
										.map(
											(entry) => ({
												"key": track.user[entry.group_id],
												"value": _zeitbild.access_level_from_string(entry.level),
											})
										)
									),
								}
							)
						),
						"attributed_user": lib_plankton.map.hashmap.implementation_map(
							lib_plankton.map.hashmap.make(
								x => x.toFixed(0),
								{
									"pairs": (
										(calendar_raw.access.attributed_user ?? [])
										.map(
											(entry) => ({
												"key": track.user[entry.user_id],
												"value": _zeitbild.access_level_from_string(entry.level),
											})
										)
									),
								}
							)
						),
					},
					"resource_id": resource_id,
				};
				const calendar_id : _zeitbild.type_calendar_id = await _zeitbild.service.calendar.add(
					calendar_object
				);
				track.calendar[calendar_raw.id] = calendar_id;
			}
		}
		return Promise.resolve<void>(undefined);
	}

}
