<template>
    <section v-if="logoutInProgress || !user">
        <p class="text-center">
            <Spinner size="5rem" />
        </p>
        <div v-if="!impersonationActive">
            <iframe
                v-for="domain in universalDomains"
                :src="`${domain}/api/user/logout-universal`"
                style="width: 1px; height: 1px; opacity: .01"
            >
            </iframe>
        </div>
    </section>
    <section v-else>
        <div v-if="showTermsUpdate" class="alert alert-info container my-4 small">
            <h4 class="mb-3">
                <Icon v="info-circle" />
                <T>terms.update.header</T>
            </h4>
            <p>
                <T>terms.update.intro</T>
            </p>
            <ul>
                <li v-for="change in $t('terms.update.changes')">
                    <LinkedText :text="change" />
                </li>
            </ul>
            <p class="text-center">
                <button class="btn btn-primary" @click.prevent="dismissTermsUpdate">
                    <Icon v="shield-check" />
                    <T>confirm.ok</T>
                </button>
            </p>
        </div>

        <TabsNav
            :tabs="['general', 'cards', 'socials', 'circles', 'backup']"
            pills
            showheaders
            navclass="mb-3 border-bottom-0"
            anchors
        >
            <template #general-header>
                <Icon v="user" />
                <T>user.headerLong</T>
            </template>
            <template #general>
                <div class="card mb-3">
                    <div class="card-body row">
                        <div class="col-12 col-md-3 text-center">
                            <p v-if="$isGranted('panel') || $isGranted('users') || $isGranted('community')">
                                <nuxt-link to="/admin" class="badge bg-primary text-white">
                                    <Icon v="collective-logo.svg" class="inverted" />
                                    <T>contact.team.member</T>
                                </nuxt-link>
                            </p>
                            <p class="mb-0">
                                <Avatar :user="user" validate />
                            </p>
                            <div>
                                <p class="mt-3 mb-1">
                                    <strong><T>user.avatar.change</T><T>quotation.colon</T></strong>
                                </p>
                                <div v-if="user.avatarSource === 'gravatar'" class="mt-3">
                                    <a href="https://gravatar.com" target="_blank" rel="noopener" class="small">
                                        <Icon v="external-link" />
                                        Gravatar
                                    </a>
                                </div>
                                <div v-else class="mt-3">
                                    Gravatar:
                                    <a href="#" @click.prevent="setAvatar('gravatar')">
                                        <Avatar :user="user" :src="gravatar(user)" dsize="2rem" />
                                    </a>
                                </div>
                                <div v-if="user.avatarSource">
                                    <a href="#" class="small" @click.prevent="setAvatar(null)">
                                        <Icon v="trash" />
                                        <T>crud.remove</T>
                                    </a>
                                </div>
                                <ImageUploader small sizes="avatar" @uploaded="uploaded" />
                                <p class="small my-2 avatar-social-hint">
                                    <T>user.avatar.social</T>
                                </p>
                            </div>
                        </div>
                        <div class="col-12 col-md-9">
                            <Alert type="danger" :message="error" />

                            <div v-if="message" class="alert alert-success">
                                <p class="mb-0 narrow-message">
                                    <Icon :v="messageIcon" />
                                    <T :params="messageParams">{{ message }}</T>
                                </p>
                            </div>

                            <form :inert="savingUsername" @submit.prevent="changeUsername">
                                <h3 class="h6">
                                    <T>user.account.changeUsername.header</T>
                                </h3>
                                <input
                                    v-model="username"
                                    type="text"
                                    class="form-control mb-3"
                                    required
                                    minlength="4"
                                    maxlength="16"
                                >
                                <p v-if="usernameError" class="small text-danger">
                                    <Icon v="exclamation-triangle" />
                                    <span class="ml-1">{{ usernameError }}</span>
                                </p>
                                <div class="d-none d-md-block mt-3">
                                    <button class="btn btn-outline-primary" :disabled="username === user.username">
                                        <T>user.account.changeUsername.action</T>
                                    </button>
                                </div>
                                <div class="d-block-force d-md-none mt-3">
                                    <button class="btn btn-outline-primary w-100" :disabled="username === user.username">
                                        <T>user.account.changeUsername.action</T>
                                    </button>
                                </div>
                            </form>

                            <hr>

                            <Alert v-if="$user() && $user()!.email.endsWith('.oauth')" type="warning" message="user.emailMissing" />

                            <form :inert="savingEmail" @submit.prevent="changeEmail">
                                <h3 class="h6">
                                    <T>user.account.changeEmail.header</T>
                                </h3>
                                <div v-if="!changeEmailAuthId" class="">
                                    <input v-model="email" type="email" class="form-control mb-3" required>
                                    <div class="d-flex flex-column flex-md-row">
                                        <Captcha v-if="showCaptcha" v-model="captchaToken" />
                                        <div :class="['d-none', 'd-md-block', showCaptcha ? 'ms-3' : '']">
                                            <button class="btn btn-outline-primary" :disabled="!canChangeEmail">
                                                <T>user.account.changeEmail.action</T>
                                            </button>
                                        </div>
                                        <div class="d-block-force d-md-none mt-3">
                                            <button class="btn btn-outline-primary w-100" :disabled="!canChangeEmail">
                                                <T>user.account.changeEmail.action</T>
                                            </button>
                                        </div>
                                    </div>
                                </div>
                                <div v-else class="input-group mb-3">
                                    <input
                                        ref="code"
                                        v-model="code"
                                        type="text"
                                        class="form-control text-center"
                                        placeholder="000000"
                                        autofocus
                                        required
                                        minlength="0"
                                        maxlength="6"
                                        inputmode="numeric"
                                        pattern="[0-9]{6}"
                                        autocomplete="one-time-code"
                                    >
                                    <button class="btn btn-outline-primary">
                                        <Icon v="key" />
                                        <T>user.code.action</T>
                                    </button>
                                </div>
                            </form>
                        </div>
                    </div>
                </div>

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

                <section class="mt-5">
                    <a href="#" class="btn btn-outline-danger" @click.prevent="logout">
                        <Icon v="sign-out" />
                        <T :params="{ username: user.username }">user.logout</T>
                    </a>

                    <a
                        v-if="accounts && Object.keys(accounts).length > 1"
                        href="#"
                        class="btn btn-outline-danger"
                        @click.prevent="logoutAll"
                    >
                        <Icon v="sign-out" />
                        <T>user.logoutAll</T>
                    </a>

                    <a v-if="user.username !== 'example'" href="#" class="btn btn-outline-danger" @click.prevent="deleteAccount">
                        <Icon v="trash-alt" />
                        <T :params="{ username: user.username }">user.deleteAccount</T>
                    </a>

                    <a v-if="impersonationActive" href="#" class="btn btn-outline-primary" @click.prevent="stopImpersonation">
                        <Icon v="user-secret" />
                        Stop impersonation
                    </a>
                </section>
            </template>

            <template #cards-header>
                <Icon v="id-card" />
                <T>profile.list</T>
                <span v-if="profiles !== undefined" class="badge text-bg-light">
                    {{ Object.keys(profiles).length }}
                </span>
            </template>
            <template #cards>
                <Loading :value="profiles">
                    <ul v-if="profiles !== undefined" class="list-group">
                        <li
                            v-for="locale in Object.keys($locales)"
                            :key="locale"
                            :class="['list-group-item', locale === config.locale ? 'profile-current' : '']"
                        >
                            <ProfileOverview :username="username" :profile="profiles[locale]" :locale="locale" @update="setProfiles" />
                        </li>
                    </ul>
                </Loading>

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

            <template #socials-header>
                <Icon v="sign-in-alt" />
                <T>user.socialConnection.header</T>
                <template v-if="authMethodsCount !== null">
                    <span v-if="authMethodsCount < 2" class="badge text-bg-warning">
                        <Icon v="exclamation-triangle" />
                    </span>
                    <span v-else class="badge text-bg-light">
                        {{ authMethodsCount }}
                    </span>
                </template>
            </template>
            <template #socials>
                <Loading :value="socialConnections">
                    <template #header>
                        <div v-if="authMethodsCount !== null && authMethodsCount < 2" class="alert alert-warning">
                            <Icon v="exclamation-triangle" />
                            <T>user.tooFewAuthMethods</T>
                        </div>
                        <div class="form-check form-switch my-2">
                            <label>
                                <input v-model="socialLookup" class="form-check-input" type="checkbox" role="switch">
                                <T>user.socialLookup</T>
                                <br>
                                <small><T>user.socialLookupWhy</T></small>
                            </label>
                        </div>
                    </template>
                    <ul v-if="socialConnections !== null" class="list-group">
                        <li :class="['list-group-item', hasEmail ? 'profile-current' : '']">
                            <div class="d-md-flex justify-content-between align-items-center">
                                <h4 class="my-md-0 d-flex align-items-center gap-2">
                                    <Icon v="envelope" />
                                    <T>user.account.changeEmail.header</T>
                                </h4>

                                <span v-if="hasEmail">
                                    <span class="me-2">
                                        {{ user.email }}
                                    </span>
                                    <a
                                        href="#general"
                                        class="btn btn-light border"
                                    >
                                        <Icon v="edit" />
                                        <T>user.account.changeEmail.action</T>
                                    </a>
                                </span>
                                <span v-else>
                                    <a
                                        href="#general"
                                        class="btn btn-light border border-warning"
                                    >
                                        <Icon v="mailbox" />
                                        <T>user.account.changeEmail.confirm</T>
                                    </a>
                                </span>
                            </div>
                        </li>
                        <li v-for="(providerOptions, provider) in socialProviders" :key="provider" :class="['list-group-item', socialConnections[provider] !== undefined ? 'profile-current' : '']">
                            <SocialConnection
                                :provider="provider"
                                :provider-options="providerOptions"
                                :connection="socialConnections[provider]"
                                @disconnected="socialConnections[provider] = undefined"
                                @set-avatar="setAvatar"
                            />
                        </li>
                        <li :class="['list-group-item', user.mfa ? 'profile-current' : '']">
                            <MfaConnection />
                        </li>
                    </ul>
                </Loading>
            </template>

            <template #circles-header>
                <Icon v="heart-circle" />
                <T>profile.circles.header</T>
            </template>
            <template #circles>
                <h5><T>profile.circles.yourMentions.header</T><T>quotation.colon</T></h5>
                <CircleMentions />
                <nuxt-link to="/editor#circle" class="btn btn-primary">
                    <Icon v="user-plus" />
                    <T>profile.circles.add</T>
                </nuxt-link>
            </template>

            <template #backup-header>
                <Icon v="copy" />
                <T>profile.backup.headerShort</T>
            </template>
            <template #backup>
                <CardsBackup v-if="!user.bannedReason" />
            </template>
        </TabsNav>

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

        <div>
            <iframe
                v-for="domain in universalDomains"
                :src="`${domain}/api/user/init-universal/${tokenCookie}`"
                style="width: 1px; height: 1px; opacity: .01"
            >
            </iframe>
        </div>
    </section>
</template>

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

import useConfig from '../composables/useConfig.ts';
import useDialogue from '../composables/useDialogue.ts';
import { longtimeCookieSetting } from '../src/cookieSettings.ts';
import { newDate, gravatar } from '../src/helpers.ts';
import { socialProviders } from '../src/socialProviders.ts';
import { usernameRegex } from '../src/username.ts';
import { useMainStore } from '../store/index.ts';

import type { Profile } from '~/src/profile.ts';

export default defineComponent({
    async setup() {
        const dialogue = useDialogue();
        const { accounts, user } = storeToRefs(useMainStore());

        if (user.value === null) {
            throw 'no user';
        }

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

        const { data: profilesData } = useFetch(`/api/profile/get/${user.value.username}?version=2&props=hide`, {
            lazy: true,
        });
        const { data: socialConnections } = useFetch('/api/user/social-connections', {
            lazy: true,
        });

        return {
            config: useConfig(),
            dialogue,
            user,
            username: ref(user.value.username),
            email: ref(user.value.email),
            socialLookup: ref(Boolean(user.value.socialLookup)),
            accounts,
            tokenCookie,
            impersonatorCookie,
            termsUpdateDismissed3Cookie,
            profilesData,
            socialConnections,
        };
    },
    data() {
        return {
            message: '',
            messageParams: {},
            messageIcon: null as string | null,
            error: '',
            changeEmailAuthId: null,
            code: '' as string | null,

            socialProviders,

            savingUsername: false,
            savingEmail: false,

            gravatar,

            showCaptcha: false,
            captchaToken: null,

            universalDomains: this.$config.public.allLocalesUrls.filter((x) => x !== this.$config.public.baseUrl),

            logoutInProgress: false,

            showTermsUpdate: newDate() < new Date(2023, 3, 6) &&
                !this.termsUpdateDismissed3Cookie,
        };
    },
    computed: {
        profiles() {
            return this.profilesData?.profiles;
        },
        impersonationActive() {
            return !!this.impersonatorCookie;
        },
        canChangeEmail() {
            return this.email && this.captchaToken;
        },
        usernameError() {
            if (!this.username.match(usernameRegex)) {
                return this.$t('user.account.changeUsername.invalid');
            }
            if (this.username !== encodeURIComponent(this.username)) {
                return this.$t('user.account.changeUsername.nonascii', { encoded: encodeURIComponent(this.username) });
            }
            return null;
        },
        hasEmail() {
            return this.user?.email && !this.user.email.endsWith('.oauth');
        },
        authMethodsCount() {
            if (!this.user || this.socialConnections === null) {
                return null;
            }
            return Object.keys(this.socialConnections).length + (this.hasEmail ? 1 : 0);
        },
    },
    watch: {
        email(v) {
            if (v !== this.user?.email) {
                this.showCaptcha = true;
            }
        },
        async socialLookup(v) {
            const response = await this.dialogue.postWithAlertOnError<any>('/api/user/set-social-lookup', { socialLookup: v });

            await this.$setToken(response.token);
        },
    },
    async mounted() {
        const user = await $fetch('/api/user/current');
        if (user) {
            await this.$setToken(user.token);
        }

        const redirectTo = window.sessionStorage.getItem('after-login');
        if (this.user && redirectTo) {
            window.sessionStorage.removeItem('after-login');
            await this.$router.push(redirectTo);
        }
    },
    methods: {
        async changeUsername() {
            this.error = '';

            if (this.savingUsername || !this.user) {
                return;
            }
            this.savingUsername = true;
            try {
                const response = await this.dialogue.postWithAlertOnError<any>('/api/user/change-username', {
                    username: this.username,
                });

                if (response.error) {
                    this.error = response.error;
                    return;
                }

                await this.$removeToken(this.user.username);
                await this.$setToken(response.token);
                this.username = this.user.username;
                this.message = 'crud.saved';
                this.messageParams = {};
                this.messageIcon = 'check-circle';
                setTimeout(() => this.message = '', 3000);
            } finally {
                this.savingUsername = false;
            }
        },
        async changeEmail() {
            this.error = '';

            if (this.savingEmail) {
                return;
            }
            this.savingEmail = true;
            try {
                const response = await this.dialogue.postWithAlertOnError<any>('/api/user/change-email', {
                    email: this.email,
                    authId: this.changeEmailAuthId,
                    code: this.code,
                    captchaToken: this.captchaToken,
                });

                if (response.error) {
                    this.error = response.error;
                    return;
                }

                if (!this.changeEmailAuthId) {
                    this.changeEmailAuthId = response.authId;
                    this.message = 'user.login.emailSent';
                    this.messageParams = { email: this.addBrackets(this.email) };
                    this.messageIcon = 'envelope-open-text';
                    this.$nextTick(() => {
                        (this.$refs.code as HTMLInputElement).focus();
                    });
                } else {
                    this.changeEmailAuthId = null;
                    this.message = '';
                    this.messageParams = {};
                    this.code = null;

                    await this.$setToken(response.token);
                    this.message = 'crud.saved';
                    this.messageParams = {};
                    this.messageIcon = 'check-circle';
                    setTimeout(() => this.message = '', 3000);
                }
            } finally {
                this.savingEmail = false;
            }
        },
        logout() {
            this.logoutInProgress = true;
            setTimeout(this.doLogout, 3000);
        },
        logoutAll() {
            window.localStorage.removeItem('account-tokens');
            this.logout();
        },
        async doLogout() {
            await this.$removeToken();
            this.logoutInProgress = false;
            setTimeout(() => window.location.reload(), 300);
        },
        setProfiles(profiles: Record<string, Partial<Profile>>) {
            this.profiles = profiles;
        },
        async deleteAccount() {
            await this.dialogue.confirm(this.$t('user.deleteAccountConfirm'), 'danger');

            await this.dialogue.postWithAlertOnError('/api/user/delete');

            if (this.impersonationActive) {
                this.stopImpersonation();
            } else {
                this.logout();
            }
        },
        async setAvatar(source: string | null) {
            const response = await this.dialogue.postWithAlertOnError<any>('/api/user/set-avatar', { source });

            await this.$setToken(response.token);
        },
        async uploaded(ids: string[]) {
            await this.setAvatar(`${this.$config.public.cloudfront}/images/${ids[0]}-avatar.png`);
        },
        async stopImpersonation() {
            if (!this.user) {
                return;
            }

            await this.$removeToken(this.user.username);
            this.tokenCookie = this.impersonatorCookie;
            this.impersonatorCookie = null;
            setTimeout(() => window.location.reload(), 300);
        },
        dismissTermsUpdate() {
            this.termsUpdateDismissed3Cookie = 'true';
            this.showTermsUpdate = false;
        },
        addBrackets(str: string): string {
            return str ? `(${str})` : '';
        },
    },
});
</script>

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

    .profile-current {
        border-inline-start: 3px solid $primary;
    }

    .narrow-message {
        max-width: 56ch;
    }

    @include media-breakpoint-up('md') {
        .avatar-social-hint {
            max-width: 200px;
        }
    }
</style>
