/*
 * Notification manager for SW
 */
declare let self: ServiceWorkerGlobalScope;

import { swLang } from "@/scripts/lang";
import { cli } from "@/scripts/operations";
import { pushNotificationDataMap } from "@/types";
import getUserName from "@/scripts/get-user-name";
import { I18n } from "@/scripts/i18n";
import { getAccountFromId } from "@/scripts/get-account-from-id";
import { char2fileName } from "@/scripts/twemoji-base";
import * as url from "@/scripts/url";

const iconUrl = (name: string) =>
	`/static-assets/notification-badges/${name}.png`;

export async function createNotification<
	K extends keyof pushNotificationDataMap,
>(data: pushNotificationDataMap[K]) {
	const n = await composeNotification(data);

	if (n) {
		return self.registration.showNotification(...n);
	} else {
		console.error("Could not compose notification", data);
		return createEmptyNotification();
	}
}

async function composeNotification<K extends keyof pushNotificationDataMap>(
	data: pushNotificationDataMap[K],
): Promise<[string, NotificationOptions] | null> {
	if (!swLang.i18n) swLang.fetchLocale();
	const i18n = (await swLang.i18n) as I18n<any>;
	const { t } = i18n;
	switch (data.type) {
		/*
		case 'driveFileCreated': // TODO (Server Side)
			return [t('_notification.fileUploaded'), {
				body: body.name,
				icon: body.url,
				data
			}];
		*/
		case "notification":
			switch (data.body.type) {
				case "follow": {
					// users/showの型定義をswos.apiへ当てはめるのが困難なのでapiFetch.requestを直接使用
					const account = await getAccountFromId(data.userId);
					if (!account) return null;
					const userDetail = await cli.request(
						"users/show",
						{ userId: data.body.userId },
						account.token,
					);
					return [
						t("_notification.youWereFollowed"),
						{
							body: getUserName(data.body.user),
							icon: data.body.user.avatarUrl,
							badge: iconUrl("user-plus"),
							data,
							actions: userDetail.isFollowing
								? []
								: [
										{
											action: "follow",
											title: t("_notification._actions.followBack"),
										},
								  ],
						},
					];
				}

				case "mention":
					return [
						t("_notification.youGotMention", {
							name: getUserName(data.body.user),
						}),
						{
							body: data.body.note.text || "",
							icon: data.body.user.avatarUrl,
							badge: iconUrl("at"),
							data,
							actions: [
								{
									action: "reply",
									title: t("_notification._actions.reply"),
								},
							],
						},
					];

				case "reply":
					return [
						t("_notification.youGotReply", {
							name: getUserName(data.body.user),
						}),
						{
							body: data.body.note.text || "",
							icon: data.body.user.avatarUrl,
							badge: iconUrl("reply"),
							data,
							actions: [
								{
									action: "reply",
									title: t("_notification._actions.reply"),
								},
							],
						},
					];

				case "renote":
					return [
						t("_notification.youRenoted", {
							name: getUserName(data.body.user),
						}),
						{
							body: data.body.note.text || "",
							icon: data.body.user.avatarUrl,
							badge: iconUrl("retweet"),
							data,
							actions: [
								{
									action: "showUser",
									title: getUserName(data.body.user),
								},
							],
						},
					];

				case "quote":
					return [
						t("_notification.youGotQuote", {
							name: getUserName(data.body.user),
						}),
						{
							body: data.body.note.text || "",
							icon: data.body.user.avatarUrl,
							badge: iconUrl("quote-right"),
							data,
							actions: [
								{
									action: "reply",
									title: t("_notification._actions.reply"),
								},
								...(data.body.note.visibility === "public" ||
								data.body.note.visibility === "home"
									? [
											{
												action: "renote",
												title: t("_notification._actions.renote"),
											},
									  ]
									: []),
							],
						},
					];

				case "reaction": {
					let reaction = data.body.reaction;
					let badge: string | undefined;

					if (reaction.startsWith(":")) {
						// カスタム絵文字の場合
						const customEmoji = data.body.note.emojis.find(
							(x) => x.name === reaction.substr(1, reaction.length - 2),
						);
						if (customEmoji) {
							if (reaction.includes("@")) {
								reaction = `:${reaction.substr(1, reaction.indexOf("@") - 1)}:`;
							}

							const u = new URL(customEmoji.url);
							if (u.href.startsWith(`${origin}/proxy/`)) {
								// もう既にproxyっぽそうだったらsearchParams付けるだけ
								u.searchParams.set("badge", "1");
								badge = u.href;
							} else {
								const dummy = `${u.host}${u.pathname}`; // 拡張子がないとキャッシュしてくれないCDNがあるので
								badge = `${origin}/proxy/${dummy}?${url.query({
									url: u.href,
									badge: "1",
								})}`;
							}
						}
					} else {
						// Unicode絵文字の場合
						badge = `/twemoji-badge/${char2fileName(reaction)}.png`;
					}

					if (
						badge
							? await fetch(badge)
									.then((res) => res.status !== 200)
									.catch(() => true)
							: true
					) {
						badge = iconUrl("plus");
					}

					return [
						`${reaction} ${getUserName(data.body.user)}`,
						{
							body: data.body.note.text || "",
							icon: data.body.user.avatarUrl,
							badge,
							data,
							actions: [
								{
									action: "showUser",
									title: getUserName(data.body.user),
								},
							],
						},
					];
				}

				case "pollVote":
					return [
						t("_notification.youGotPoll", {
							name: getUserName(data.body.user),
						}),
						{
							body: data.body.note.text || "",
							icon: data.body.user.avatarUrl,
							badge: iconUrl("poll-h"),
							data,
						},
					];

				case "pollEnded":
					return [
						t("_notification.pollEnded"),
						{
							body: data.body.note.text || "",
							badge: iconUrl("clipboard-check-solid"),
							data,
						},
					];

				case "receiveFollowRequest":
					return [
						t("_notification.youReceivedFollowRequest"),
						{
							body: getUserName(data.body.user),
							icon: data.body.user.avatarUrl,
							badge: iconUrl("clock"),
							data,
							actions: [
								{
									action: "accept",
									title: t("accept"),
								},
								{
									action: "reject",
									title: t("reject"),
								},
							],
						},
					];

				case "followRequestAccepted":
					return [
						t("_notification.yourFollowRequestAccepted"),
						{
							body: getUserName(data.body.user),
							icon: data.body.user.avatarUrl,
							badge: iconUrl("check"),
							data,
						},
					];

				case "groupInvited":
					return [
						t("_notification.youWereInvitedToGroup", {
							userName: getUserName(data.body.user),
						}),
						{
							body: data.body.invitation.group.name,
							badge: iconUrl("id-card-alt"),
							data,
							actions: [
								{
									action: "accept",
									title: t("accept"),
								},
								{
									action: "reject",
									title: t("reject"),
								},
							],
						},
					];

				case "app":
					return [
						data.body.header || data.body.body,
						{
							body: data.body.header && data.body.body,
							icon: data.body.icon,
							data,
						},
					];

				default:
					return null;
			}
		case "unreadMessagingMessage":
			if (data.body.groupId === null) {
				return [
					t("_notification.youGotMessagingMessageFromUser", {
						name: getUserName(data.body.user),
					}),
					{
						icon: data.body.user.avatarUrl,
						badge: iconUrl("comments"),
						tag: `messaging:user:${data.body.userId}`,
						data,
						renotify: true,
					},
				];
			}
			return [
				t("_notification.youGotMessagingMessageFromGroup", {
					name: data.body.group.name,
				}),
				{
					icon: data.body.user.avatarUrl,
					badge: iconUrl("comments"),
					tag: `messaging:group:${data.body.groupId}`,
					data,
					renotify: true,
				},
			];
		default:
			return null;
	}
}

export async function createEmptyNotification() {
	return new Promise<void>(async (res) => {
		if (!swLang.i18n) swLang.fetchLocale();
		const i18n = (await swLang.i18n) as I18n<any>;
		const { t } = i18n;

		await self.registration.showNotification(
			t("_notification.emptyPushNotificationMessage"),
			{
				silent: true,
				badge: iconUrl("null"),
				tag: "read_notification",
			},
		);

		res();

		setTimeout(async () => {
			for (const n of [
				...(await self.registration.getNotifications({
					tag: "user_visible_auto_notification",
				})),
				...(await self.registration.getNotifications({
					tag: "read_notification",
				})),
			]) {
				n.close();
			}
		}, 1000);
	});
}
