<template>
	<div
		v-if="server.moduleErrors.backups"
		class="flex w-full flex-col items-center justify-center gap-4 p-4"
	>
		<div class="flex max-w-lg flex-col items-center rounded-3xl bg-bg-raised p-6 shadow-xl">
			<div class="flex flex-col items-center text-center">
				<div class="flex flex-col items-center gap-4">
					<div class="grid place-content-center rounded-full bg-bg-orange p-4">
						<IssuesIcon class="size-12 text-orange" />
					</div>
					<h1 class="m-0 mb-2 w-fit text-4xl font-bold">Failed to load backups</h1>
				</div>
				<p class="text-lg text-secondary">
					We couldn't load your server's backups. Here's what went wrong:
				</p>
				<p>
					<span class="break-all font-mono">{{
						JSON.stringify(server.moduleErrors.backups.error)
					}}</span>
				</p>
				<ButtonStyled size="large" color="brand" @click="() => server.refresh(['backups'])">
					<button class="mt-6 !w-full">Retry</button>
				</ButtonStyled>
			</div>
		</div>
	</div>
	<div v-else-if="data" class="contents">
		<BackupCreateModal ref="createBackupModal" :server="server" />
		<BackupRenameModal ref="renameBackupModal" :server="server" />
		<BackupRestoreModal ref="restoreBackupModal" :server="server" />
		<BackupDeleteModal ref="deleteBackupModal" :server="server" @delete="deleteBackup" />
		<BackupSettingsModal ref="backupSettingsModal" :server="server" />

		<div class="mb-6 flex flex-col items-center justify-between gap-4 sm:flex-row">
			<div class="flex flex-col gap-2">
				<div class="flex items-center gap-2">
					<h1 class="m-0 text-2xl font-extrabold text-contrast">Backups</h1>
					<TagItem
						v-tooltip="`${data.backup_quota - data.used_backup_quota} backup slots remaining`"
						class="cursor-help"
						:style="{
							'--_color':
								data.backup_quota <= data.used_backup_quota
									? 'var(--color-red)'
									: data.backup_quota - data.used_backup_quota <= 3
										? 'var(--color-orange)'
										: undefined,
							'--_bg-color':
								data.backup_quota <= data.used_backup_quota
									? 'var(--color-red-bg)'
									: data.backup_quota - data.used_backup_quota <= 3
										? 'var(--color-orange-bg)'
										: undefined,
						}"
					>
						{{ data.used_backup_quota }} / {{ data.backup_quota }}
					</TagItem>
				</div>
				<p class="m-0">
					You can have up to {{ data.backup_quota }} backups at once, stored securely off-site.
				</p>
			</div>
			<div
				class="grid w-full grid-cols-[repeat(auto-fit,_minmax(180px,1fr))] gap-2 sm:flex sm:w-fit sm:flex-row"
			>
				<ButtonStyled type="standard">
					<button
						v-tooltip="
							'Auto backups are currently unavailable; we apologize for the inconvenience.'
						"
						:disabled="true || server.general?.status === 'installing'"
						@click="showbackupSettingsModal"
					>
						<SettingsIcon class="h-5 w-5" />
						Auto backups
					</button>
				</ButtonStyled>
				<ButtonStyled type="standard" color="brand">
					<button
						v-tooltip="backupCreationDisabled"
						class="w-full sm:w-fit"
						:disabled="!!backupCreationDisabled"
						@click="showCreateModel"
					>
						<PlusIcon class="h-5 w-5" />
						Create backup
					</button>
				</ButtonStyled>
			</div>
		</div>

		<div class="flex w-full flex-col gap-2">
			<div
				v-if="backups.length === 0"
				class="mt-6 flex items-center justify-center gap-2 text-center text-secondary"
			>
				<template v-if="data.used_backup_quota">
					<SpinnerIcon class="animate-spin" />
					Loading backups...
				</template>
				<template v-else> You don't have any backups yet. </template>
			</div>
			<BackupItem
				v-for="backup in backups"
				:key="`backup-${backup.id}`"
				:backup="backup"
				:kyros-url="props.server.general?.node.instance"
				:jwt="props.server.general?.node.token"
				@prepare="() => prepareDownload(backup.id)"
				@download="() => triggerDownloadAnimation()"
				@rename="() => renameBackupModal?.show(backup)"
				@restore="() => restoreBackupModal?.show(backup)"
				@lock="
					() => {
						if (backup.locked) {
							unlockBackup(backup.id)
						} else {
							lockBackup(backup.id)
						}
					}
				"
				@delete="
					(skipConfirmation?: boolean) =>
						!skipConfirmation ? deleteBackup(backup) : deleteBackupModal?.show(backup)
				"
				@retry="() => retryBackup(backup.id)"
			/>
		</div>

		<div
			class="over-the-top-download-animation"
			:class="{ 'animation-hidden': !overTheTopDownloadAnimation }"
		>
			<div>
				<div
					class="animation-ring-3 flex items-center justify-center rounded-full border-4 border-solid border-brand bg-brand-highlight opacity-40"
				></div>
				<div
					class="animation-ring-2 flex items-center justify-center rounded-full border-4 border-solid border-brand bg-brand-highlight opacity-60"
				></div>
				<div
					class="animation-ring-1 flex items-center justify-center rounded-full border-4 border-solid border-brand bg-brand-highlight"
				>
					<DownloadIcon class="h-20 w-20 text-contrast" />
				</div>
			</div>
		</div>
	</div>
</template>

<script setup lang="ts">
import { DownloadIcon, IssuesIcon, PlusIcon, SettingsIcon, SpinnerIcon } from '@modrinth/assets'
import { ButtonStyled, injectNotificationManager, TagItem } from '@modrinth/ui'
import type { Backup } from '@modrinth/utils'
import { useStorage } from '@vueuse/core'
import { computed, ref } from 'vue'

import BackupCreateModal from '~/components/ui/servers/BackupCreateModal.vue'
import BackupDeleteModal from '~/components/ui/servers/BackupDeleteModal.vue'
import BackupItem from '~/components/ui/servers/BackupItem.vue'
import BackupRenameModal from '~/components/ui/servers/BackupRenameModal.vue'
import BackupRestoreModal from '~/components/ui/servers/BackupRestoreModal.vue'
import BackupSettingsModal from '~/components/ui/servers/BackupSettingsModal.vue'
import type { ModrinthServer } from '~/composables/servers/modrinth-servers.ts'

const { addNotification } = injectNotificationManager()
const props = defineProps<{
	server: ModrinthServer
	isServerRunning: boolean
}>()

const route = useNativeRoute()
const serverId = route.params.id

const userPreferences = useStorage(`pyro-server-${serverId}-preferences`, {
	backupWhileRunning: false,
})

defineEmits(['onDownload'])

const data = computed(() => props.server.general)
const backups = computed(() => {
	if (!props.server.backups?.data) return []

	return [...props.server.backups.data].sort((a, b) => {
		return new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
	})
})

useHead({
	title: `Backups - ${data.value?.name ?? 'Server'} - Modrinth`,
})

const overTheTopDownloadAnimation = ref()

const createBackupModal = ref<InstanceType<typeof BackupCreateModal>>()
const renameBackupModal = ref<InstanceType<typeof BackupRenameModal>>()
const restoreBackupModal = ref<InstanceType<typeof BackupRestoreModal>>()
const deleteBackupModal = ref<InstanceType<typeof BackupDeleteModal>>()
const backupSettingsModal = ref<InstanceType<typeof BackupSettingsModal>>()

const backupCreationDisabled = computed(() => {
	if (props.isServerRunning && !userPreferences.value.backupWhileRunning) {
		return 'Cannot create backup while server is running'
	}
	if (
		data.value?.used_backup_quota !== undefined &&
		data.value?.backup_quota !== undefined &&
		data.value?.used_backup_quota >= data.value?.backup_quota
	) {
		return `All ${data.value.backup_quota} of your backup slots are in use`
	}
	if (backups.value.some((backup) => backup.task?.create?.state === 'ongoing')) {
		return 'A backup is already in progress'
	}
	if (props.server.general?.status === 'installing') {
		return 'Cannot create backup while server is installing'
	}
	return undefined
})

const showCreateModel = () => {
	createBackupModal.value?.show()
}

const showbackupSettingsModal = () => {
	backupSettingsModal.value?.show()
}

function triggerDownloadAnimation() {
	overTheTopDownloadAnimation.value = true
	setTimeout(() => (overTheTopDownloadAnimation.value = false), 500)
}

const prepareDownload = async (backupId: string) => {
	try {
		await props.server.backups?.prepare(backupId)
	} catch (error) {
		console.error('Failed to prepare download:', error)
		addNotification({
			type: 'error',
			title: 'Failed to prepare backup for download',
			text: error as string,
		})
	}
}

const lockBackup = async (backupId: string) => {
	try {
		await props.server.backups?.lock(backupId)
		await props.server.refresh(['backups'])
	} catch (error) {
		console.error('Failed to toggle lock:', error)
	}
}

const unlockBackup = async (backupId: string) => {
	try {
		await props.server.backups?.unlock(backupId)
		await props.server.refresh(['backups'])
	} catch (error) {
		console.error('Failed to toggle lock:', error)
	}
}

const retryBackup = async (backupId: string) => {
	try {
		await props.server.backups?.retry(backupId)
		await props.server.refresh(['backups'])
	} catch (error) {
		console.error('Failed to retry backup:', error)
	}
}

async function deleteBackup(backup?: Backup) {
	if (!backup) {
		addNotification({
			type: 'error',
			title: 'Error deleting backup',
			text: 'Backup is null',
		})
		return
	}

	try {
		await props.server.backups?.delete(backup.id)
		await props.server.refresh()
	} catch (error) {
		const message = error instanceof Error ? error.message : String(error)
		addNotification({
			type: 'error',
			title: 'Error deleting backup',
			text: message,
		})
	}
}
</script>

<style scoped>
.over-the-top-download-animation {
	position: fixed;
	z-index: 100;
	inset: 0;
	display: flex;
	justify-content: center;
	align-items: center;
	pointer-events: none;
	scale: 0.5;
	transition: all 0.5s ease-out;
	opacity: 1;

	&.animation-hidden {
		scale: 0.8;
		opacity: 0;

		.animation-ring-1 {
			width: 25rem;
			height: 25rem;
		}
		.animation-ring-2 {
			width: 50rem;
			height: 50rem;
		}
		.animation-ring-3 {
			width: 100rem;
			height: 100rem;
		}
	}

	> div {
		position: relative;
		display: flex;
		justify-content: center;
		align-items: center;
		width: fit-content;
		height: fit-content;

		> * {
			position: absolute;
			scale: 1;
			transition: all 0.2s ease-out;
			width: 20rem;
			height: 20rem;
		}
	}
}
</style>
