<template>
	<div v-if="isLoading" class="flex min-h-[50vh] items-center justify-center">
		<SpinnerIcon class="h-12 w-12 animate-spin text-brand" />
	</div>
	<div
		v-else-if="organization"
		class="new-page sidebar"
		:class="{ 'alt-layout': cosmetics.leftContentLayout || routeHasSettings }"
	>
		<ModalCreation ref="modal_creation" :organization-id="organization.id" />
		<template v-if="routeHasSettings">
			<template v-if="canAccessSettings">
				<div class="normal-page__sidebar">
					<div
						class="bg-surface mb-4 flex flex-col rounded-xl border border-solid border-surface-4 p-4"
					>
						<div class="flex items-center gap-4">
							<Avatar size="sm" :src="organization.icon_url" />
							<div class="flex flex-col justify-center gap-1">
								<h2 class="m-0 text-base">
									<nuxt-link :to="`/organization/${organization.slug}/settings`">
										{{ organization.name }}
									</nuxt-link>
								</h2>
								<span>
									{{ formatCompactNumber(acceptedMembers?.length || 0) }}
									member<template v-if="acceptedMembers?.length !== 1">s</template>
								</span>
							</div>
						</div>
					</div>

					<NavStack
						:items="[
							{
								link: `/organization/${organization.slug}/settings`,
								label: 'Overview',
								icon: SettingsIcon,
							},
							{
								link: `/organization/${organization.slug}/settings/members`,
								label: 'Members',
								icon: UsersIcon,
							},
							{
								link: `/organization/${organization.slug}/settings/projects`,
								label: 'Projects',
								icon: BoxIcon,
							},
							{
								link: `/organization/${organization.slug}/settings/analytics`,
								label: 'Analytics',
								icon: ChartIcon,
							},
						]"
					/>
				</div>
				<div class="normal-page__content">
					<NuxtPage />
				</div>
			</template>
		</template>
		<template v-else>
			<div class="normal-page__header py-4">
				<ContentPageHeader>
					<template #icon>
						<Avatar :src="organization.icon_url" :alt="organization.name" size="96px" />
					</template>
					<template #title>
						{{ organization.name }}
					</template>
					<template #title-suffix>
						<div class="ml-1 flex items-center gap-2 font-semibold">
							<OrganizationIcon />
							Organization
						</div>
					</template>
					<template #summary>
						{{ organization.description }}
					</template>
					<template #stats>
						<div
							class="flex items-center gap-2 border-0 border-r border-solid border-divider pr-4 font-semibold"
						>
							<UsersIcon class="h-6 w-6 text-secondary" />
							{{ formatCompactNumber(acceptedMembers?.length || 0) }}
							members
						</div>
						<div
							class="flex items-center gap-2 border-0 border-r border-solid border-divider pr-4 font-semibold"
						>
							<BoxIcon class="h-6 w-6 text-secondary" />
							{{ formatCompactNumber(projects?.length || 0) }}
							projects
						</div>
						<div
							v-tooltip="formatNumber(sumDownloads)"
							class="flex items-center gap-2 font-semibold"
						>
							<DownloadIcon class="h-6 w-6 text-secondary" />
							{{ formatCompactNumber(sumDownloads) }}
							downloads
						</div>
					</template>
					<template #actions>
						<ButtonStyled v-if="auth.user && currentMember" size="large">
							<NuxtLink :to="`/organization/${organization.slug}/settings`">
								<SettingsIcon aria-hidden="true" />
								Manage
							</NuxtLink>
						</ButtonStyled>
						<ButtonStyled size="large" circular type="transparent">
							<OverflowMenu
								:options="[
									{
										id: 'manage-projects',
										action: () =>
											router.push('/organization/' + organization?.slug + '/settings/projects'),
										hoverFilledOnly: true,
										shown: !!(auth.user && currentMember),
									},
									{ divider: true, shown: !!(auth?.user && currentMember) },
									{ id: 'copy-id', action: () => copyId() },
									{ id: 'copy-permalink', action: () => copyPermalink() },
								]"
								aria-label="More options"
							>
								<MoreVerticalIcon aria-hidden="true" />
								<template #manage-projects>
									<BoxIcon aria-hidden="true" />
									Manage projects
								</template>
								<template #copy-id>
									<ClipboardCopyIcon aria-hidden="true" />
									{{ formatMessage(commonMessages.copyIdButton) }}
								</template>
								<template #copy-permalink>
									<ClipboardCopyIcon aria-hidden="true" />
									{{ formatMessage(commonMessages.copyPermalinkButton) }}
								</template>
							</OverflowMenu>
						</ButtonStyled>
					</template>
				</ContentPageHeader>
			</div>
			<div class="normal-page__sidebar">
				<AdPlaceholder v-if="!auth.user" />

				<div class="card flex-card">
					<h2>Members</h2>
					<div class="details-list">
						<template v-for="member in acceptedMembers" :key="member?.user.id">
							<nuxt-link
								class="details-list__item details-list__item--type-large"
								:to="`/user/${member?.user?.username}`"
							>
								<Avatar :src="member?.user.avatar_url" circle />
								<div class="rows">
									<span class="flex items-center gap-1">
										{{ member?.user?.username }}
										<CrownIcon
											v-if="member?.is_owner"
											v-tooltip="'Organization owner'"
											class="text-brand-orange"
										/>
									</span>
									<span class="details-list__item__text--style-secondary">
										{{ member?.role ? member.role : 'Member' }}
									</span>
								</div>
							</nuxt-link>
						</template>
					</div>
				</div>
			</div>
			<div class="normal-page__content">
				<div v-if="isInvited" class="universal-card information invited">
					<h2>Invitation to join {{ organization.name }}</h2>
					<p>You have been invited to join {{ organization.name }}.</p>
					<div class="input-group">
						<ButtonStyled color="brand">
							<button @click="onAcceptInvite">
								<CheckIcon />
								Accept
							</button>
						</ButtonStyled>
						<ButtonStyled color="red">
							<button @click="onDeclineInvite">
								<XIcon />
								Decline
							</button>
						</ButtonStyled>
					</div>
				</div>
				<div v-if="navLinks.length > 2" class="mb-4 max-w-full overflow-x-auto">
					<NavTabs :links="navLinks" replace />
				</div>
				<ProjectCardList v-if="projects && projects.length > 0">
					<template
						v-for="project in (route.params.projectType !== undefined
							? (projects ?? []).filter((x) =>
									x.project_types.includes(
										typeof route.params.projectType === 'string'
											? route.params.projectType.slice(0, route.params.projectType.length - 1)
											: route.params.projectType[0]?.slice(
													0,
													route.params.projectType[0].length - 1,
												) || '',
									),
								)
							: (projects ?? [])
						)
							.slice()
							.sort((a, b) => b.downloads - a.downloads)"
						:key="project.id"
					>
						<ProjectCard
							v-if="isProjectServer(project)"
							:link="`/server/${project.slug || project.id}`"
							:title="project.name"
							:icon-url="project.icon_url"
							:summary="project.summary"
							:tags="project.categories"
							:server-online-players="
								project.minecraft_java_server?.ping?.data?.players_online ?? 0
							"
							:server-recent-plays="project.minecraft_java_server?.verified_plays_2w ?? 0"
							:server-region="project.minecraft_server?.region"
							:server-status-online="!!project.minecraft_java_server?.ping?.data"
							:server-modpack-content="getServerModpackContent(project)"
							:status="
								auth.user && (auth.user.id! === user.id || tags.staffRoles.includes(auth.user.role))
									? (project.status as ProjectStatus)
									: undefined
							"
							:max-tags="2"
							layout="list"
							is-server-project
							exclude-loaders
						/>
						<ProjectCard
							v-else
							:link="`/${project.project_types[0] ?? 'project'}/${project.slug || project.id}`"
							:title="project.name"
							:icon-url="project.icon_url"
							:banner="project.gallery.find((element) => element.featured)?.url"
							:summary="project.summary"
							:date-updated="project.updated"
							:downloads="project.downloads"
							:followers="project.followers"
							:tags="project.categories"
							:environment="
								project.client_side && project.server_side
									? {
											clientSide: project.client_side,
											serverSide: project.server_side,
										}
									: undefined
							"
							:status="
								auth.user && (auth.user.id! === user.id || tags.staffRoles.includes(auth.user.role))
									? (project.status as ProjectStatus)
									: undefined
							"
							:color="project.color"
							layout="list"
						/>
					</template>
				</ProjectCardList>
				<div v-else-if="true" class="error">
					<UpToDate class="icon" />
					<br />
					<span class="preserve-lines text">
						This organization doesn't have any projects yet.
						<template v-if="isPermission(currentMember?.permissions, 1 << 4)">
							Would you like to
							<a class="link" @click="modal_creation?.show()">create one</a>?
						</template>
					</span>
				</div>
			</div>
		</template>
	</div>
</template>

<script setup lang="ts">
import type { Labrinth } from '@modrinth/api-client'
import {
	BoxIcon,
	ChartIcon,
	CheckIcon,
	ClipboardCopyIcon,
	CrownIcon,
	DownloadIcon,
	MoreVerticalIcon,
	OrganizationIcon,
	SettingsIcon,
	SpinnerIcon,
	UsersIcon,
	XIcon,
} from '@modrinth/assets'
import {
	Avatar,
	ButtonStyled,
	commonMessages,
	ContentPageHeader,
	injectModrinthClient,
	NavTabs,
	OverflowMenu,
	ProjectCard,
	ProjectCardList,
	useCompactNumber,
	useFormatNumber,
	useVIntl,
} from '@modrinth/ui'
import type { Organization, ProjectStatus, ProjectType } from '@modrinth/utils'
import { useQuery } from '@tanstack/vue-query'

import UpToDate from '~/assets/images/illustrations/up_to_date.svg?component'
import AdPlaceholder from '~/components/ui/AdPlaceholder.vue'
import ModalCreation from '~/components/ui/create/ProjectCreateModal.vue'
import NavStack from '~/components/ui/NavStack.vue'
import { acceptTeamInvite, removeTeamMember } from '~/helpers/teams.js'
import {
	OrganizationContext,
	provideOrganizationContext,
} from '~/providers/organization-context.ts'
import { isPermission } from '~/utils/permissions.ts'

type ProjectV3 = Labrinth.Projects.v3.Project & {
	client_side: 'required' | 'optional' | 'unsupported'
	server_side: 'required' | 'optional' | 'unsupported'
}

const vintl = useVIntl()
const { formatMessage } = vintl

const formatNumber = useFormatNumber()
const { formatCompactNumber } = useCompactNumber()

const auth: { user: any } & any = await useAuth()
const user = await useUser()
const cosmetics = useCosmetics()
const route = useNativeRoute()
const router = useRouter()
const tags = useGeneratedState()
const config = useRuntimeConfig()
const modal_creation = useTemplateRef('modal_creation')

const orgId = useRouteId()

if (route.path.includes('settings')) {
	useSeoMeta({
		robots: 'noindex',
	})
}

// hacky way to show the edit button on the corner of the card.
const routeHasSettings = computed(() => route.path.includes('settings'))

const client = injectModrinthClient()

const {
	data: organization,
	refetch: refreshOrganization,
	error: orgError,
	isPending: organizationIsPending,
} = useQuery({
	queryKey: computed(() => ['organization', orgId]),
	// @ts-expect-error
	queryFn: () => client.labrinth.organizations_v3.get(orgId),
	enabled: !!orgId,
})

watch(
	orgError,
	(error) => {
		if (error) {
			const status = (error as any).statusCode ?? (error as any).status ?? 404
			showError({
				fatal: true,
				statusCode: status,
				message: 'Organization not found',
			})
		}
	},
	{ immediate: true },
)

const {
	data: projects,
	refetch: refreshProjects,
	isFetching: projectsIsFetching,
} = useQuery({
	queryKey: computed(() => ['organization', orgId, 'projects']),
	queryFn: async () => {
		// @ts-expect-error
		const rawProjects = (await client.labrinth.organizations_v3.getProjects(orgId)) as ProjectV3[]

		return rawProjects.map((project) => {
			let categories = project.categories.concat(project.loaders)
			if (project.mrpack_loaders) {
				categories = categories.concat(project.mrpack_loaders as string[])
			}

			const singleplayer = project.singleplayer && (project.singleplayer as string[])[0]
			const clientAndServer =
				project.client_and_server && (project.client_and_server as string[])[0]
			const clientOnly = project.client_only && (project.client_only as string[])[0]
			const serverOnly = project.server_only && (project.server_only as string[])[0]

			let client_side: ProjectV3['client_side'] | undefined
			let server_side: ProjectV3['server_side'] | undefined

			// quick and dirty hack to show envs as legacy
			if (singleplayer && clientAndServer && !clientOnly && !serverOnly) {
				client_side = 'required'
				server_side = 'required'
			} else if (singleplayer && clientAndServer && clientOnly && !serverOnly) {
				client_side = 'required'
				server_side = 'unsupported'
			} else if (singleplayer && clientAndServer && !clientOnly && serverOnly) {
				client_side = 'unsupported'
				server_side = 'required'
			} else if (singleplayer && clientAndServer && clientOnly && serverOnly) {
				client_side = 'optional'
				server_side = 'optional'
			}

			return { ...project, categories, client_side, server_side }
		})
	},
	placeholderData: [],
})

const refresh = async () => {
	await Promise.all([refreshOrganization(), refreshProjects()])
}

// Loading state
const isLoading = computed(() => {
	return organizationIsPending.value || projectsIsFetching.value
})

// Filter accepted, sort by role, then by name and Owner role always goes first
const acceptedMembers = computed(() => {
	const acceptedMembers = organization.value?.members?.filter((x) => x.accepted) ?? []
	const owner = acceptedMembers.find((x) => x.is_owner)
	const rest = acceptedMembers.filter((x) => !x.is_owner) ?? []

	rest.sort((a, b) => {
		if (a.role === b.role) {
			return a.user.username.localeCompare(b.user.username)
		} else {
			return a.role.localeCompare(b.role)
		}
	})

	return owner ? [owner, ...rest] : rest
})

const isInvited = computed(() => {
	return currentMember.value?.accepted === false
})

const projectTypes = computed(() => {
	const obj: Record<string, boolean> = {}

	for (const project of projects.value ?? []) {
		obj[project.project_types[0] ?? 'project'] = true
	}

	delete obj.project

	return Object.keys(obj)
})
function isProjectServer(project: ProjectV3): boolean {
	return project.minecraft_server != null
}

function getServerModpackContent(project: ProjectV3) {
	const content = project.minecraft_java_server?.content
	if (content?.kind === 'modpack') {
		const { project_name, project_icon, project_id } = content
		if (!project_name) return undefined
		return {
			name: project_name,
			icon: project_icon,
			onclick:
				project_id !== project.id
					? () => {
							navigateTo(`/project/${project_id}`)
						}
					: undefined,
			showCustomModpackTooltip: project_id === project.id,
		}
	}
	return undefined
}

const sumDownloads = computed(() => {
	let sum = 0

	for (const project of projects.value ?? []) {
		sum += project.downloads
	}

	return sum
})

const onAcceptInvite = useClientTry(async () => {
	await acceptTeamInvite(organization.value?.team_id)
	await refreshOrganization()
})

const onDeclineInvite = useClientTry(async () => {
	await removeTeamMember(organization.value?.team_id, auth.value?.user?.id)
	await refreshOrganization()
})

const organizationContext = new OrganizationContext(
	organization as Ref<Organization | null>,
	projects as Ref<ProjectV3[] | null>,
	auth,
	tags,
	refresh,
)
const { currentMember } = organizationContext

provideOrganizationContext(organizationContext)

const canAccessSettings = computed(() => !!currentMember.value?.accepted)

watch(
	[routeHasSettings, acceptedMembers, currentMember],
	() => {
		if (routeHasSettings.value && acceptedMembers.value.length > 0 && !canAccessSettings.value) {
			showError({
				fatal: true,
				statusCode: 401,
				statusMessage: 'Unauthorized',
			})
		}
	},
	{ flush: 'sync', immediate: true },
)

watch(
	organization,
	(org) => {
		if (org) {
			const title = `${org.name} - Organization`
			const description = `${org.description} - View the organization ${org.name} on Modrinth`

			useSeoMeta({
				title,
				description,
				ogTitle: title,
				ogDescription: org.description,
				ogImage: org.icon_url ?? 'https://cdn.modrinth.com/placeholder.png',
			})
		}
	},
	{ immediate: true },
)

const navLinks = computed(() => [
	{
		label: formatMessage(commonMessages.allProjectType),
		href: `/organization/${organization.value?.slug}`,
	},
	...projectTypes.value
		.map((x) => {
			return {
				label: formatMessage(getProjectTypeMessage(x as ProjectType, true)),
				href: `/organization/${organization.value?.slug}/${x}s`,
			}
		})
		.slice()
		.sort((a, b) => a.label.localeCompare(b.label)),
])

async function copyId() {
	await navigator.clipboard.writeText(organization.value?.id ?? '')
}

async function copyPermalink() {
	await navigator.clipboard.writeText(
		`${config.public.siteUrl}/organization/${organization.value?.id}`,
	)
}
</script>

<style scoped lang="scss">
.page-header__settings {
	display: flex;
	flex-direction: row;
	gap: var(--gap-md);
	margin-bottom: var(--gap-md);

	.title-section {
		display: flex;
		flex-direction: column;
		justify-content: center;
		gap: var(--gap-xs);
	}

	.settings-title {
		margin: 0 !important;
		font-size: var(--font-size-md);
	}
}

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

.universal-card {
	h1 {
		margin-bottom: var(--gap-md);
	}
}

.creator-list {
	display: flex;
	flex-direction: column;
	padding: var(--gap-xl);

	h3 {
		margin: 0 0 var(--gap-sm);
	}

	.creator {
		display: grid;
		gap: var(--gap-xs);
		background-color: var(--color-raised-bg);
		padding: var(--gap-sm);
		margin-left: -0.5rem;
		border-radius: var(--radius-lg);
		grid-template:
			'avatar name' auto
			'avatar role' auto
			/ auto 1fr;

		p {
			margin: 0;
		}

		.name {
			grid-area: name;
			align-self: flex-end;
			margin-left: var(--gap-xs);
			font-weight: bold;

			display: flex;
			align-items: center;
			gap: 0.25rem;

			svg {
				color: var(--color-orange);
			}
		}

		.role {
			grid-area: role;
			align-self: flex-start;
			margin-left: var(--gap-xs);
		}

		.avatar {
			grid-area: avatar;
		}
	}
}

.secondary-stat {
	align-items: center;
	display: flex;
	margin-bottom: 0.8rem;
}

.secondary-stat__icon {
	height: 1rem;
	width: 1rem;
}

.secondary-stat__text {
	margin-left: 0.4rem;
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

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

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

.organization-description {
	margin-top: var(--spacing-card-sm);
	margin-bottom: 0;
}

.title-and-link {
	display: flex;
	justify-content: space-between;
	align-items: center;

	h3 {
		margin: 0;
	}

	a {
		display: flex;
		align-items: center;
		gap: var(--gap-xs);
		color: var(--color-blue);
	}
}

.project-overview {
	gap: var(--gap-md);
	padding: var(--gap-xl);

	.project-card {
		padding: 0;
		border-radius: 0;
		background-color: transparent;
		box-shadow: none;

		:deep(.title) {
			font-size: var(--font-size-nm) !important;
		}
	}
}

.popout-heading {
	padding: var(--gap-sm) var(--gap-md);
	margin: 0;
	font-size: var(--font-size-md);
	color: var(--color-text);
}

.popout-checkbox {
	padding: var(--gap-sm) var(--gap-md);
}
</style>
