<script setup lang="ts">
import { useNuxtApp, useFetch } from 'nuxt/app';

import buildLocaleList from '#shared/buildLocaleList.ts';
import type { TermsEntryRaw } from '#shared/classes.ts';
import { longtimeCookieSetting } from '#shared/cookieSettings.ts';
import { buildFlags } from '#shared/flags.ts';
import { sleep, PermissionAreas } from '#shared/helpers.ts';
import opinions from '#shared/opinions.ts';
import { applyProfileVisibilityRules } from '#shared/profile.ts';
import type { UserWithProfiles, Profile } from '#shared/profile.ts';
import type { Authenticator } from '#shared/user.ts';
import useConfig from '~/composables/useConfig.ts';
import useMainPronoun from '~/composables/useMainPronoun.ts';
import useSimpleHead from '~/composables/useSimpleHead.ts';
import { loadPronounLibrary } from '~/src/data.ts';
import { getUrlForLocale } from '~/src/domain.ts';

definePageMeta({
    translatedPaths: (config) => {
        if (!config.profile.enabled) {
            return [];
        }
        return ['/@:username', '/u/:username'];
    },
});

const {
    $translator: translator,
    $translateForPronoun: translateForPronoun,
    $user: visitor,
    $multiIsGranted: multiIsGranted,
    $isSafari: isSafari,
} = useNuxtApp();
const route = useRoute();
const router = useRouter();
const config = useConfig();

const pronounLibrary = await loadPronounLibrary(config);

const tokenCookie = useCookie('token', longtimeCookieSetting);
const impersonatorCookie = useCookie('impersonator', longtimeCookieSetting);

const usernameFromRoute = route.params.username as string;
const userAsyncData = useFetch<UserWithProfiles>(`/api/profile/get/${encodeURIComponent(usernameFromRoute)}?version=2&props=pronouns,flags,opinions&lprops[${config.locale}]=all`);
const profile = computed(() => {
    if (!userAsyncData.data.value) {
        return null;
    }

    for (const locale in userAsyncData.data.value.profiles) {
        if (locale === config.locale) {
            return applyProfileVisibilityRules(visitor(), userAsyncData.data.value.profiles[locale], false);
        }
    }

    return null;
});
const username = computed(() => {
    if (!userAsyncData.data.value) {
        return usernameFromRoute;
    }

    if (userAsyncData.data.value.username && userAsyncData.data.value.username !== usernameFromRoute && import.meta.client) {
        history.pushState(
            '',
            document.title,
            `/@${userAsyncData.data.value.username}`,
        );
    }

    return userAsyncData.data.value.username;
});

const { mainPronoun } = useMainPronoun(pronounLibrary, profile, translator);
const flags = buildFlags(config.locale);
useSimpleHead({
    title: username.value ? `@${username.value}` : undefined,
    description: computed(() => profile.value ? profile.value.description ?? null : null),
    banner: username.value ? `api/banner/@${username.value}.png` : undefined,
    noindex: true,
    keywords: computed(() => profile.value?.flags?.map((flagName) => flags[flagName])
        .filter((flag) => flag !== undefined)
        .map((flag) => translateForPronoun(flag.display, mainPronoun.value))),
}, translator);

await userAsyncData;

const user = userAsyncData.data;

const terms = ref<TermsEntryRaw[]>([]);
const cardMenuVisible = ref(false);
const embedMenuVisible = ref(false);
const icsMenuVisible = ref(false);

const cardsEnabled = ref(true);

const contentWarningDismissed = ref(false);

const authenticators = ref<Omit<Authenticator, 'userId'>[] | undefined>();
const showExpiredAuthenticators = ref(false);

const profileLocales = computed(() => {
    return buildLocaleList(config.locale).filter((locale) => user.value?.profiles[locale.code] !== undefined);
});
const verifiedLinks = computed(() => {
    if (!user.value) {
        return [];
    }
    return [...new Set(Object.values(user.value.profiles).map((p) => Object.keys(p.verifiedLinks || {}))
        .flat())];
});

const icsLink = computed(() => {
    return `${getUrlForLocale(config.locale)}/api/queer-calendar-${config.locale}-@${username.value}.ics`;
});

onMounted(async () => {
    if (config.terminology.enabled) {
        if (isSafari()) {
            await sleep(500); // spread out less important requests, it seems to cause issues on safari
        }
        terms.value = await $fetch('/api/terms');
    }
    if (user.value && multiIsGranted([PermissionAreas.Superuser, PermissionAreas.Community])) {
        authenticators.value = await $fetch(`/api/admin/authenticators/${user.value.id}`);
    }
});

const generateCard = async (dark: boolean) => {
    if (!profile.value) {
        return;
    }
    await $fetch(`/api/profile/request-card?dark=${dark ? '1' : '0'}`, { method: 'POST' });
    profile.value[dark ? 'cardDark' : 'card'] = '';
    startCheckingForCard();
};

const cardCheckHandle = ref<ReturnType<typeof setInterval> | undefined>();
const startCheckingForCard = () => {
    if (cardCheckHandle.value || !profile.value || profile.value.card || profile.value.cardDark) {
        return;
    }
    cardCheckHandle.value = setInterval(checkForCard, 3000);
};
watch(profile, () => {
    if (import.meta.client) {
        startCheckingForCard();
    }
});
const checkForCard = async () => {
    if (!profile.value) {
        return;
    }
    try {
        const card = await $fetch('/api/profile/has-card');
        if (card.card !== '' && card.cardDark !== '') {
            profile.value.card = card.card;
            profile.value.cardDark = card.cardDark;
            clearInterval(cardCheckHandle.value);
        }
    } catch {
        clearInterval(cardCheckHandle.value);
    }
};

const selectMainPronouns = (profile: Partial<Profile>) => {
    if (!profile.pronouns) {
        return [];
    }

    const best = [];
    for (const { value: pronoun, opinion } of profile.pronouns) {
        const opinionValue = opinions[opinion]?.value;
        if (opinionValue !== undefined) {
            if (opinionValue >= 0) {
                best.push(pronoun);
            }
        } else {
            const customOpinion = profile.opinions?.[opinion];
            if (customOpinion !== undefined &&
                customOpinion.colour !== 'grey' &&
                customOpinion.style !== 'small' &&
                !['ban', 'slash'].includes(customOpinion.icon) &&
                !(customOpinion.icon || '').endsWith('-slash')
            ) {
                best.push(pronoun);
            }
        }
    }

    return best.slice(0, 3);
};
const impersonate = async () => {
    const { token } = await $fetch(`/api/admin/impersonate/${encodeURIComponent(username.value)}`);
    impersonatorCookie.value = tokenCookie.value;
    tokenCookie.value = token;
    await router.push(`/${config.user.route}`);
    setTimeout(() => window.location.reload(), 500);
};

const authenticatorIcon = (type: string) => {
    switch (type) {
        case 'email':
        case 'changedEmail':
            return 'envelope';
        case 'indieauth':
            return 'indieauth.png';
        case 'mfa_secret':
        case 'mfa_recovery':
            return 'mobile';
        default:
            return `b:${type}`;
    }
};

const { data: latestPosts } = useAsyncData('latest-posts', async () => {
    if (!config.blog) {
        return [];
    }
    return (await $fetch('/api/blog', { query: { locale: config.locale } })).slice(0, 6);
}, {
    lazy: true,
});

const embedCode = computed(() => {
    return `<iframe src="${getUrlForLocale(config.locale)}/card/@${username.value}" style="border:0; width:100%; height:400px;" loading="lazy" referrerpolicy="no-referrer"><a href="${getUrlForLocale(config.locale)}/@${username.value}" target="_blank" rel="noopener">@${username.value}</a></iframe>`;
});
</script>

<template>
    <Page v-if="user && profile">
        <section
            v-if="$multiIsGranted([PermissionAreas.Users, PermissionAreas.Community]) &&
                user.bannedReason"
        >
            <div class="alert alert-warning">
                <p class="h4">
                    <Icon v="ban" />
                    {{ $t('ban.banned') }}
                </p>
                <p>{{ user.bannedReason }}</p>
                <p class="mb-0 small">
                    <T>ban.appeal</T>
                </p>
            </div>
        </section>

        <AdPlaceholder :phkey="['header', null]" />

        <div class="position-relative">
            <div
                v-if="profile.sensitive && profile.sensitive.length && !contentWarningDismissed"
                class="content-warning text-center"
            >
                <ContentWarning
                    :warnings="profile.sensitive"
                    @dismiss="contentWarningDismissed = true"
                />
            </div>
            <!--
            <MarkSus @hasSus="hasSus = true">
                <Profile :user="user" :profile="profile" :terms="terms" :expand-links="hasSus" />
            </MarkSus>
            -->

            <!-- <ClientOnly> -->
            <Profile :user="user" :profile="profile" :terms="terms" />
            <!-- <template #placeholder>
                    <div class="text-center" style="min-height: 400px">
                        <Spinner size="5rem" />
                    </div>
                </template>
            </ClientOnly> -->

            <div v-if="profile.sensitive && profile.sensitive.length && contentWarningDismissed" class="text-center">
                <ContentWarning
                    :warnings="profile.sensitive"
                    dismissed
                    class="small d-inline-block"
                    @blur="contentWarningDismissed = false"
                />
            </div>
        </div>

        <Separator icon="heart" />
        <section v-if="config.blog" class="row">
            <div class="col-12 col-lg-6">
                <ShopifyEmbed :col="6" />
            </div>
            <div class="col-12 col-lg-6">
                <Loading :value="latestPosts">
                    <template #header>
                        <h2 class="mb-3">
                            <Icon v="pen-nib" />
                            <T>links.blogLatest</T>
                        </h2>
                    </template>
                    <BlogEntriesList :posts="latestPosts!" :col="6" />
                </Loading>
            </div>
        </section>
        <section v-else>
            <ShopifyEmbed />
        </section>

        <CardsBanner />

        <AdPlaceholder :phkey="['content-0', 'content-mobile-0']" />

        <template #aside-left>
            <ProfileShare :user="user" class="d-none d-xxl-block" :main-pronoun="mainPronoun" />
            <AdPlaceholder :phkey="['aside-left', null]" class="d-none d-xxl-block" />
        </template>

        <template #aside-right>
            <div class="row">
                <div class="my-2 col-12 col-lg-4 col-xxl-12">
                    <div v-if="$user()?.username === user.username" class="list-group list-group-flare">
                        <div class="list-group-item pt-3">
                            <h5>
                                <Icon v="user" />
                                <T>profile.personal.header</T>
                            </h5>
                            <small><T>profile.personal.description</T></small>
                        </div>
                        <nuxt-link to="/editor" class="list-group-item list-group-item-action list-group-item-hoverable">
                            <Icon v="edit" />
                            <T>profile.edit</T>
                        </nuxt-link>
                        <template v-if="cardsEnabled">
                            <a v-if="!cardMenuVisible && !(profile.card === '' || profile.cardDark === '')" href="#" class="list-group-item list-group-item-action list-group-item-hoverable" @click.prevent="cardMenuVisible = true">
                                <Icon v="id-card" />
                                <T>profile.card.link</T>
                            </a>
                            <div v-else class="list-group-item">
                                <p class="small">
                                    <Icon v="id-card" />
                                    <T>profile.card.link</T><T>quotation.colon</T>
                                </p>
                                <small v-if="profile.card === '' || profile.cardDark === ''">
                                    <Spinner />
                                    <T>profile.card.generating</T>
                                </small>
                                <span v-else>
                                    <a
                                        v-if="profile.card"
                                        :href="profile.card"
                                        target="_blank"
                                        rel="noopener"
                                        class="btn btn-success btn-sm mx-1"
                                    >
                                        <Icon v="sun" />
                                        <T>mode.light</T>
                                    </a>
                                    <a
                                        v-if="profile.cardDark"
                                        :href="profile.cardDark"
                                        target="_blank"
                                        rel="noopener"
                                        class="btn btn-success btn-sm mx-1"
                                    >
                                        <Icon v="moon" />
                                        <T>mode.dark</T>
                                    </a>

                                    <hr v-if="profile.card || profile.cardDark">
                                    <small>
                                        <T>profile.card.generate</T><T>quotation.colon</T><br>
                                        <button
                                            type="button"
                                            class="btn btn-outline-success btn-sm"
                                            @click="generateCard(false)"
                                        >
                                            <Icon v="sun" />
                                            <T>mode.light</T>
                                        </button>
                                        <button
                                            type="button"
                                            class="btn btn-outline-success btn-sm"
                                            @click="generateCard(true)"
                                        >
                                            <Icon v="moon" />
                                            <T>mode.dark</T>
                                        </button>
                                    </small>
                                </span>
                            </div>
                        </template>
                        <a v-if="!embedMenuVisible" href="#" class="list-group-item list-group-item-action list-group-item-hoverable" @click.prevent="embedMenuVisible = true">
                            <Icon v="box-full" />
                            <T>profile.embed.header</T>
                        </a>
                        <div v-else class="list-group-item">
                            <p class="small">
                                <Icon v="box-full" />
                                <T>profile.embed.header</T><T>quotation.colon</T>
                            </p>
                            <textarea v-model="embedCode" class="form-control form-control-sm mb-2" rows="3" readonly></textarea>
                            <ClipboardLinkButton :link="embedCode" class="w-100" />
                        </div>
                        <template v-if="(profile.events?.length ?? 0) + (profile.customEvents?.length ?? 0) > 0">
                            <a
                                v-if="!icsMenuVisible"
                                href="#"
                                class="list-group-item list-group-item-action list-group-item-hoverable"
                                @click.prevent="icsMenuVisible = true"
                            >
                                <Icon v="calendar-plus" />
                                iCalendar
                            </a>
                            <div v-else class="list-group-item">
                                <p class="small">
                                    <Icon v="calendar-plus" />
                                    iCalendar
                                </p>
                                <CalendarIcs :url="icsLink" small />
                            </div>
                        </template>
                    </div>

                    <div
                        v-if="$multiIsGranted([PermissionAreas.Superuser, PermissionAreas.Community])"
                        class="list-group list-group-flare mt-3"
                    >
                        <div class="list-group-item pt-3">
                            <h5>
                                <Icon v="user-cog" />
                                <T>admin.header</T>
                            </h5>
                        </div>
                        <a
                            v-if="$isGranted(PermissionAreas.Superuser)"
                            href="#"
                            class="list-group-item list-group-item-action list-group-item-hoverable small"
                            @click.prevent="impersonate()"
                        ><Icon v="user-secret" /> Impersonate
                        </a>
                        <nuxt-link
                            v-if="$isGranted(PermissionAreas.Superuser)"
                            :to="`/admin/audit-log/${user.username}/${user.id}`"
                            class="list-group-item list-group-item-action list-group-item-hoverable small"
                        >
                            <Icon v="file-search" /> Audit log
                        </nuxt-link>
                        <div class="list-group-item pt-3">
                            <h6>
                                <Icon v="key" /> Authenticators
                            </h6>
                            <Loading :value="authenticators">
                                <p>
                                    <a
                                        v-if="!showExpiredAuthenticators"
                                        href="#"
                                        @click.prevent="showExpiredAuthenticators = true"
                                    >
                                        Include expired ones
                                    </a>
                                </p>
                                <ul>
                                    <template v-for="authenticator in authenticators">
                                        <li
                                            v-if="showExpiredAuthenticators || authenticator.validUntil === null"
                                            :class="authenticator.validUntil === null ? '' : 'small text-muted'"
                                        >
                                            <Tooltip :text="authenticator.type">
                                                <Icon :v="authenticatorIcon(authenticator.type)" />
                                            </Tooltip>
                                            {{ $datetime($ulidTime(authenticator.id)) }}
                                            <pre><code>{{ authenticator.payload }}</code></pre>
                                        </li>
                                    </template>
                                </ul>
                            </Loading>
                        </div>
                    </div>
                </div>

                <AdPlaceholder :phkey="['aside-right', null]" class="d-block d-lg-none d-xxl-block" />

                <div class="my-2 col-12 col-lg-4 col-xxl-12">
                    <div v-if="Object.keys(user.profiles).length > 1" class="list-group list-group-flare">
                        <div class="list-group-item pt-3">
                            <h5>
                                <Icon v="language" />
                                <T>profile.language.header</T>
                            </h5>
                            <small>
                                <T :params="{ username: user.username }">profile.language.description</T><T>quotation.colon</T>
                            </small>
                        </div>
                        <LocaleLink
                            v-for="(options, locale) in $locales"
                            v-show="user.profiles[locale] !== undefined"
                            :key="locale"
                            :locale="locale"
                            :link="`/@${user.username}`"
                            :class="['list-group-item list-group-item-action list-group-item-hoverable small', locale === config.locale ? 'list-group-item-active' : '']"
                        >
                            {{ options.name }}
                            <small v-if="options.extra" class="text-muted">({{ options.extra }})</small>
                        </LocaleLink>
                    </div>
                </div>

                <div class="my-2 col-12 col-lg-4 col-xxl-12">
                    <ProfileShare :user="user" class="d-xxl-none" :main-pronoun="mainPronoun" />
                </div>
            </div>
        </template>

        <template #below>
            <Ban :user="user" :profile="profile" />
            <div class="text-center my-4 small">
                <AccessibilitySettings />
            </div>
        </template>
    </Page>
    <Page v-else-if="user?.username">
        <div class="my-md-5 pt-md-2">
            <h2 class="text-nowrap mb-3">
                <Avatar :user="user" />
                @{{ username }}
            </h2>

            <div v-if="Object.values(profileLocales).length" class="list-group">
                <LocaleLink
                    v-for="locale in profileLocales"
                    :key="locale.code"
                    :locale="locale.code"
                    :link="`/@${username}`"
                    class="list-group-item list-group-item-action list-group-item-hoverable d-flex flex-column flex-md-row justify-content-between"
                >
                    <div class="h3">
                        {{ locale.name }}
                        <small v-if="locale.extra" class="text-muted">({{ locale.extra }})</small>
                    </div>
                    <div class="d-flex align-items-center">
                        <ForeignPronoun
                            v-for="pronoun in selectMainPronouns(user.profiles[locale.code])"
                            :key="pronoun"
                            :pronoun="pronoun"
                            :locale="locale.code"
                        />
                    </div>
                </LocaleLink>
            </div>
            <div v-else class="alert alert-info">
                <p class="mb-0">
                    <Icon v="info-circle" />
                    <T>profile.empty</T>
                </p>
            </div>

            <a v-for="link in verifiedLinks" :href="link" rel="me">&nbsp;</a>

            <Ban :user="user" />
        </div>

        <template #aside-right>
            <ProfileShare v-if="Object.keys(user.profiles).length" :user="user" show-qr-start class="aside-home" :main-pronoun="mainPronoun" />
        </template>
    </Page>
    <Page v-else>
        <NotFound />
    </Page>
</template>

<style lang="scss" scoped>
    @import "assets/variables";

    $aside-margin: 2 * $spacer;
    @include media-breakpoint-up('xxl') {
        aside {
            position: absolute;
            top: 0;
            left: calc(100% + #{$aside-margin});
            width: min(300px, calc((100vw - #{$container-width}) / 2 - #{$aside-margin}));
        }

        .aside-home {
            margin-top: 164px;
        }
    }

    .list-group-flare > :first-child {
        border-top: 3px solid $primary;
    }

    .content-warning {
        position: absolute;
        left: -2*$glass-blur;
        top: -2*$glass-blur;
        width: calc(100% + 4*#{$glass-blur});
        height: calc(100% + 4*#{$glass-blur});
        background: $gray-400;
        z-index: 30;
        .alert {
            margin: 3*$spacer auto;
            display: inline-block;
        }
    }
</style>
