<script lang="ts">
	import { onMount } from 'svelte';
	import Message from '$lib/Loading/Message.svelte';
	import Skeleton from '$lib/Loading/Skeleton.svelte';
	import HeadTitle from '$lib/Home/HeadTitle.svelte';
	import { parseScheduleHtml } from '$lib/Data/hololive';
	import proxy from '$lib/Utility/proxy';
	import locale from '$stores/locale';
	import root from '$lib/Utility/root';
	import identity from '$stores/identity';
	import Icon from '@iconify/svelte';

	interface ParseResult {
		lives: {
			time: Date;
			link: string;
			videoId: string;
			streamer: string;
			livePreviewImage: string;
			guests: string[];
			streaming: boolean;
		}[];
		dict: Record<string, string>;
	}

	let schedulePromise: Promise<Response>;
	let pinnedStreams: string[] = [];

	onMount(() => getPinnedStreams());

	const getPinnedStreams = () => {
		let streams: string[] = [];

		const setSchedule = () => {
			pinnedStreams = streams;
			schedulePromise = fetch(proxy('https://schedule.hololive.tv'), {
				headers: {
					Cookie: 'timezone=Asia/Tokyo'
				}
			});
		};

		if ($identity.id !== -2) {
			fetch(root(`/api/configuration?id=${$identity.id}`)).then((response) => {
				if (response.ok)
					response.json().then((data) => {
						if (data && data.configuration) streams = data.pinned_hololive_streams;

						setSchedule();
					});
			});

			return;
		}

		setSchedule();
	};

	const typeSchedule = (schedule: any) => schedule as ParseResult;

	const pinStream = (streamer: string) =>
		fetch(root(`/api/configuration/pin?stream=${encodeURIComponent(streamer)}`), {
			method: 'PUT',
			headers: {
				'Content-Type': 'application/json'
			}
		}).then(getPinnedStreams);

	const getClosestUpcomingPinnedStreams = (schedule: ParseResult) => {
		const now = Date.now();
		let closestUpcomingPinnedStreams = new Map();

		schedule.lives.forEach((live) => {
			const liveTime = new Date(live.time).getTime();

			if (liveTime > now && pinnedStreams.includes(live.streamer)) {
				const existing = closestUpcomingPinnedStreams.get(live.streamer);

				if (!existing || liveTime < new Date(existing.time).getTime())
					closestUpcomingPinnedStreams.set(live.streamer, live);
			}
		});

		return closestUpcomingPinnedStreams;
	};
</script>

<HeadTitle route="hololive Schedule" path="/hololive" />

{#await schedulePromise}
	<Message message="Loading schedule ..." />

	<Skeleton grid={true} count={100} width="49%" height="16.25em" />
{:then scheduleResponse}
	{#if scheduleResponse}
		{#await scheduleResponse.text()}
			<Message message="Parsing schedule ..." />

			<Skeleton grid={true} count={100} width="49%" height="16.25em" />
		{:then untypedSchedule}
			{@const schedule = typeSchedule(parseScheduleHtml(untypedSchedule))}
			{@const closestUpcomingPinnedStreams = getClosestUpcomingPinnedStreams(schedule)}

			{#if schedule.lives.length === 0}
				<Message message="No upcoming streams." loader="ripple" />
			{/if}

			<div class="container">
				{#each schedule.lives
					.filter((live) => {
						try {
							$locale().hololive.dateFormatter(new Date(live.time));

							return true;
						} catch {
							return false;
						}
					})
					.sort((a, b) => {
						const now = Date.now();
						const aTime = new Date(a.time).getTime();
						const bTime = new Date(b.time).getTime();
						const aIsLive = a.streaming;
						const bIsLive = b.streaming;
						const aIsClosestPinned = closestUpcomingPinnedStreams.get(a.streamer) === a;
						const bIsClosestPinned = closestUpcomingPinnedStreams.get(b.streamer) === b;

						if (aIsLive && pinnedStreams.includes(a.streamer)) return -1;
						if (bIsLive && pinnedStreams.includes(b.streamer)) return 1;

						if (aIsLive && !bIsLive) return -1;
						if (bIsLive && !aIsLive) return 1;

						if (aIsClosestPinned && !bIsClosestPinned) return -1;
						if (bIsClosestPinned && !aIsClosestPinned) return 1;

						if (aTime > now && !(aTime < now && !aIsLive)) return -1;
						if (bTime > now && !(bTime < now && !bIsLive)) return 1;

						return bTime - aTime;
					}) as live}
					<div class="stream card">
						{#if $identity.id !== -2}
							<div class="pin-icon">
								<a
									href={'#'}
									on:click={(e) => {
										e.preventDefault();
										pinStream(live.streamer);
									}}
								>
									<Icon
										icon={`pajamas:thumbtack${
											pinnedStreams.includes(live.streamer) ? '-solid' : ''
										}`}
									/>
								</a>
							</div>
						{/if}

						<p>
							[{#if live.streaming}
								<b class="live">{$locale().hololive.live}</b
								>{:else if new Date(live.time).getTime() < Date.now()}
								<span class="ended">{$locale().hololive.ended}</span>{:else}
								<span class="upcoming">{$locale().hololive.upcoming}</span>{/if}]
							<b>{live.streamer}</b> <span class="opaque">|</span>
							{$locale().hololive.dateFormatter(new Date(live.time))}
							{#if live.guests.length > 0}
								<br />
								<small>
									{$locale().hololive.with}{live.guests
										.join($locale().hololive.comma)
										.replace(
											new RegExp(
												`${$locale().hololive.comma}([^${$locale().hololive.commaNoSpace}]+)$`,
												'g'
											),
											`${$locale().hololive.comma}${$locale().hololive.ampersand}$1`
										)}
								</small>
							{/if}
						</p>

						<a href={live.link} class="preview" target="_blank">
							<img src={live.livePreviewImage} alt="Stream Thumbnail" />
						</a>
					</div>
				{/each}
			</div>
		{:catch}
			<Message loader="ripple" slot>
				{$locale().hololive.parseError}
				<a href={'#'} on:click={() => location.reload()}>Try again?</a>
			</Message>
		{/await}
	{:else}
		<Message message="Loading schedule ..." />

		<Skeleton grid={true} count={100} width="49%" height="16.25em" />
	{/if}
{:catch}
	<Message message="" loader="ripple" slot>
		{$locale().hololive.loadError} Please
		<a href={'#'} on:click={() => location.reload()}>try again</a> later.
	</Message>
{/await}

<style lang="scss">
	.preview {
		// margin: 0.15rem;

		img {
			border-radius: 8px;
			height: 20vh;
			object-fit: cover;
			transition: transform 0.3s ease;
		}

		&:hover {
			img {
				position: relative;
				z-index: 2;
				transition: transform 0.3s ease;
				transform: scale(1.05);
			}
		}

		display: flex;
		justify-content: center;
		align-items: center;
	}

	.live {
		color: var(--base0F);
	}

	.upcoming {
		color: var(--base0C);
	}

	.ended {
		color: var(--base0D);
	}

	.container {
		display: grid;
		grid-template-columns: repeat(auto-fill, minmax(22.5em, 1fr));
		gap: 0.5em;
	}

	.pin-icon {
		position: absolute;
		right: 0;
		top: 0;
		padding: 0.75rem;
	}

	.stream {
		position: relative;
	}
</style>
