<template>
	<ModalWrapper ref="modal" header="Creating an instance">
		<div class="modal-header">
			<Chips v-model="creationType" :items="['custom', 'from file', 'import from launcher']" />
		</div>
		<hr class="card-divider" />
		<div v-if="creationType === 'custom'" class="modal-body">
			<div class="image-upload">
				<Avatar :src="display_icon" size="md" :rounded="true" />
				<div class="image-input">
					<Button @click="upload_icon()">
						<UploadIcon />
						Select icon
					</Button>
					<Button :disabled="!display_icon" @click="reset_icon">
						<XIcon />
						Remove icon
					</Button>
				</div>
			</div>
			<div class="input-row">
				<p class="input-label">Name</p>
				<input
					v-model="profile_name"
					autocomplete="off"
					class="text-input"
					type="text"
					maxlength="100"
				/>
			</div>
			<div class="input-row">
				<p class="input-label">Loader</p>
				<Chips v-model="loader" :items="loaders" />
			</div>
			<div class="input-row">
				<p class="input-label">Game version</p>
				<div class="flex gap-4 items-center">
					<multiselect
						v-model="game_version"
						class="selector"
						:options="game_versions"
						:multiple="false"
						:searchable="true"
						placeholder="Select game version"
						open-direction="top"
						:show-labels="false"
					/>
					<Checkbox v-model="showSnapshots" class="shrink-0" label="Show all versions" />
				</div>
			</div>
			<div v-if="loader !== 'vanilla'" class="input-row">
				<p class="input-label">Loader version</p>
				<Chips v-model="loader_version" :items="['stable', 'latest', 'other']" />
			</div>
			<div v-if="loader_version === 'other' && loader !== 'vanilla'">
				<div v-if="game_version" class="input-row">
					<p class="input-label">Select version</p>
					<multiselect
						v-model="specified_loader_version"
						class="selector"
						:options="selectable_versions"
						:searchable="true"
						placeholder="Select loader version"
						open-direction="top"
						:show-labels="false"
					/>
				</div>
				<div v-else class="input-row">
					<p class="warning">Select a game version before you select a loader version</p>
				</div>
			</div>
			<div class="input-group push-right">
				<Button @click="hide()">
					<XIcon />
					Cancel
				</Button>
				<Button color="primary" :disabled="!check_valid || creating" @click="create_instance()">
					<PlusIcon v-if="!creating" />
					{{ creating ? 'Creating...' : 'Create' }}
				</Button>
			</div>
		</div>
		<div v-else-if="creationType === 'from file'" class="modal-body">
			<Button @click="openFile"> <FolderOpenIcon /> Import from file </Button>
			<div class="info"><InfoIcon /> Or drag and drop your .mrpack file</div>
		</div>
		<div v-else class="modal-body">
			<Chips
				v-model="selectedProfileType"
				:items="profileOptions"
				:format-label="(profile) => profile?.name"
			/>
			<div class="path-selection">
				<h3>{{ selectedProfileType.name }} path</h3>
				<div class="path-input">
					<div class="iconified-input">
						<FolderOpenIcon />
						<input
							v-model="selectedProfileType.path"
							type="text"
							placeholder="Path to launcher"
							@change="setPath"
						/>
						<Button class="r-btn" @click="() => (selectedProfileType.path = '')">
							<XIcon />
						</Button>
					</div>
					<Button icon-only @click="selectLauncherPath">
						<FolderSearchIcon />
					</Button>
					<Button icon-only @click="reload">
						<UpdatedIcon />
					</Button>
				</div>
			</div>
			<div class="table">
				<div class="table-head table-row">
					<div class="toggle-all table-cell">
						<Checkbox
							class="select-checkbox"
							:model-value="
								profiles.get(selectedProfileType.name)?.every((child) => child.selected)
							"
							@update:model-value="
								(newValue) =>
									profiles
										.get(selectedProfileType.name)
										?.forEach((child) => (child.selected = newValue))
							"
						/>
					</div>
					<div class="name-cell table-cell">Profile name</div>
				</div>
				<div
					v-if="
						profiles.get(selectedProfileType.name) &&
						profiles.get(selectedProfileType.name).length > 0
					"
					class="table-content"
				>
					<div
						v-for="(profile, index) in profiles.get(selectedProfileType.name)"
						:key="index"
						class="table-row"
					>
						<div class="checkbox-cell table-cell">
							<Checkbox v-model="profile.selected" class="select-checkbox" />
						</div>
						<div class="name-cell table-cell">
							{{ profile.name }}
						</div>
					</div>
				</div>
				<div v-else class="table-content empty">No profiles found</div>
			</div>
			<div class="button-row">
				<Button
        		  v-if="selectedProfileType.name === 'Curseforge'"
        		  @click="showCurseForgeProfileModal"
        		  :disabled="loading"
        		>
        		  <CodeIcon />
        		  Import from Profile Code
        		</Button>
				<Button
					:disabled="
						loading ||
						!Array.from(profiles.values())
							.flatMap((e) => e)
							.some((e) => e.selected)
					"
					color="primary"
					@click="next"
				>
					{{
						loading
							? 'Importing...'
							: Array.from(profiles.values())
										.flatMap((e) => e)
										.some((e) => e.selected)
								? `Import ${
										Array.from(profiles.values())
											.flatMap((e) => e)
											.filter((e) => e.selected).length
									} profiles`
								: 'Select profiles to import'
					}}
				</Button>
				<ProgressBar
					v-if="loading"
					:progress="(importedProfiles / (totalProfiles + 0.0001)) * 100"
				/>
			</div>
		</div>
	</ModalWrapper>
  	<CurseForgeProfileImportModal ref="curseforgeProfileModal" :close-parent="hide" />
</template>

<script setup>
import {
	FolderOpenIcon,
	FolderSearchIcon,
	InfoIcon,
	PlusIcon,
	UpdatedIcon,
	UploadIcon,
	XIcon,
} from '@modrinth/assets'
import { Avatar, Button, Checkbox, Chips, injectNotificationManager } from '@modrinth/ui'
import { convertFileSrc } from '@tauri-apps/api/core'
import { getCurrentWebview } from '@tauri-apps/api/webview'
import { open } from '@tauri-apps/plugin-dialog'
import { computed, onUnmounted, ref, shallowRef } from 'vue'
import Multiselect from 'vue-multiselect'

import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
import ProgressBar from '@/components/ui/ProgressBar.vue'
import { trackEvent } from '@/helpers/analytics'
import {
	get_default_launcher_path,
	get_importable_instances,
	import_instance,
} from '@/helpers/import.js'
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
import { create_profile_and_install_from_file } from '@/helpers/pack.js'
import { create } from '@/helpers/profile'
import { get_loaders } from '@/helpers/tags'

import CurseForgeProfileImportModal from '@/components/ui/CurseForgeProfileImportModal.vue'

const { handleError } = injectNotificationManager()

const profile_name = ref('')
const game_version = ref('')
const loader = ref('vanilla')
const loader_version = ref('stable')
const specified_loader_version = ref('')
const icon = ref(null)
const display_icon = ref(null)
const creating = ref(false)
const showSnapshots = ref(false)
const creationType = ref('custom')
const isShowing = ref(false)

defineExpose({
	show: async () => {
		game_version.value = ''
		specified_loader_version.value = ''
		profile_name.value = ''
		creating.value = false
		showSnapshots.value = false
		loader.value = 'vanilla'
		loader_version.value = 'stable'
		icon.value = null
		display_icon.value = null
		isShowing.value = true
		modal.value.show()

		unlistener.value = await getCurrentWebview().onDragDropEvent(async (event) => {
			// Only if modal is showing
			if (!isShowing.value) return
			if (event.payload.type !== 'drop') return
			if (creationType.value !== 'from file') return
			hide()
			const { paths } = event.payload
			if (paths && paths.length > 0 && paths[0].endsWith('.mrpack')) {
				await create_profile_and_install_from_file(paths[0]).catch(handleError)
				trackEvent('InstanceCreate', {
					source: 'CreationModalFileDrop',
				})
			}
		})

		trackEvent('InstanceCreateStart', { source: 'CreationModal' })
	},
})

const unlistener = ref(null)
const hide = () => {
	isShowing.value = false
	modal.value.hide()
	if (unlistener.value) {
		unlistener.value()
		unlistener.value = null
	}
}

const showCurseForgeProfileModal = () => {
  curseforgeProfileModal.value?.show()
}

onUnmounted(() => {
	if (unlistener.value) {
		unlistener.value()
		unlistener.value = null
	}
})

const [
	fabric_versions,
	forge_versions,
	quilt_versions,
	neoforge_versions,
	all_game_versions,
	loaders,
] = await Promise.all([
	get_loader_versions('fabric').then(shallowRef).catch(handleError),
	get_loader_versions('forge').then(shallowRef).catch(handleError),
	get_loader_versions('quilt').then(shallowRef).catch(handleError),
	get_loader_versions('neo').then(shallowRef).catch(handleError),
	get_game_versions().then(shallowRef).catch(handleError),
	get_loaders()
		.then((value) =>
			ref(
				value
					.filter((item) => item.supported_project_types.includes('modpack'))
					.map((item) => item.name.toLowerCase()),
			),
		)
		.catch((err) => {
			handleError(err)
			return ref([])
		}),
])
loaders.value.unshift('vanilla')

const game_versions = computed(() => {
	return all_game_versions.value.versions
		.filter((item) => {
			let defaultVal = item.type === 'release' || showSnapshots.value
			if (loader.value === 'fabric') {
				defaultVal &= fabric_versions.value.gameVersions.some((x) => item.id === x.id)
			} else if (loader.value === 'forge') {
				defaultVal &= forge_versions.value.gameVersions.some((x) => item.id === x.id)
			} else if (loader.value === 'quilt') {
				defaultVal &= quilt_versions.value.gameVersions.some((x) => item.id === x.id)
			} else if (loader.value === 'neoforge') {
				defaultVal &= neoforge_versions.value.gameVersions.some((x) => item.id === x.id)
			}

			return defaultVal
		})
		.map((item) => item.id)
})

const modal = ref(null)
const curseforgeProfileModal = ref(null)

const check_valid = computed(() => {
	return (
		profile_name.value.trim() &&
		game_version.value &&
		game_versions.value.includes(game_version.value)
	)
})

const create_instance = async () => {
	creating.value = true
	const loader_version_value =
		loader_version.value === 'other' ? specified_loader_version.value : loader_version.value
	const loaderVersion = loader.value === 'vanilla' ? null : (loader_version_value ?? 'stable')

	hide()
	creating.value = false

	await create(
		profile_name.value,
		game_version.value,
		loader.value,
		loader.value === 'vanilla' ? null : (loader_version_value ?? 'stable'),
		icon.value,
	).catch(handleError)

	trackEvent('InstanceCreate', {
		profile_name: profile_name.value,
		game_version: game_version.value,
		loader: loader.value,
		loader_version: loaderVersion,
		has_icon: !!icon.value,
		source: 'CreationModal',
	})
}

const upload_icon = async () => {
	const res = await open({
		multiple: false,
		filters: [
			{
				name: 'Image',
				extensions: ['png', 'jpeg', 'svg', 'webp', 'gif', 'jpg'],
			},
		],
	})

	icon.value = res.path ?? res

	if (!icon.value) return
	display_icon.value = convertFileSrc(icon.value)
}

const reset_icon = () => {
	icon.value = null
	display_icon.value = null
}

const selectable_versions = computed(() => {
	if (game_version.value) {
		if (loader.value === 'fabric') {
			return fabric_versions.value.gameVersions[0].loaders.map((item) => item.id)
		} else if (loader.value === 'forge') {
			return forge_versions.value.gameVersions
				.find((item) => item.id === game_version.value)
				.loaders.map((item) => item.id)
		} else if (loader.value === 'quilt') {
			return quilt_versions.value.gameVersions[0].loaders.map((item) => item.id)
		} else if (loader.value === 'neoforge') {
			return neoforge_versions.value.gameVersions
				.find((item) => item.id === game_version.value)
				.loaders.map((item) => item.id)
		}
	}
	return []
})

const openFile = async () => {
	const newProject = await open({ multiple: false })
	if (!newProject) return
	hide()
	await create_profile_and_install_from_file(newProject.path ?? newProject).catch(handleError)

	trackEvent('InstanceCreate', {
		source: 'CreationModalFileOpen',
	})
}

const profiles = ref(
	new Map([
		['MultiMC', []],
		['GDLauncher', []],
		['ATLauncher', []],
		['Curseforge', []],
		['PrismLauncher', []],
	]),
)

const loading = ref(false)
const importedProfiles = ref(0)
const totalProfiles = ref(0)

const selectedProfileType = ref('MultiMC')
const profileOptions = ref([
	{ name: 'MultiMC', path: '' },
	{ name: 'GDLauncher', path: '' },
	{ name: 'ATLauncher', path: '' },
	{ name: 'Curseforge', path: '' },
	{ name: 'PrismLauncher', path: '' },
])

// Attempt to get import profiles on default paths
const promises = profileOptions.value.map(async (option) => {
	const path = await get_default_launcher_path(option.name).catch(handleError)
	if (!path || path === '') return

	// Try catch to allow failure and simply ignore default path attempt
	try {
		const instances = await get_importable_instances(option.name, path)

		if (!instances) return
		profileOptions.value.find((profile) => profile.name === option.name).path = path
		profiles.value.set(
			option.name,
			instances.map((name) => ({ name, selected: false })),
		)
	} catch {
		// Allow failure silently
	}
})
await Promise.all(promises)

const selectLauncherPath = async () => {
	selectedProfileType.value.path = await open({ multiple: false, directory: true })

	if (selectedProfileType.value.path) {
		await reload()
	}
}

const reload = async () => {
	const instances = await get_importable_instances(
		selectedProfileType.value.name,
		selectedProfileType.value.path,
	).catch(handleError)
	if (instances) {
		profiles.value.set(
			selectedProfileType.value.name,
			instances.map((name) => ({ name, selected: false })),
		)
	} else {
		profiles.value.set(selectedProfileType.value.name, [])
	}
}

const setPath = () => {
	profileOptions.value.find((profile) => profile.name === selectedProfileType.value.name).path =
		selectedProfileType.value.path
}

const next = async () => {
	importedProfiles.value = 0
	totalProfiles.value = Array.from(profiles.value.values())
		.map((profiles) => profiles.filter((profile) => profile.selected).length)
		.reduce((a, b) => a + b, 0)
	loading.value = true
	for (const launcher of Array.from(profiles.value.entries()).map(([launcher, profiles]) => ({
		launcher,
		path: profileOptions.value.find((option) => option.name === launcher).path,
		profiles,
	}))) {
		for (const profile of launcher.profiles.filter((profile) => profile.selected)) {
			await import_instance(launcher.launcher, launcher.path, profile.name)
				.catch(handleError)
				.then(() => console.log(`Successfully Imported ${profile.name} from ${launcher.launcher}`))
			profile.selected = false
			importedProfiles.value++
		}
	}
	loading.value = false
}
</script>

<style lang="scss" scoped>
.modal-body {
	display: flex;
	flex-direction: column;
	gap: var(--gap-md);
	margin-top: var(--gap-lg);
}

.input-label {
	font-size: 1rem;
	font-weight: bolder;
	color: var(--color-contrast);
	margin-bottom: 0.5rem;
}

.text-input {
	width: 20rem;
}

.image-upload {
	display: flex;
	gap: 1rem;
}

.image-input {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	justify-content: center;
}

.warning {
	font-style: italic;
}

:deep(button.checkbox) {
	border: none;
}

.selector {
	max-width: 20rem;
}

.labeled-divider {
	text-align: center;
}

.labeled-divider:after {
	background-color: var(--color-raised-bg);
	content: 'Or';
	color: var(--color-base);
	padding: var(--gap-sm);
	position: relative;
	top: -0.5rem;
}

.info {
	display: flex;
	flex-direction: row;
	gap: 0.5rem;
	align-items: center;
}

.modal-header {
	display: flex;
	flex-direction: row;
	justify-content: space-between;
	align-items: center;
	padding-bottom: 0;
}

.path-selection {
	padding: var(--gap-xl);
	background-color: var(--color-bg);
	border-radius: var(--radius-lg);
	display: flex;
	flex-direction: column;
	gap: var(--gap-md);

	h3 {
		margin: 0;
	}

	.path-input {
		display: flex;
		align-items: center;
		width: 100%;
		flex-direction: row;
		gap: var(--gap-sm);

		.iconified-input {
			flex-grow: 1;
			:deep(input) {
				width: 100%;
				flex-basis: auto;
			}
		}
	}
}

.table {
	border: 1px solid var(--color-bg);
}

.table-row {
	grid-template-columns: min-content auto;
}

.table-content {
	max-height: calc(5 * (18px + 2rem));
	height: calc(5 * (18px + 2rem));
	overflow-y: auto;
}

.select-checkbox {
	button.checkbox {
		border: none;
	}
}

.button-row {
	display: flex;
	flex-direction: row;
	justify-content: space-between;
	align-items: center;
	gap: var(--gap-md);

	.transparent {
		padding: var(--gap-sm) 0;
	}
}

.empty {
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 1.5rem;
	font-weight: bolder;
	color: var(--color-contrast);
}

.card-divider {
	margin: var(--gap-md) var(--gap-lg) 0 var(--gap-lg);
}
</style>
