<template>
    <div v-if="requiresLogin && !testerPasswordValid" class="body">
        <div class="container">
            <div class="alert alert-warning m-3 text-center">
                <Icon v="exclamation-triangle" />
                This is a test server
            </div>
            <div class="m-3">
                <div class="input-group py-1">
                    <input v-model="testerPassword" class="form-control" type="password" placeholder="Password" @keydown.enter.prevent="checkTesterPassword">
                    <button type="button" class="btn btn-primary btn-sm border" @click.prevent="checkTesterPassword">
                        <Icon v="sign-in" />
                        Sign in
                    </button>
                </div>
                <p v-if="testerPasswordCookie && !testerPasswordValid" class="small text-danger">
                    <Icon v="exclamation-triangle" />
                    Password invalid
                </p>
            </div>
        </div>
    </div>
    <div v-else class="body">
        <div class="flex-grow-1 vh">
            <Header />
            <Nuxt />
            <TranslationMode />
            <ScrollButton />
        </div>
        <Footer />
        <DialogueBox ref="dialogue" />
        <Lightbox />
    </div>
</template>

<script lang="ts">
import Vue from 'vue';
import type { AxiosRequestConfig } from 'axios';
import dark from '../plugins/dark.ts';
import sorter from 'avris-sorter';
import { sleep } from '../src/helpers.ts';
import md5 from 'js-md5';
import { longtimeCookieSetting } from '../src/cookieSettings.ts';
import { LoadScriptError } from '../src/errors.ts';
import type { default as dialogueBox, DialogueMessage } from '../components/DialogueBox.vue';
import type { Color } from '../src/bootstrap.ts';

type DialogueBox = InstanceType<typeof dialogueBox>;

// no need to be super secure, just a sign that the page is not public
const TESTER_PASSWORD_COOKIE_KEY = 'tester-password';
const TESTER_PASSWORD_HASH = '82feeb96d60170e714df8fb062301e90';

declare global {
    interface Window {
        dataLayer: unknown[];
    }
}

declare module 'vue/types/vue' {
    interface Vue {
        $alert(message: string | DialogueMessage, color?: Color): Promise<string | string[] | undefined>;
        $confirm(message?: string | DialogueMessage, color?: Color): Promise<string | string[] | undefined>;
        $editor(
            value: string | string[],
            message?: string | DialogueMessage,
            color?: Color,
        ): Promise<string | string[] | undefined>;
        $alertRaw(message: string | DialogueMessage, color?: Color): Promise<string | string[] | undefined>;

        $post<T = unknown>(url: string, data?: unknown, options?: AxiosRequestConfig, timeout?: number): Promise<T>;
    }
}

export default dark.extend({
    data(): { testerPassword: string, testerPasswordCookie: string } {
        return {
            testerPassword: '',
            testerPasswordCookie: this.$cookies.get(TESTER_PASSWORD_COOKIE_KEY),
        };
    },
    computed: {
        adsEnabled(): boolean {
            if (this.$isGranted()) {
                const adsVisible = parseInt(localStorage.getItem('adsVisible') || '0') === 1;
                if (!adsVisible) {
                    return false;
                }
            }

            return !!this.$config.ads?.enabled && process.env.NODE_ENV !== 'development';
        },
        requiresLogin(): boolean {
            return process.env.NODE_ENV === 'test' ||
                this.$config.locale !== '_' && !this.$locales[this.$config.locale]?.published;
        },
        testerPasswordValid(): boolean {
            return !!this.testerPasswordCookie && md5(this.testerPasswordCookie) === TESTER_PASSWORD_HASH;
        },
        dialogue(): DialogueBox {
            return this.$refs.dialogue as DialogueBox;
        },
    },
    mounted() {
        Vue.prototype.$alert = (
            message: string | DialogueMessage,
            color: Color = 'primary',
        ): Promise<string | string[] | undefined> => {
            return new Promise((resolve, reject) => {
                this.dialogue.show(false, message, color, undefined, undefined, resolve, reject);
            });
        };
        Vue.prototype.$confirm = (
            message: string | DialogueMessage = '',
            color: Color = 'primary',
        ): Promise<string | string[] | undefined> => {
            return new Promise((resolve, reject) => {
                this.dialogue.show(true, message, color, undefined, undefined, resolve, reject);
            });
        };
        Vue.prototype.$editor = (
            value: string | string[],
            message: string | DialogueMessage = '',
            color: Color = 'primary',
        ): Promise<string | string[] | undefined> => {
            return new Promise((resolve, reject) => {
                this.dialogue.show(false, message, color, value, 'lg', resolve, reject);
            });
        };
        Vue.prototype.$alertRaw = (
            message: string | DialogueMessage,
            color: Color = 'primary',
        ): Promise<string | string[] | undefined> => {
            return new Promise((resolve, reject) => {
                this.dialogue.show(
                    false,
                    `<pre class="text-start"><code>${message}</code></pre>`,
                    color,
                    undefined,
                    'lg',
                    resolve,
                    reject,
                );
            });
        };
        Vue.prototype.$post = <T = unknown>(
            url: string,
            data: unknown = undefined,
            options: AxiosRequestConfig = {},
            timeout = 30000,
        ): Promise<T> => {
            return new Promise((resolve, reject) => {
                this.$axios.$post(url, data, { ...options, timeout })
                    .then((data) => resolve(data))
                    .catch(async (error) => {
                        let errorMessage = this.$t('error.generic');
                        if (typeof error.response?.data?.error === 'string') {
                            errorMessage = this.$t(error.response?.data?.error);
                            // in case no translatable key was provided
                            if (errorMessage === undefined) {
                                errorMessage = error.response?.data?.error;
                            }
                        }
                        await this.$alert(errorMessage, 'danger');
                        reject(new Error(`POST to ${url} failed: ${error.response?.data?.error || 'unknown error'}`));
                    });
            });
        };

        sorter();

        this.confirmAge();

        this.loadAds();
        this.loadGTM();

        let needsRefresh = false;
        const bc = new BroadcastChannel('account_switch');
        bc.onmessage = (ev): void => {
            if (ev.data !== this.$user()?.username) {
                needsRefresh = true;
                if (document.hasFocus()) {
                    needsRefresh = false;
                    window.location.reload();
                }
            }
        };
        window.onfocus = (): void => {
            if (needsRefresh) {
                needsRefresh = false;
                window.location.reload();
            }
        };
    },
    methods: {
        async confirmAge(): Promise<void> {
            if (!this.$te('footer.ageLimit') || localStorage.getItem('ageConfirmed')) {
                return;
            }

            while (this.$refs.dialogue === undefined) {
                await sleep(100);
            }
            await this.$alert(this.$t('footer.ageLimit'));

            localStorage.setItem('ageConfirmed', '1');
        },
        async loadAds(): Promise<void> {
            if (!this.adsEnabled) {
                return;
            }

            try {
                await this.$loadScript('publift', 'https://cdn.fuseplatform.net/publift/tags/2/3329/fuse.js');
            } catch (error) {
                if (error instanceof LoadScriptError) {
                    return;
                }
                throw error;
            }
        },
        async loadGTM(): Promise<void> {
            if (!this.adsEnabled) {
                return;
            }

            try {
                await this.$loadScript('gtm', 'https://www.googletagmanager.com/gtag/js?id=G-TDJEP12Q3M');
            } catch (error) {
                if (error instanceof LoadScriptError) {
                    return;
                }
                throw error;
            }

            window.dataLayer = window.dataLayer || [];
            function gtag(..._args: unknown[]): void {
                window.dataLayer.push(arguments);
            }
            gtag('js', new Date());
            gtag('config', 'G-TDJEP12Q3M');
        },
        checkTesterPassword(): void {
            this.$cookies.set(TESTER_PASSWORD_COOKIE_KEY, this.testerPassword, longtimeCookieSetting);
            this.testerPasswordCookie = this.testerPassword;
        },
    },
});
</script>

<style lang="scss">
    @import "assets/variables";
    @import "~avris-sorter/dist/Sorter.min.css";

    @include media-breakpoint-up('lg', $grid-breakpoints) {
        .body {
            margin-top: $header-margin;
        }
        .sticky-top {
            top: $header-height - 1px;
        }
    }

    .vh {
        min-height: calc(100vh - #{$header-height});
    }
</style>
