<script lang="ts">
	import userIdentity from '$stores/identity';
	import type { AniListAuthorisation } from '$lib/AniList/identity';
	import { onMount } from 'svelte';
	import { tops, wrapped, type TopMedia, SortOptions } from '$lib/AniList/wrapped';
	import {
		fullActivityHistory,
		activityHistory as getActivityHistory
	} from '$lib/AniList/activity';
	import { Type, mediaListCollection, type Media } from '$lib/AniList/media';
	import anime from '$stores/anime';
	import lastPruneTimes from '$stores/lastPruneTimes';
	import manga from '$stores/manga';
	import Error from '$lib/Error/RateLimited.svelte';
	import { domToBlob } from 'modern-screenshot';
	import { browser } from '$app/environment';
	import { page } from '$app/stores';
	import { clearAllParameters } from '../../Utility/parameters';
	import SettingHint from '$lib/Settings/SettingHint.svelte';
	import { database } from '$lib/Database/activities';
	import Activity from './Top/Activity.svelte';
	import Anime from './Top/Anime.svelte';
	import Manga from './Top/Manga.svelte';
	import ActivityHistory from './ActivityHistory.svelte';
	import MediaExtras from './MediaExtras.svelte';
	import MediaPanel from './Media.svelte';
	import Watermark from './Watermark.svelte';
	import Skeleton from '$lib/Skeleton.svelte';
	import Message from '$lib/Loading/Message.svelte';

	export let user: AniListAuthorisation;

	const currentYear = new Date(Date.now()).getFullYear();
	let selectedYear = new Date(Date.now()).getFullYear();
	let episodes = 0;
	let chapters = 0;
	let minutesWatched = 0;
	let animeList: Media[] | undefined = undefined;
	let mangaList: Media[] | undefined = undefined;
	let calculatedAnimeList: Media[] | undefined = undefined;
	let calculatedMangaList: Media[] | undefined = undefined;
	let originalAnimeList: Media[] | undefined = undefined;
	let originalMangaList: Media[] | undefined = undefined;
	let transparency = false;
	let lightTheme = true;
	let watermark = false;
	let includeMusic = false;
	let includeSpecials = true;
	let includeRepeats = false;
	let width = 1920;
	let lightMode = false;
	let highestRatedCount = 5;
	let genreTagCount = 5;
	let mounted = false;
	let generated = false;
	let disableActivityHistory = true;
	let excludedKeywordsInput = '';
	let excludedKeywords: string[] = [];
	let useFullActivityHistory = false;
	let topGenresTags = true;
	let topMedia: TopMedia;
	let highestRatedMediaPercentage = true;
	let highestRatedGenreTagPercentage = true;
	let genreTagsSort = SortOptions.SCORE;
	let mediaSort = SortOptions.SCORE;
	let includeMovies = true;
	let includeOVAs = true;
	let activityHistoryPosition: 'TOP' | 'BELOW_TOP' | 'ORIGINAL' = 'ORIGINAL';
	let includeOngoingMediaFromPreviousYears = false;

	$: {
		if (browser && mounted) {
			$page.url.searchParams.set('transparency', transparency.toString());
			$page.url.searchParams.set('lightTheme', lightTheme.toString());
			$page.url.searchParams.set('watermark', watermark.toString());
			$page.url.searchParams.set('includeMusic', includeMusic.toString());
			$page.url.searchParams.set('includeSpecials', includeSpecials.toString());
			$page.url.searchParams.set('includeRepeats', includeRepeats.toString());
			$page.url.searchParams.set('lightMode', lightMode.toString());
			$page.url.searchParams.set('highestRatedCount', highestRatedCount.toString());
			$page.url.searchParams.set('genreTagCount', genreTagCount.toString());
			$page.url.searchParams.set('disableActivityHistory', disableActivityHistory.toString());
			$page.url.searchParams.set(
				'highestRatedMediaPercentage',
				highestRatedMediaPercentage.toString()
			);
			$page.url.searchParams.set(
				'highestRatedGenreTagPercentage',
				highestRatedGenreTagPercentage.toString()
			);
			$page.url.searchParams.set('genreTagsSort', genreTagsSort.toString());
			$page.url.searchParams.set('mediaSort', mediaSort.toString());
			$page.url.searchParams.set('includeMovies', includeMovies.toString());
			$page.url.searchParams.set('includeOVAs', includeOVAs.toString());

			history.replaceState(null, '', `?${$page.url.searchParams.toString()}`);
		}
	}
	$: {
		includeMusic = includeMusic;
		includeSpecials = includeSpecials;
		includeRepeats = includeRepeats;
		disableActivityHistory = disableActivityHistory;
		highestRatedMediaPercentage = highestRatedMediaPercentage;
		highestRatedGenreTagPercentage = highestRatedGenreTagPercentage;
		topGenresTags = topGenresTags;
		genreTagsSort = genreTagsSort;
		mediaSort = mediaSort;
		includeMovies = includeMovies;
		includeOVAs = includeOVAs;
		selectedYear = selectedYear;
		includeOngoingMediaFromPreviousYears = includeOngoingMediaFromPreviousYears;

		update().then(updateWidth).catch(updateWidth);
	}
	$: {
		animeList = animeList;
		mangaList = mangaList;
		highestRatedCount = highestRatedCount;

		new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
	}
	$: {
		genreTagCount = genreTagCount;

		if (animeList && mangaList)
			topMedia = tops(
				[...(animeList || []), ...(mangaList || [])],
				genreTagCount,
				genreTagsSort,
				excludedKeywords
			);

		new Promise((resolve) => setTimeout(resolve, 1)).then(updateWidth);
	}
	$: {
		excludedKeywords = excludedKeywords;

		if (excludedKeywords.length > 0 && animeList !== undefined && mangaList !== undefined) {
			animeList = originalAnimeList;
			mangaList = originalMangaList;
			animeList = excludeKeywords(animeList as Media[]);
			mangaList = excludeKeywords(mangaList as Media[]);
		}

		updateWidth();
	}
	$: genreTagTitle = (() => {
		switch (genreTagsSort) {
			case SortOptions.SCORE:
				return 'Highest Rated';
			case SortOptions.MINUTES_WATCHED:
				return 'Most Watched';
			case SortOptions.COUNT:
				return 'Most Common';
		}
	})();
	$: animeMostTitle = (() => {
		switch (mediaSort) {
			case SortOptions.SCORE:
				return 'Highest Rated';
			case SortOptions.MINUTES_WATCHED:
				return 'Most Watched';
			case SortOptions.COUNT:
				return 'Most Common';
		}
	})();
	$: mangaMostTitle = (() => {
		switch (mediaSort) {
			case SortOptions.SCORE:
				return 'Highest Rated';
			case SortOptions.MINUTES_WATCHED:
				return 'Most Read';
			case SortOptions.COUNT:
				return 'Most Common';
		}
	})();

	const updateWidth = () => {
		if (!browser) return;

		const wrappedContainer = document.querySelector('#wrapped') as HTMLElement;

		if (!wrappedContainer) return;

		wrappedContainer.style.width = `1920px`;

		const reset = () => {
			let topWidths = 0;
			let middleWidths = 0;
			let bottomWidths = 0;

			wrappedContainer.querySelectorAll('.category').forEach((item) => {
				const category = item as HTMLElement;
				const style = window.getComputedStyle(category);
				const width =
					category.offsetWidth +
					parseFloat(style.marginLeft) +
					parseFloat(style.marginRight) +
					parseFloat(style.paddingLeft) +
					parseFloat(style.paddingRight) +
					parseFloat(style.borderLeftWidth) +
					parseFloat(style.borderRightWidth);

				if (category.classList.contains('top-category')) {
					topWidths += width;
				} else if (category.classList.contains('middle-category')) {
					middleWidths += width;
				} else if (category.classList.contains('bottom-category')) {
					bottomWidths += width;
				}
			});

			let requiredWidth = topWidths > middleWidths ? topWidths : middleWidths;

			if (!disableActivityHistory && bottomWidths > requiredWidth) requiredWidth = bottomWidths;

			requiredWidth += wrappedContainer.offsetWidth - wrappedContainer.clientWidth;

			wrappedContainer.style.width = `${requiredWidth}px`;
			width = requiredWidth;
		};

		reset();
		reset();
	};

	onMount(async () => {
		clearAllParameters([
			'transparency',
			'lightTheme',
			'watermark',
			'includeMusic',
			'includeSpecials',
			'includeRepeats',
			'forceDark',
			'highestRatedCount',
			'genreTagCount',
			'disableActivityHistory',
			'highestRatedMediaPercentage',
			'highestRatedGenreTagPercentage',
			'genreTagsSort',
			'mediaSort',
			'includeMovies',
			'includeOVAs'
		]);

		if (browser) {
			transparency = $page.url.searchParams.get('transparency') === 'true';
			lightTheme = $page.url.searchParams.get('lightTheme') === 'true';
			watermark = $page.url.searchParams.get('watermark') === 'true';
			includeMusic = $page.url.searchParams.get('includeMusic') === 'true';
			includeSpecials = $page.url.searchParams.get('includeSpecials') === 'true';
			includeRepeats = $page.url.searchParams.get('includeRepeats') === 'true';
			lightMode = $page.url.searchParams.get('lightMode') === 'true';
			highestRatedCount = parseInt($page.url.searchParams.get('highestRatedCount') || '5', 10);
			genreTagCount = parseInt($page.url.searchParams.get('genreTagCount') || '5', 10);
			disableActivityHistory = $page.url.searchParams.get('disableActivityHistory') === 'true';
			highestRatedMediaPercentage =
				$page.url.searchParams.get('highestRatedMediaPercentage') === 'true';
			highestRatedGenreTagPercentage =
				$page.url.searchParams.get('highestRatedGenreTagPercentage') === 'true';
			// genreTagsSort = parseInt($page.url.searchParams.get('genreTagsSort') || '0', 10);
			// mediaSort = parseInt($page.url.searchParams.get('mediaSort') || '0', 10);
			includeMovies = $page.url.searchParams.get('includeMovies') === 'true';
			includeOVAs = $page.url.searchParams.get('includeOVAs') === 'true';
		}

		await update().then(() => (mounted = true));
	});

	const update = async () => {
		if ($userIdentity.id === -1) return;

		let rawAnimeList = await mediaListCollection(
			user,
			$userIdentity,
			Type.Anime,
			$anime,
			$lastPruneTimes.anime,
			{
				forcePrune: true,
				includeCompleted: true,
				all: true
			}
		);
		calculatedAnimeList = rawAnimeList
			.filter(
				(item, index, self) =>
					self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index &&
					(includeMusic ? true : item.format !== 'MUSIC') &&
					(includeRepeats
						? true
						: item.startDate.year === selectedYear || item.endDate.year === selectedYear
						? true
						: item.mediaListEntry?.repeat === 0) &&
					(item.mediaListEntry?.startedAt.year === selectedYear ||
						item.mediaListEntry?.completedAt.year === selectedYear ||
						((item.mediaListEntry?.createdAt
							? new Date(item.mediaListEntry?.createdAt * 1000).getFullYear() === selectedYear
							: false) && item.mediaListEntry
							? item.mediaListEntry?.progress >= 1
							: false)) &&
					(includeMovies ? true : item.format !== 'MOVIE') &&
					(includeSpecials ? true : item.format !== 'SPECIAL') &&
					(includeOVAs ? true : item.format !== 'OVA')
			)
			.sort((a, b) => {
				switch (mediaSort) {
					case SortOptions.MINUTES_WATCHED:
						if (a.duration === undefined || a.mediaListEntry?.progress === undefined) return 1;
						else if (b.duration === undefined || b.mediaListEntry?.progress === undefined)
							return -1;
						else
							return (
								b.duration * b.mediaListEntry.progress - a.duration * a.mediaListEntry.progress
							);
					case SortOptions.SCORE:
					default:
						if (a.mediaListEntry?.score === undefined) return 1;
						else if (b.mediaListEntry?.score === undefined) return -1;
						else return b.mediaListEntry?.score - a.mediaListEntry?.score;
				}
			});

		animeList = rawAnimeList
			.filter(
				(item, index, self) =>
					self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index &&
					(includeMusic ? true : item.format !== 'MUSIC') &&
					(includeRepeats
						? true
						: item.startDate.year === selectedYear || item.endDate.year === selectedYear
						? true
						: item.mediaListEntry?.repeat === 0) &&
					(item.mediaListEntry?.startedAt.year === selectedYear ||
						item.mediaListEntry?.completedAt.year === selectedYear ||
						((item.mediaListEntry?.createdAt
							? new Date(item.mediaListEntry?.createdAt * 1000).getFullYear() === selectedYear
							: false) && item.mediaListEntry
							? item.mediaListEntry?.progress >= 1
							: false) ||
						(includeOngoingMediaFromPreviousYears
							? (item.mediaListEntry?.updatedAt
									? new Date(item.mediaListEntry?.updatedAt * 1000).getFullYear() === selectedYear
									: false) && item.mediaListEntry
								? item.mediaListEntry?.status === 'CURRENT'
								: false
							: false)) &&
					(includeMovies ? true : item.format !== 'MOVIE') &&
					(includeSpecials ? true : item.format !== 'SPECIAL') &&
					(includeOVAs ? true : item.format !== 'OVA')
			)
			.sort((a, b) => {
				switch (mediaSort) {
					case SortOptions.MINUTES_WATCHED:
						if (a.duration === undefined || a.mediaListEntry?.progress === undefined) return 1;
						else if (b.duration === undefined || b.mediaListEntry?.progress === undefined)
							return -1;
						else
							return (
								b.duration * b.mediaListEntry.progress - a.duration * a.mediaListEntry.progress
							);
					case SortOptions.SCORE:
					default:
						if (a.mediaListEntry?.score === undefined) return 1;
						else if (b.mediaListEntry?.score === undefined) return -1;
						else return b.mediaListEntry?.score - a.mediaListEntry?.score;
				}
			});

		let rawMangaList = await mediaListCollection(
			user,
			$userIdentity,
			Type.Manga,
			$manga,
			$lastPruneTimes.manga,
			{
				forcePrune: true,
				includeCompleted: true,
				all: true
			}
		);
		calculatedMangaList = rawMangaList
			.filter(
				(item, index, self) =>
					self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index &&
					(includeRepeats ? true : item.mediaListEntry?.repeat === 0) &&
					(item.mediaListEntry?.startedAt.year === selectedYear ||
						item.mediaListEntry?.completedAt.year === selectedYear ||
						((item.mediaListEntry?.createdAt
							? new Date(item.mediaListEntry?.createdAt * 1000).getFullYear() === selectedYear
							: false) && item.mediaListEntry
							? item.mediaListEntry?.progress >= 1
							: false))
			)
			.sort((a, b) => {
				if (a.mediaListEntry?.score === undefined) return 1;
				else if (b.mediaListEntry?.score === undefined) return -1;
				else return b.mediaListEntry?.score - a.mediaListEntry?.score;
			});

		mangaList = rawMangaList
			.filter(
				(item, index, self) =>
					self.findIndex((itemToCompare) => itemToCompare.id === item.id) === index &&
					(includeRepeats ? true : item.mediaListEntry?.repeat === 0) &&
					(item.mediaListEntry?.startedAt.year === selectedYear ||
						item.mediaListEntry?.completedAt.year === selectedYear ||
						((item.mediaListEntry?.createdAt
							? new Date(item.mediaListEntry?.createdAt * 1000).getFullYear() === selectedYear
							: false) && item.mediaListEntry
							? item.mediaListEntry?.progress >= 1
							: false) ||
						(includeOngoingMediaFromPreviousYears
							? (item.mediaListEntry?.updatedAt
									? new Date(item.mediaListEntry?.updatedAt * 1000).getFullYear() === selectedYear
									: false) && item.mediaListEntry
								? item.mediaListEntry?.status === 'CURRENT'
								: false
							: false))
			)
			.sort((a, b) => {
				if (a.mediaListEntry?.score === undefined) return 1;
				else if (b.mediaListEntry?.score === undefined) return -1;
				else return b.mediaListEntry?.score - a.mediaListEntry?.score;
			});

		episodes = 0;
		minutesWatched = 0;
		chapters = 0;

		for (const media of calculatedAnimeList) {
			episodes += media.mediaListEntry?.progress || 0;
			minutesWatched += (media.mediaListEntry?.progress || 0) * media.duration || 0;
		}

		for (const media of calculatedMangaList) chapters += media.mediaListEntry?.progress || 0;
	};

	/* eslint-disable @typescript-eslint/no-explicit-any */
	// const year = (statistic: { startYears: any }) =>
	// 	statistic.startYears.find((y: { startYear: number }) => y.startYear === 2023);

	const screenshot = async () => {
		let element = document.querySelector('#wrapped') as HTMLElement;

		if (element !== null) {
			domToBlob(element, {
				backgroundColor: transparency ? 'transparent' : lightTheme ? '#edf1f5' : '#0b1622',
				quality: 1,
				scale: 2,
				fetch: {
					requestInit: {
						mode: 'cors'
					},
					bypassingCache: true
				}
			}).then((blob) => {
				const downloadWrapper = document.createElement('a');
				// const wrappedImageButton = document.getElementById(
				// 	'wrapped-image-download'
				// ) as HTMLAnchorElement;
				const image = document.createElement('img');
				const object = (window.URL || window.webkitURL || window || {}).createObjectURL(blob);

				// downloadWrapper.download = `due_dot_moe_wrapped_${dark ? 'dark' : 'light'}.png`;
				downloadWrapper.href = object;
				downloadWrapper.target = '_blank';
				image.src = object;

				downloadWrapper.appendChild(image);

				// if (wrappedImageButton !== null) {
				// 	wrappedImageButton.href = object;
				// }

				const wrappedFinal = document.getElementById('wrapped-final');

				if (wrappedFinal !== null) {
					wrappedFinal.innerHTML = '';

					wrappedFinal.appendChild(downloadWrapper);

					generated = true;
				}

				downloadWrapper.click();
			});
		}
	};

	// const abbreviate = (string: string, maxLength = 40, enabled = true) => {
	// 	if (!enabled) {
	// 		return string;
	// 	}

	// 	if (string.length <= maxLength) {
	// 		return string;
	// 	}

	// 	return string.slice(0, maxLength - 3) + ' …';
	// };

	const submitExcludedKeywords = () => {
		if (excludedKeywordsInput.length <= 0 && excludedKeywords.length > 0) {
			animeList = originalAnimeList;
			mangaList = originalMangaList;
			excludedKeywords = [];
		} else if (excludedKeywordsInput.length >= 0 && excludedKeywords.length <= 0) {
			originalAnimeList = animeList;
			originalMangaList = mangaList;
		}

		if (excludedKeywordsInput.length > 0)
			excludedKeywords = excludedKeywordsInput
				.split(',')
				.map((k) => k.trim())
				.filter((k) => k.length > 0);
	};

	const excludeKeywords = (media: Media[]) => {
		if (excludedKeywords.length <= 0) return media;

		return media.filter((m) => {
			for (const keyword of excludedKeywords) {
				if (m.title.english?.toLowerCase().includes(keyword.toLowerCase())) return false;
				if (m.title.romaji?.toLowerCase().includes(keyword.toLowerCase())) return false;
				if (m.title.native?.toLowerCase().includes(keyword.toLowerCase())) return false;
			}

			return true;
		});
	};

	const pruneFullYear = async () => {
		await database.activities.bulkDelete((await database.activities.toArray()).map((m) => m.page));
	};

	// const mergeArraySort = (a: any, b: any, mode: 'tags' | 'genres') => {
	// 	let merged = [...a, ...b].sort((a, b) => b.meanScore - a.meanScore);

	// 	merged = merged.filter(
	// 		(item, index, self) =>
	// 			self.findIndex((itemToCompare) =>
	// 				mode === 'genres'
	// 					? itemToCompare.genre === item.genre
	// 					: itemToCompare.tag.name === item.tag.name
	// 			) === index
	// 	);

	// 	return merged;
	// };

	// const randomCoverFromTop10 = (
	// 	statistics: { anime: any; manga: any },
	// 	mode: 'tags' | 'genres'
	// ) => {
	// 	const top = mergeArraySort(statistics.anime[mode], statistics.manga[mode], mode);

	// 	return mediaCover(top[Math.floor(Math.random() * top.length)].mediaIds[0]);
	// };
</script>

{#if $userIdentity.id === undefined}
	<div class="card">Please log in to view this page.</div>
{:else if $userIdentity.id !== -1}
	{#await selectedYear !== currentYear || useFullActivityHistory || new Date().getMonth() <= 6 ? fullActivityHistory(user, $userIdentity, selectedYear) : getActivityHistory($userIdentity)}
		<Message message="Loading activity history ..." />

		<Skeleton count={2} />
	{:then activities}
		{#await wrapped(user, $userIdentity, selectedYear)}
			<Message message="Loading user data ..." />

			<Skeleton count={2} />
		{:then wrapped}
			<div id="list-container">
				<div class="card">
					<div
						id="wrapped"
						class:light-theme={lightMode}
						style={`width: ${width}px; flex-shrink: 0;`}
						class:transparent={transparency}
					>
						{#if !disableActivityHistory && activityHistoryPosition === 'TOP' && activities.length > 0 && selectedYear === currentYear}
							<ActivityHistory {user} {activities} year={selectedYear} {activityHistoryPosition} />
						{/if}
						<div class="categories-grid" style="padding-bottom: 0;">
							<Activity
								{wrapped}
								year={selectedYear}
								{activities}
								{useFullActivityHistory}
								{updateWidth}
							/>
							<Anime animeList={calculatedAnimeList} {minutesWatched} {episodes} />
							<Manga mangaList={calculatedMangaList} {chapters} />
						</div>
						{#if !disableActivityHistory && activityHistoryPosition === 'BELOW_TOP' && activities.length > 0 && selectedYear === currentYear}
							<ActivityHistory {user} {activities} year={selectedYear} {activityHistoryPosition} />
						{/if}
						<MediaPanel
							{animeList}
							{mangaList}
							{highestRatedMediaPercentage}
							{highestRatedCount}
							{updateWidth}
							{wrapped}
							{animeMostTitle}
							{mangaMostTitle}
						/>
						{#if topMedia && topGenresTags && ((topMedia.topGenreMedia && topMedia.genres.length > 0) || (topMedia.topTagMedia && topMedia.tags.length > 0))}
							<MediaExtras
								{topMedia}
								{genreTagTitle}
								{highestRatedGenreTagPercentage}
								{updateWidth}
							/>
						{/if}
						{#if !disableActivityHistory && activityHistoryPosition === 'ORIGINAL' && activities.length > 0 && selectedYear === currentYear}
							<ActivityHistory {user} {activities} year={selectedYear} {activityHistoryPosition} />
						{/if}
						{#if watermark}
							<Watermark />
						{/if}
					</div>
				</div>
				<div class="list">
					<div class:card={generated}>
						<div id="wrapped-final" />

						{#if generated}
							<p />

							<blockquote style="margin: 0 0 0 1.5rem;">
								Click on the image to download, or right click and select "Save Image As...".
							</blockquote>
						{/if}
					</div>

					{#if generated}
						<p />
					{/if}

					<div id="options" class="card">
						<button on:click={screenshot} data-umami-event="Generate Wrapped">
							Generate image
						</button>

						<details class="no-shadow" open>
							<summary>Display</summary>

							<input type="checkbox" bind:checked={watermark} /> Show watermark<br />
							<input type="checkbox" bind:checked={transparency} /> Enable background transparency<br
							/>
							<input type="checkbox" bind:checked={lightMode} />
							Enable light mode<br />
							<input type="checkbox" bind:checked={topGenresTags} />
							Show top genres and tags<br />
							<input
								type="checkbox"
								bind:checked={disableActivityHistory}
								disabled={selectedYear !== currentYear}
							/>
							Hide activity history<br />
							<input type="checkbox" bind:checked={highestRatedMediaPercentage} /> Show highest
							rated media percentages<br />
							<input type="checkbox" bind:checked={highestRatedGenreTagPercentage} /> Show highest
							rated genre and tag percentages<br />
							<input type="checkbox" bind:checked={includeOngoingMediaFromPreviousYears} /> Show
							ongoing media from previous years<br />
							<select bind:value={activityHistoryPosition}>
								<option value="TOP">Above Top Row</option>
								<option value="BELOW_TOP">Below Top Row</option>
								<option value="ORIGINAL">Bottom</option>
							</select>
							Activity history position<br />
							<select bind:value={highestRatedCount}>
								{#each [3, 4, 5, 6, 7, 8, 9, 10] as count}
									<option value={count}>{count}</option>
								{/each}
							</select>
							Highest rated media count<br />
							<select bind:value={genreTagCount}>
								{#each [3, 4, 5, 6, 7, 8, 9, 10] as count}
									<option value={count}>{count}</option>
								{/each}
							</select>
							Highest genre and tag count<br />
							<button on:click={updateWidth}>Find best fit</button>
							<button on:click={() => (width -= 25)}>-25px</button>
							<button on:click={() => (width += 25)}>+25px</button>
							Width adjustment<br />
						</details>

						<details class="no-shadow" open>
							<summary>Calculation</summary>

							<input type="checkbox" bind:checked={useFullActivityHistory} />
							Enable full-year activity<button class="smaller-button" on:click={pruneFullYear}
								>Refresh data</button
							>
							<br />
							<select bind:value={selectedYear}>
								{#each Array.from({ length: currentYear - 2012 }) as _, i}
									<option value={currentYear - i}>
										{currentYear - i}
									</option>
								{/each}
							</select>
							Calculate for year<br />
							<select bind:value={mediaSort}>
								<option value={SortOptions.SCORE}>Score</option>
								<option value={SortOptions.MINUTES_WATCHED}>Minutes Watched/Read</option>
							</select>
							Anime and manga sort<br />
							<select bind:value={genreTagsSort}>
								<option value={SortOptions.SCORE}>Score</option>
								<option value={SortOptions.MINUTES_WATCHED}>Minutes Watched/Read</option>
								<option value={SortOptions.COUNT}>Count</option>
							</select>
							Genre and tag sort<br />
							<input type="checkbox" bind:checked={includeMusic} /> Include music<br />
							<input type="checkbox" bind:checked={includeRepeats} /> Include rewatches & rereads<br
							/>
							<input type="checkbox" bind:checked={includeSpecials} /> Include specials<br />
							<input type="checkbox" bind:checked={includeOVAs} /> Include OVAs<br />
							<input type="checkbox" bind:checked={includeMovies} /> Include movies<br />
							<input
								type="text"
								bind:value={excludedKeywordsInput}
								on:keypress={(e) => {
									e.key === 'Enter' && submitExcludedKeywords();
								}}
							/>
							Excluded keywords
							<button on:click={submitExcludedKeywords} title="Or click your Enter key"
								>Submit</button
							>
							<br />
							<SettingHint>Comma separated list (e.g., "My Hero, Kaguya")</SettingHint>
						</details>
					</div>
				</div>
			</div>
		{:catch}
			<Error type="User" card list={false} />
		{/await}
	{:catch}
		<Error
			card
			type={`${useFullActivityHistory ? 'Full-year activity' : 'Activity'} history`}
			loginSessionError={!useFullActivityHistory}
			list={false}
		>
			{#if useFullActivityHistory}
				<p>
					With <b>many</b> activities, it may take multiple attempts to obtain all of your activity history
					from AniList. If this occurs, wait one minute and try again to continue populating your local
					activity history database.
				</p>
			{/if}
		</Error>
	{/await}
{:else}
	<Message message="Loading user ..." />

	<Skeleton count={2} />
{/if}

<style>
	@import './wrapped.css';
</style>
