<template>
	<div>
		<ConfirmModal
			v-if="canEdit"
			ref="deleteModal"
			:title="formatMessage(messages.deleteModalTitle)"
			:description="formatMessage(messages.deleteModalDescription)"
			:has-to-type="false"
			:proceed-label="formatMessage(commonMessages.deleteLabel)"
			@proceed="deleteCollection()"
		/>
		<div class="normal-page">
			<div class="normal-page__sidebar">
				<div class="card">
					<div class="card__overlay input-group">
						<template v-if="canEdit && isEditing === false">
							<Button @click="isEditing = true">
								<EditIcon aria-hidden="true" />
								{{ formatMessage(commonMessages.editButton) }}
							</Button>
							<Button id="delete-collection" @click="() => $refs.deleteModal.show()">
								<TrashIcon aria-hidden="true" />
								{{ formatMessage(commonMessages.deleteLabel) }}
							</Button>
						</template>
						<template v-else-if="canEdit && isEditing === true">
							<PopoutMenu class="btn">
								<EditIcon aria-hidden="true" />
								{{ formatMessage(messages.editIconButton) }}
								<template #menu>
									<span class="icon-edit-menu">
										<FileInput
											id="project-icon"
											:max-size="262144"
											:show-icon="true"
											accept="image/png,image/jpeg,image/gif,image/webp"
											class="btn btn-transparent upload"
											style="white-space: nowrap"
											aria-label="Upload icon"
											@change="showPreviewImage"
										>
											<UploadIcon aria-hidden="true" />
										</FileInput>
										<Button
											v-if="!deletedIcon && (previewImage || collection.icon_url)"
											style="white-space: nowrap"
											transparent
											@click="
												() => {
													deletedIcon = true
													previewImage = null
												}
											"
										>
											<TrashIcon aria-hidden="true" />
											{{ formatMessage(messages.deleteIconButton) }}
										</Button>
									</span>
								</template>
							</PopoutMenu>
						</template>
					</div>
					<!-- Editing -->
					<template v-if="isEditing">
						<div class="inputs universal-labels">
							<div class="avatar-section">
								<Avatar
									size="md"
									:src="deletedIcon ? null : previewImage ? previewImage : collection.icon_url"
								/>
							</div>
							<label for="collection-title">
								<span class="label__title">
									{{ formatMessage(commonMessages.titleLabel) }}
								</span>
							</label>
							<input id="collection-title" v-model="name" maxlength="255" type="text" />
							<label for="collection-description">
								<span class="label__title">
									{{ formatMessage(commonMessages.descriptionLabel) }}
								</span>
							</label>
							<div class="textarea-wrapper">
								<textarea id="collection-description" v-model="summary" maxlength="255" />
							</div>
							<label for="visibility">
								<span class="label__title">
									{{ formatMessage(commonMessages.visibilityLabel) }}
								</span>
							</label>
							<DropdownSelect
								id="visibility"
								v-model="visibility"
								:options="['listed', 'unlisted', 'private']"
								:disabled="visibility === 'rejected'"
								:multiple="false"
								:display-name="
									(s) => {
										if (s === 'listed') return formatMessage(commonMessages.publicLabel)
										return formatMessage(commonMessages[`${s}Label`])
									}
								"
								:searchable="false"
							/>
						</div>
						<div class="push-right input-group">
							<Button @click="isEditing = false">
								<XIcon aria-hidden="true" />
								{{ formatMessage(commonMessages.cancelButton) }}
							</Button>
							<Button color="primary" @click="saveChanges()">
								<SaveIcon aria-hidden="true" />
								{{ formatMessage(commonMessages.saveButton) }}
							</Button>
						</div>
					</template>
					<!-- Content -->
					<template v-if="!isEditing">
						<div class="page-header__icon">
							<Avatar size="md" :src="collection.icon_url" />
						</div>
						<div class="page-header__text">
							<h1 class="title">{{ collection.name }}</h1>

							<div>
								<span class="collection-label">
									<BoxIcon aria-hidden="true" />
									{{ formatMessage(messages.collectionLabel) }}
								</span>
							</div>

							<div class="collection-info">
								<div class="metadata-item markdown-body collection-description">
									<p>{{ collection.description }}</p>
								</div>

								<hr class="card-divider" />

								<div v-if="canEdit" class="primary-stat">
									<template v-if="collection.status === 'listed'">
										<GlobeIcon class="primary-stat__icon" aria-hidden="true" />
										<div class="primary-stat__text">
											<strong>
												{{ formatMessage(commonMessages.publicLabel) }}
											</strong>
										</div>
									</template>
									<template v-else-if="collection.status === 'unlisted'">
										<LinkIcon class="primary-stat__icon" aria-hidden="true" />
										<div class="primary-stat__text">
											<strong>
												{{ formatMessage(commonMessages.unlistedLabel) }}
											</strong>
										</div>
									</template>
									<template v-else-if="collection.status === 'private'">
										<LockIcon class="primary-stat__icon" aria-hidden="true" />
										<div class="primary-stat__text">
											<strong>
												{{ formatMessage(commonMessages.privateLabel) }}
											</strong>
										</div>
									</template>
									<template v-else-if="collection.status === 'rejected'">
										<XIcon class="primary-stat__icon" aria-hidden="true" />
										<div class="primary-stat__text">
											<strong>
												{{ formatMessage(commonMessages.rejectedLabel) }}
											</strong>
										</div>
									</template>
								</div>
							</div>

							<div class="primary-stat">
								<LibraryIcon class="primary-stat__icon" aria-hidden="true" />
								<div v-if="projects" class="primary-stat__text">
									<IntlFormatted
										:message-id="messages.projectsCountLabel"
										:values="{
											count: formatCompactNumber(projects.length || 0),
										}"
									>
										<template #stat="{ children }">
											<span class="primary-stat__counter">
												<component :is="() => normalizeChildren(children)" />
											</span>
										</template>
									</IntlFormatted>
								</div>
							</div>

							<div class="metadata-item">
								<div
									v-tooltip="
										formatMessage(commonMessages.dateAtTimeTooltip, {
											date: new Date(collection.created),
											time: new Date(collection.created),
										})
									"
									class="date"
								>
									<CalendarIcon aria-hidden="true" />
									<label>
										{{
											formatMessage(messages.createdAtLabel, {
												ago: formatRelativeTime(collection.created),
											})
										}}
									</label>
								</div>
							</div>

							<div v-if="collection.id !== 'following'" class="metadata-item">
								<div
									v-tooltip="
										formatMessage(commonMessages.dateAtTimeTooltip, {
											date: new Date(collection.updated),
											time: new Date(collection.updated),
										})
									"
									class="date"
								>
									<UpdatedIcon aria-hidden="true" />
									<label>
										{{
											formatMessage(messages.updatedAtLabel, {
												ago: formatRelativeTime(collection.updated),
											})
										}}
									</label>
								</div>
							</div>
						</div>

						<hr class="card-divider" />

						<div class="collection-info">
							<h2 class="card-header">
								{{ formatMessage(messages.curatedByLabel) }}
							</h2>
							<div class="metadata-item">
								<nuxt-link
									class="team-member columns button-transparent"
									:to="'/user/' + creator.username"
								>
									<Avatar :src="creator.avatar_url" :alt="creator.username" size="sm" circle />

									<div class="member-info">
										<p class="name">{{ creator.username }}</p>
										<p class="role">{{ formatMessage(messages.ownerLabel) }}</p>
									</div>
								</nuxt-link>
							</div>
							<!-- <hr class="card-divider" />
            <div class="input-group">
              <Button @click="() => $refs.shareModal.show()">
                <ShareIcon />
                Share
              </Button>
            </div> -->
						</div>
					</template>
				</div>
				<!-- <AdPlaceholder v-if="!auth.user" /> -->
			</div>
			<div class="normal-page__content">
				<nav class="navigation-card">
					<NavRow
						:links="[
							{
								label: formatMessage(commonMessages.allProjectType),
								href: `/collection/${collection.id}`,
							},
							...projectTypes.map((x) => {
								return {
									label: formatMessage(getProjectTypeMessage(x, true)),
									href: `/collection/${collection.id}/${x}s`,
								}
							}),
						]"
					/>
					<button
						v-tooltip="
							formatMessage(
								commonMessages[`${cosmetics.searchDisplayMode.collection || 'list'}InputView`],
							)
						"
						:aria-label="
							formatMessage(
								commonMessages[`${cosmetics.searchDisplayMode.collection || 'list'}InputView`],
							)
						"
						class="square-button"
						@click="cycleSearchDisplayMode()"
					>
						<GridIcon v-if="cosmetics.searchDisplayMode.collection === 'grid'" />
						<ImageIcon v-else-if="cosmetics.searchDisplayMode.collection === 'gallery'" />
						<ListIcon v-else />
					</button>
				</nav>

				<div
					v-if="projects && projects?.length > 0"
					:class="
						'project-list display-mode--' + (cosmetics.searchDisplayMode.collection || 'list')
					"
				>
					<ProjectCard
						v-for="project in (route.params.projectType !== undefined
							? projects.filter(
									(x) =>
										x.project_type ===
										route.params.projectType.substr(0, route.params.projectType.length - 1),
								)
							: projects
						)
							.slice()
							.sort((a, b) => b.downloads - a.downloads)"
						:id="project.id"
						:key="project.id"
						:type="project.project_type"
						:categories="project.categories"
						:created-at="project.published"
						:updated-at="project.updated"
						:description="project.description"
						:downloads="project.downloads ? project.downloads.toString() : '0'"
						:follows="project.followers ? project.followers.toString() : '0'"
						:featured-image="project.gallery.find((element) => element.featured)?.url"
						:icon-url="project.icon_url"
						:name="project.title"
						:client-side="project.client_side"
						:server-side="project.server_side"
						:color="project.color"
						:show-updated-date="!canEdit && collection.id !== 'following'"
						:show-created-date="!canEdit && collection.id !== 'following'"
					>
						<button
							v-if="canEdit"
							class="iconified-button remove-btn"
							@click="
								() => {
									removeProjects = [project]
									saveChanges()
								}
							"
						>
							<TrashIcon aria-hidden="true" />
							{{ formatMessage(messages.removeProjectButton) }}
						</button>
						<button
							v-if="collection.id === 'following'"
							class="iconified-button"
							@click="unfollowProject(project)"
						>
							<TrashIcon aria-hidden="true" />
							{{ formatMessage(messages.unfollowProjectButton) }}
						</button>
					</ProjectCard>
				</div>
				<div v-else class="error">
					<UpToDate class="icon" />
					<br />
					<span v-if="auth.user && auth.user.id === creator.id" class="preserve-lines text">
						<IntlFormatted :message-id="messages.noProjectsAuthLabel">
							<template #create-link="{ children }">
								<a class="link" @click.prevent="$router.push('/mods')">
									<component :is="() => children" />
								</a>
							</template>
						</IntlFormatted>
					</span>
					<span v-else class="text">{{ formatMessage(messages.noProjectsLabel) }}</span>
				</div>
			</div>
		</div>
	</div>
</template>

<script setup>
import {
	BoxIcon,
	CalendarIcon,
	EditIcon,
	GlobeIcon,
	GridIcon,
	ImageIcon,
	LibraryIcon,
	LinkIcon,
	ListIcon,
	LockIcon,
	SaveIcon,
	TrashIcon,
	UpdatedIcon,
	UploadIcon,
	XIcon,
} from '@modrinth/assets'
import {
	Avatar,
	Button,
	commonMessages,
	ConfirmModal,
	DropdownSelect,
	FileInput,
	injectNotificationManager,
	PopoutMenu,
	useRelativeTime,
} from '@modrinth/ui'
import { isAdmin } from '@modrinth/utils'
import { IntlFormatted } from '@vintl/vintl/components'
import UpToDate from 'assets/images/illustrations/up_to_date.svg'

import { getProjectTypeMessage } from '@/utils/i18n-project-type'
import { normalizeChildren } from '@/utils/vue-children.ts'
import NavRow from '~/components/ui/NavRow.vue'
import ProjectCard from '~/components/ui/ProjectCard.vue'

const { addNotification } = injectNotificationManager()
const vintl = useVIntl()
const { formatMessage } = vintl
const formatRelativeTime = useRelativeTime()
const formatCompactNumber = useCompactNumber()

const messages = defineMessages({
	collectionDescription: {
		id: 'collection.description',
		defaultMessage: '{description} - View the collection {name} by {username} on Modrinth',
	},
	collectionLabel: {
		id: 'collection.label.collection',
		defaultMessage: 'Collection',
	},
	collectionTitle: {
		id: 'collection.title',
		defaultMessage: '{name} - Collection',
	},
	editIconButton: {
		id: 'collection.button.edit-icon',
		defaultMessage: 'Edit icon',
	},
	deleteIconButton: {
		id: 'collection.button.delete-icon',
		defaultMessage: 'Delete icon',
	},
	createdAtLabel: {
		id: 'collection.label.created-at',
		defaultMessage: 'Created {ago}',
	},
	collectionNotFoundError: {
		id: 'collection.error.not-found',
		defaultMessage: 'Collection not found',
	},
	curatedByLabel: {
		id: 'collection.label.curated-by',
		defaultMessage: 'Curated by',
	},
	deleteModalDescription: {
		id: 'collection.delete-modal.description',
		defaultMessage: 'This will remove this collection forever. This action cannot be undone.',
	},
	deleteModalTitle: {
		id: 'collection.delete-modal.title',
		defaultMessage: 'Are you sure you want to delete this collection?',
	},
	followingCollectionDescription: {
		id: 'collection.description.following',
		defaultMessage: "Auto-generated collection of all the projects you're following.",
	},
	noProjectsLabel: {
		id: 'collection.label.no-projects',
		defaultMessage: 'This collection has no projects!',
	},
	noProjectsAuthLabel: {
		id: 'collection.label.no-projects-auth',
		defaultMessage:
			"You don't have any projects.\nWould you like to <create-link>add one</create-link>?",
	},
	ownerLabel: {
		id: 'collection.label.owner',
		defaultMessage: 'Owner',
	},
	projectsCountLabel: {
		id: 'collection.label.projects-count',
		defaultMessage:
			'{count, plural, one {<stat>{count}</stat> project} other {<stat>{count}</stat> projects}}',
	},
	removeProjectButton: {
		id: 'collection.button.remove-project',
		defaultMessage: 'Remove project',
	},
	unfollowProjectButton: {
		id: 'collection.button.unfollow-project',
		defaultMessage: 'Unfollow project',
	},
	updatedAtLabel: {
		id: 'collection.label.updated-at',
		defaultMessage: 'Updated {ago}',
	},
})

const data = useNuxtApp()
const route = useNativeRoute()
const auth = await useAuth()
const cosmetics = useCosmetics()
const tags = useTags()

const isEditing = ref(false)

function cycleSearchDisplayMode() {
	cosmetics.value.searchDisplayMode.collection = data.$cycleValue(
		cosmetics.value.searchDisplayMode.collection,
		tags.value.projectViewModes,
	)
}

let collection, refreshCollection, creator, projects, refreshProjects

try {
	if (route.params.id === 'following') {
		collection = ref({
			id: 'following',
			icon_url: 'https://cdn.modrinth.com/follow-collection.png',
			name: formatMessage(commonMessages.followedProjectsLabel),
			description: formatMessage(messages.followingCollectionDescription),
			status: 'private',
			user: auth.value.user.id,
			created: auth.value.user.created,
			updated: auth.value.user.created,
		})
		;[{ data: projects, refresh: refreshProjects }] = await Promise.all([
			useAsyncData(
				`user/${auth.value.user.id}/follows`,
				() => useBaseFetch(`user/${auth.value.user.id}/follows`),
				{
					transform: (projects) => {
						for (const project of projects) {
							project.categories = project.categories.concat(project.loaders)
						}

						return projects
					},
				},
			),
		])
		creator = ref(auth.value.user)
		refreshCollection = async () => {}
	} else {
		const val = await useAsyncData(`collection/${route.params.id}`, () =>
			useBaseFetch(`collection/${route.params.id}`, { apiVersion: 3 }),
		)
		collection = val.data
		refreshCollection = val.refresh
		;[{ data: creator }, { data: projects, refresh: refreshProjects }] = await Promise.all([
			await useAsyncData(`user/${collection.value.user}`, () =>
				useBaseFetch(`user/${collection.value.user}`),
			),
			await useAsyncData(
				`projects?ids=${encodeURIComponent(JSON.stringify(collection.value.projects))}]`,
				() =>
					useBaseFetch(
						`projects?ids=${encodeURIComponent(JSON.stringify(collection.value.projects))}`,
					),
				{
					transform: (projects) => {
						for (const project of projects) {
							project.categories = project.categories.concat(project.loaders)
						}

						return projects
					},
				},
			),
		])
	}
} catch (err) {
	console.error(err)
	throw createError({
		fatal: true,
		statusCode: 404,
		message: formatMessage(messages.collectionNotFoundError),
	})
}

if (!collection.value) {
	throw createError({
		fatal: true,
		statusCode: 404,
		message: formatMessage(messages.collectionNotFoundError),
	})
}

const title = computed(() =>
	formatMessage(messages.collectionTitle, { name: collection.value.name }),
)

useSeoMeta({
	title,
	description: () =>
		formatMessage(messages.collectionDescription, {
			name: collection.value.name,
			description: collection.value.description,
			username: creator.value.username,
		}),
	ogTitle: title,
	ogDescription: collection.value.description,
	ogImage: collection.value.icon_url ?? 'https://cdn.modrinth.com/placeholder.png',
	robots: collection.value.status === 'listed' ? 'all' : 'noindex',
})

const canEdit = computed(
	() =>
		auth.value.user &&
		(auth.value.user.id === collection.value.user || isAdmin(auth.value.user)) &&
		collection.value.id !== 'following',
)

const projectTypes = computed(() => {
	const projectSet = new Set(
		projects.value?.map((project) => project?.project_type).filter((x) => x !== undefined) || [],
	)
	projectSet.delete('project')
	return Array.from(projectSet)
})

const icon = ref(null)
const deletedIcon = ref(false)
const previewImage = ref(null)

const name = ref(collection.value.name)
const summary = ref(collection.value.description)
const visibility = ref(collection.value.status)
const removeProjects = ref([])

async function unfollowProject(project) {
	await userFollowProject(project)
	projects.value = projects.value.filter((x) => x.id !== project.id)
}

async function saveChanges() {
	startLoading()
	try {
		if (deletedIcon.value) {
			await useBaseFetch(`collection/${collection.value.id}/icon`, {
				method: 'DELETE',
				apiVersion: 3,
			})
		} else if (icon.value) {
			const ext = icon.value?.type?.split('/').pop()
			if (!ext) throw new Error('Invalid file type')
			await useBaseFetch(`collection/${collection.value.id}/icon?ext=${ext}`, {
				method: 'PATCH',
				body: icon.value,
				apiVersion: 3,
			})
		}

		const projectsToRemove = removeProjects.value?.map((p) => p.id) ?? []
		const newProjects = projects.value
			.filter((p) => !projectsToRemove.includes(p.id))
			.map((p) => p.id)
		const newProjectIds = projectsToRemove.length > 0 ? newProjects : undefined

		await useBaseFetch(`collection/${collection.value.id}`, {
			method: 'PATCH',
			body: {
				name: name.value,
				description: summary.value || null,
				status: visibility.value,
				new_projects: newProjectIds,
			},
			apiVersion: 3,
		})

		await refreshCollection()
		await refreshProjects()

		name.value = collection.value.name
		summary.value = collection.value.description
		visibility.value = collection.value.status
		removeProjects.value = []

		isEditing.value = false
	} catch (err) {
		addNotification({
			title: formatMessage(commonMessages.errorNotificationTitle),
			text: err,
			type: 'error',
		})
	}
	await initUserCollections()
	stopLoading()
}

async function deleteCollection() {
	startLoading()
	try {
		await useBaseFetch(`collection/${collection.value.id}`, {
			method: 'DELETE',
			apiVersion: 3,
		})
		if (auth.value.user.id === collection.value.user) {
			await navigateTo('/dashboard/collections')
		} else {
			await navigateTo(`/user/${collection.value.user}/collections`)
		}
	} catch (err) {
		addNotification({
			title: formatMessage(commonMessages.errorNotificationTitle),
			text: err.data ? err.data.description : err,
			type: 'error',
		})
	}
	await initUserCollections()
	stopLoading()
}

function showPreviewImage(files) {
	const reader = new FileReader()
	icon.value = files[0]
	deletedIcon.value = false
	reader.readAsDataURL(icon.value)
	reader.onload = (event) => {
		previewImage.value = event.target.result
	}
}
</script>

<style scoped lang="scss">
.animated-dropdown {
	// Omorphia's dropdowns are harcoded in width, so we need to override that
	width: 100% !important;
}

.inputs {
	margin-bottom: 1rem;

	input {
		margin-top: 0.5rem;
		width: 100%;
	}

	textarea {
		min-height: 10rem;
	}

	label {
		margin-bottom: 0;
	}
}

.team-member {
	align-items: center;
	padding: 0.25rem 0.5rem;

	.member-info {
		overflow: hidden;
		margin: auto 0 auto 0.75rem;

		.name {
			font-weight: bold;
		}

		p {
			font-size: var(--font-size-sm);
			margin: 0.2rem 0;
		}
	}
}

.remove-btn {
	margin-top: auto;
}

.card {
	padding: var(--spacing-card-lg);

	.page-header__icon {
		margin-block: 0;
	}

	.card__overlay {
		top: var(--spacing-card-lg);
		right: var(--spacing-card-lg);
	}
}

.collection-info {
	display: grid;
	grid-template-columns: 1fr;
}

.date {
	color: var(--color-text-secondary);
	font-size: var(--font-size-nm);
	display: flex;
	align-items: center;
	margin-bottom: 0.25rem;
	cursor: default;

	.label {
		margin-right: 0.25rem;
	}

	svg {
		height: 1rem;
		margin-right: 0.25rem;
	}
}

.card-header {
	font-size: 1.125rem;
	font-weight: bold;
	color: var(--color-heading);
	margin-bottom: 0.5rem;
	width: fit-content;
}

.title {
	margin: var(--gap-md) 0 var(--spacing-card-xs) 0;
	font-size: var(--font-size-xl);
	color: var(--color-text-dark);
}

.collection-label {
	font-weight: 500;
	display: flex;
	align-items: center;
	gap: 0.25rem;
}

.collection-description {
	margin-top: var(--spacing-card-sm);
	margin-bottom: 0;
}
</style>
