<script lang="ts">
	import { resolve } from '$app/paths';
	import {
		addPlaylistVideo,
		getComments,
		getPersonalPlaylists,
		removePlaylistVideo
	} from '$lib/api/index';
	import type { Comments, PlaylistPage } from '$lib/api/model';
	import Thumbnail from '$lib/components/thumbnail/VideoThumbnail.svelte';
	import Transcript from '$lib/components/watch/Transcript.svelte';
	import { getBestThumbnail } from '$lib/images';
	import { letterCase } from '$lib/letterCasing';
	import { numberWithCommas } from '$lib/numbers';
	import {
		invidiousAuthStore,
		interfaceAutoExpandChapters,
		interfaceAutoExpandComments,
		playerMiniplayerEnabled,
		playerPlaylistHistory,
		playerState,
		playerTheatreModeByDefaultStore,
		playerTheatreModeIsActive,
		playlistCacheStore,
		type PlayerState,
		filterContentListStore
	} from '$lib/store';
	import ui from 'beercss';
	import { onDestroy, onMount, tick } from 'svelte';
	import { _ } from '$lib/i18n';
	import { get } from 'svelte/store';
	import Author from '$lib/components/Author.svelte';
	import Description from '$lib/components/watch/Description.svelte';
	import LikesDislikes from '$lib/components/watch/LikesDislikes.svelte';
	import Comment from '$lib/components/watch/Comment.svelte';
	import { expandSummery, isYTBackend } from '$lib/misc';
	import { humanizeSeconds, relativeTimestamp } from '$lib/time';
	import { getWatchDetails } from '$lib/watch';
	import { page } from '$app/state';
	import Share from '$lib/components/Share.svelte';
	import Playlist from '$lib/components/watch/Playlist.svelte';
	import { isItemFiltered } from '$lib/filtering/index';

	let { data = $bindable() } = $props();

	let playerElement: HTMLMediaElement | undefined = $state();

	let comments: Comments | null = $state(null);
	data.streamed.comments?.then((streamedComments) => {
		comments = streamedComments;
	});

	let personalPlaylists: PlaylistPage[] | null = $state(null);
	data.streamed.personalPlaylists?.then((streamPlaylists) => (personalPlaylists = streamPlaylists));

	playerTheatreModeIsActive.set(get(playerTheatreModeByDefaultStore));

	let pauseTimerSeconds: number = $state(-1);

	let showTranscript = $state(false);

	let playerCurrentTime: number = $state(0);

	let premiereTime = $state('');
	let premiereUpdateInterval: ReturnType<typeof setTimeout>;

	if (
		!data.video.premiereTimestamp &&
		(!$playerState || $playerState.data.video.videoId !== data.video.videoId)
	) {
		playerState.set({
			data: data
		});
	}

	$effect(() => {
		if ($interfaceAutoExpandComments && comments) {
			expandSummery('comment-section');
		}
	});

	async function load(state: PlayerState) {
		playerElement = state.playerElement;

		if (data.playlistId) {
			await goToCurrentPlaylistItem();
			playerPlaylistHistory.set([data.video.videoId, ...$playerPlaylistHistory]);
		}

		if ($interfaceAutoExpandChapters) {
			expandSummery('chapter-section');
		}

		if (playerElement) {
			playerElement.addEventListener('timeupdate', () => {
				if (!playerElement) return;
				playerCurrentTime = playerElement.currentTime;
			});
		}
	}

	onMount(async () => {
		if (data.video.premiereTimestamp) {
			premiereTime = relativeTimestamp(data.video.premiereTimestamp);
			premiereUpdateInterval = setInterval(async () => {
				data = await getWatchDetails(data.video.videoId, page.url);

				if (data.video.premiereTimestamp) {
					premiereTime = relativeTimestamp(data.video.premiereTimestamp);
				} else {
					clearInterval(premiereUpdateInterval);
					playerState.set({ ...$playerState, data: { ...data } });
				}
			}, 60000);
		}

		// Required due to needing the playerElement
		if ($playerState?.playerElement) {
			await load($playerState);
		} else {
			let loadedPlayer = false;
			playerState.subscribe(async (updatedPlayerState) => {
				if (!updatedPlayerState?.playerElement || loadedPlayer) {
					return;
				}
				loadedPlayer = true;

				await load(updatedPlayerState);
			});
		}
	});

	onDestroy(() => {
		// Reset title when page left.
		document.title = 'Materialious';

		if (pauseTimeout) {
			clearTimeout(pauseTimeout);
		}

		if (premiereUpdateInterval) {
			clearInterval(premiereUpdateInterval);
		}

		if (
			!playerElement ||
			playerElement.paused ||
			playerElement.ended ||
			playerElement.currentTime === 0 ||
			playerElement.readyState <= 2 ||
			!$playerMiniplayerEnabled
		) {
			playerState.set(undefined);
		}

		playerTheatreModeIsActive.set(false);
	});

	async function goToCurrentPlaylistItem() {
		await tick();
		const playlistCurrentVideo = document.getElementById(data.video.videoId);
		const playlistScrollable = document.getElementById('playlist');

		if (playlistCurrentVideo && playlistScrollable) {
			playlistScrollable.scrollTop =
				playlistCurrentVideo.offsetTop - playlistScrollable.offsetTop - 200;
		}
	}

	async function toggleVideoToPlaylist(playlistId: string) {
		if (!personalPlaylists) return;

		const selectedPlaylist = personalPlaylists.filter((item) => {
			return item.playlistId === playlistId;
		});

		if (selectedPlaylist.length === 0) {
			return;
		}

		const videosToDelete = selectedPlaylist[0].videos.filter((item) => {
			return item.videoId === data.video.videoId;
		});

		if (videosToDelete.length > 0) {
			videosToDelete.forEach(async (toDelete) => {
				await removePlaylistVideo(playlistId, toDelete.indexId);
			});
		} else {
			await addPlaylistVideo(playlistId, data.video.videoId);
		}

		personalPlaylists = await getPersonalPlaylists();
	}

	async function loadMoreComments() {
		if (!comments) {
			return;
		}

		let loadedComments: Comments;

		try {
			if (comments.getContinuation) {
				loadedComments = await comments.getContinuation();
			} else if (comments.continuation) {
				loadedComments = await getComments(data.video.videoId, {
					continuation: comments.continuation
				});
			} else {
				return;
			}

			if (loadedComments.comments.length > 0) {
				comments.continuation = loadedComments.continuation;
				comments.comments = [...comments.comments, ...loadedComments.comments];
			}
		} catch (error) {
			console.error('Error loading more comments:', error);
		}
	}

	function toggleTheatreMode() {
		playerTheatreModeIsActive.set(!$playerTheatreModeIsActive);
	}

	let pauseTimeout: ReturnType<typeof setTimeout> | undefined = $state();
	function setPauseTimer() {
		if (pauseTimeout) {
			clearTimeout(pauseTimeout);
		}
		pauseTimeout = setTimeout(() => {
			playerElement?.pause();
			pauseTimerSeconds = 0;
			clearTimeout(pauseTimeout);
		}, pauseTimerSeconds * 1000);
	}
</script>

<svelte:head>
	<title>{data.video.title}</title>
</svelte:head>

<div class="grid no-padding">
	<div class={`s12 m12 l${$playerTheatreModeIsActive ? '12' : '9'}`}>
		<div style="display: flex;justify-content: center;">
			{#if data.video.premiereTimestamp}
				<article class="video-placeholder">
					<p>{$_('player.premiere')}</p>
					<h6 class="no-margin no-padding">
						{premiereTime}
					</h6>
				</article>
				<div class="space"></div>
			{/if}
		</div>

		<h5 class="no-margin">{letterCase(data.video.title)}</h5>

		<div class="grid no-padding">
			<div class="s12 m12 l7" style="height: 100%;display: flex;align-items: center;">
				<Author channel={data.video} />
			</div>
			<div class="s12 m12 l5 video-actions">
				<div>
					<LikesDislikes video={data.video} returnYTDislikes={data.streamed.returnYTDislikes} />

					<button
						onclick={toggleTheatreMode}
						class="m l"
						class:surface-container-highest={!$playerTheatreModeIsActive}
					>
						<i>width_wide</i>
						<div class="tooltip">{$_('player.theatreMode')}</div>
					</button>
					{#if data.video.lengthSeconds > 360 && !data.video.hlsUrl}
						<button
							onclick={() => {
								if (pauseTimerSeconds < 1) {
									pauseTimerSeconds = 300;
								}
								ui('#pause-timer');
							}}
							class:surface-container-highest={pauseTimerSeconds < 1}
						>
							<i>snooze</i>
							<div class="tooltip">{$_('player.pauseTimer')}</div>
						</button>
					{/if}
					<button
						onclick={() => (
							(showTranscript = !showTranscript),
							playerTheatreModeIsActive.set(false)
						)}
						class:surface-container-highest={!showTranscript}
					>
						<i>description</i>
						<div class="tooltip">
							{$_('transcript')}
						</div>
					</button>
					<Share
						includePromptText={$_('player.share.includeTimestamp')}
						shares={[
							{
								type: 'materialious',
								path: resolve('/watch/[videoId]', { videoId: data.video.videoId }),
								param: {
									key: 'time',
									value: () => Math.round(playerCurrentTime)
								}
							},
							{
								type: 'invidious',
								path: `/watch?v=${data.video.videoId}`,
								param: {
									key: 't',
									value: () => Math.round(playerCurrentTime)
								}
							},
							{
								type: 'invidious redirect',
								path: `/watch?v=${data.video.videoId}`,
								param: {
									key: 't',
									value: () => Math.round(playerCurrentTime)
								}
							},
							{
								type: 'youtube',
								path: `/watch?v=${data.video.videoId}`,
								param: {
									key: 't',
									value: () => Math.round(playerCurrentTime)
								}
							}
						]}
						iconOnly={true}
						style="margin-left: 0;"
					/>
					{#if personalPlaylists && personalPlaylists.length > 0}
						<button class="surface-container-highest">
							<i>add</i>
							<div class="tooltip">{$_('player.addToPlaylist')}</div>
							<menu class="no-wrap mobile">
								{#each personalPlaylists as personalPlaylist (personalPlaylist.playlistId)}
									<li
										role="presentation"
										class="row"
										onclick={async () => {
											await toggleVideoToPlaylist(personalPlaylist.playlistId);
											(document.activeElement as HTMLElement)?.blur();
										}}
									>
										<nav>
											<span class="max">{personalPlaylist.title}</span>
											{#if personalPlaylist.videos.filter((item) => {
												return item.videoId === data.video.videoId;
											}).length > 0}
												<i>close</i>
											{:else}
												<i>add</i>
											{/if}
										</nav>
									</li>
								{/each}
							</menu>
						</button>
					{:else}
						<button disabled class="surface-container-highest">
							<i>add</i>
							<div class="tooltip">
								{#if $invidiousAuthStore || isYTBackend()}
									{$_('player.noPlaylists')}
								{:else}
									{$_('loginRequired')}
								{/if}
							</div>
						</button>
					{/if}
				</div>
			</div>
		</div>

		<article class="border">
			<Description video={data.video} description={data.content.description} />
		</article>

		{#if data.content.timestamps.length > 0}
			<article class="border">
				<details>
					<summary id="chapter-section" class="bold none">
						<nav>
							<div class="max">
								<p>{$_('player.chapters')}</p>
							</div>
						</nav>
					</summary>
					<div class="space"></div>
					<div class="chapter-list" id="chapters">
						<ul class="list">
							{#each data.content.timestamps as timestamp (timestamp)}
								<li>
									<img
										class="round large"
										loading="lazy"
										src={getBestThumbnail(data.video.videoThumbnails) as string}
										alt="Thumbnail for current video"
										role="presentation"
										onclick={() => {
											if (playerElement) playerElement.currentTime = timestamp.time;
										}}
									/>
									<div
										role="presentation"
										onclick={() => {
											if (playerElement) playerElement.currentTime = timestamp.time;
										}}
										class="max"
										style="white-space: pre-line; overflow-wrap: break-word;"
									>
										<p style="margin: 0;">{timestamp.title}</p>
										<span
											class:primary={playerCurrentTime >= timestamp.time &&
												(playerCurrentTime <= timestamp.endTime || timestamp.endTime === -1)}
											class="chip no-margin">{timestamp.timePretty}</span
										>
									</div>
									<Share
										shares={[
											{
												type: 'materialious',
												path: resolve(`/watch/[videoId]?time=${Math.round(timestamp.time)}`, {
													videoId: data.video.videoId
												})
											},
											{
												type: 'invidious',
												path: `/watch?v=${data.video.videoId}&t=${Math.round(timestamp.time)}`
											},
											{
												type: 'invidious redirect',
												path: `/watch?v=${data.video.videoId}&t=${Math.round(timestamp.time)}`
											},
											{
												type: 'youtube',
												path: `/watch?v=${data.video.videoId}&t=${Math.round(timestamp.time)}`
											}
										]}
										iconOnly={true}
									/>
								</li>
							{/each}
						</ul>
					</div>
				</details>
			</article>
		{/if}

		{#if comments && comments.comments.length > 0}
			<article class="border">
				<details>
					<summary id="comment-section" class="none bold">
						<nav>
							<div class="max">{numberWithCommas(comments.commentCount)} {$_('comments')}</div>
						</nav>
					</summary>

					<div class="space"></div>

					<div class="comment-list">
						{#each comments.comments as comment (comment)}
							<Comment {comment} videoId={data.video.videoId}></Comment>
						{/each}
						{#if comments.continuation}
							<div class="space"></div>
							<button onclick={loadMoreComments} class="secondary">
								<i>read_more</i>
								<span>{$_('loadMore')}</span>
							</button>
						{/if}
					</div>
				</details>
			</article>
		{/if}
	</div>
	{#if !$playerTheatreModeIsActive}
		<div class="s12 m12 l3 recommended">
			{#if showTranscript}
				<Transcript video={data.video} bind:currentTime={playerCurrentTime} />
			{/if}
			{#if data.playlistId && data.playlistId in $playlistCacheStore}
				<Playlist video={data.video} playlist={$playlistCacheStore[data.playlistId]} />
			{:else if data.video.recommendedVideos}
				{#key $filterContentListStore?.length}
					{#each data.video.recommendedVideos as recommendedVideo (recommendedVideo.videoId)}
						{#if !isItemFiltered(recommendedVideo)}
							<article class="no-padding border">
								{#key recommendedVideo.videoId}
									<Thumbnail video={recommendedVideo} sideways={true} />
								{/key}
							</article>
						{/if}
					{/each}
				{/key}
			{/if}
		</div>
	{/if}
</div>

<dialog id="pause-timer">
	<div>
		<h6>{$_('player.pauseVideoIn')} {humanizeSeconds(pauseTimerSeconds)}</h6>

		<nav class="group">
			<button
				onclick={() => {
					pauseTimerSeconds += 300;
					setPauseTimer();
				}}
				class="left-round">+5 mins</button
			>
			<button
				onclick={() => {
					pauseTimerSeconds += 1800;
					setPauseTimer();
				}}
				class="no-round">+30 mins</button
			>
			<button
				onclick={() => {
					pauseTimerSeconds += 3600;
					setPauseTimer();
				}}
				class="no-round">+1 hr</button
			>
			<button
				onclick={() => {
					pauseTimerSeconds += 7200;
					setPauseTimer();
				}}
				class="right-round">+2 hrs</button
			>
		</nav>

		<div class="space"></div>

		<nav class="wrap">
			<button
				onclick={() => {
					pauseTimerSeconds = 0;
					clearTimeout(pauseTimeout);
					ui('#pause-timer');
				}}
				class="secondary max"
			>
				<i>delete</i>
				<span>Clear</span>
			</button>
		</nav>
	</div>
</dialog>

<style>
	.chapter-list,
	.comment-list {
		overflow-y: scroll;
		overflow-x: hidden;
	}

	.chapter-list {
		max-height: 300px;
	}

	.comment-list {
		max-height: 600px;
	}

	.recommended {
		margin-top: calc(var(--video-player-height) * -1);
	}

	.video-actions {
		display: flex;
		align-items: center;
		justify-content: flex-end;
	}

	.video-placeholder {
		height: 50vh;
	}

	.video-actions button {
		margin-left: 0;
	}

	@media screen and (max-width: 1200px) {
		.video-actions {
			align-items: flex-start;
			flex-direction: column;
		}
	}

	@media only screen and (max-width: 993px) {
		.recommended {
			margin-top: 0;
		}
	}
</style>
