/* Essentials YOOtheme Pro 2.2.16 build 0813.1608; ZOOlanders https://www.zoolanders.com; Copyright (C) Joolanders, SL; http://www.gnu.org/licenses/gpl.html GNU/GPL */

import http from '@yootheme/http';
import essentials from 'essentials';
import { uuid } from '@essentials/core/util';
import { includes, isEmpty, isObject } from 'uikit-util';
import UIkit from 'uikit';

export default {
    data: () => ({
        drivers: essentials.customizer?.auth_drivers || {},
    }),

    computed: {
        auths: {
            get() {
                return essentials.Config.get('auth.auths', []);
            },

            set(values) {
                essentials.Config.set('auth.auths', values);
            },
        },
    },

    methods: {
        save(auth) {
            const driver = this.drivers[auth.driver];

            if (!driver) {
                throw Error(`Unknown Driver: ${auth.driver}`);
            }

            const auths = [...this.auths];
            const index = auths.findIndex((a) => a.id === auth.id);

            if (index >= 0) {
                // update
                auths[index] = auth;
            } else {
                // an auth could already have an id
                // TODO find a better way to deal with it
                Object.assign(auth, { id: auth.id || uuid(), timestamp: Date.now() });
                auths.push(auth);
            }

            this.auths = auths;

            return auth;
        },

        remove(values, predeleteEndpoint) {
            return new Promise((resolve, reject) => {
                if (!predeleteEndpoint) {
                    resolve({});
                    return;
                }

                return http(predeleteEndpoint)
                    .post({})
                    .json()
                    .catch((e) => reject(e?.json));
            })
                .then(() => {
                    const auths = [...this.auths];
                    const index = auths.findIndex((a) => a.id === values.id);

                    auths.splice(index, 1);
                    this.auths = auths;
                })
                .catch((e) => {
                    UIkit.notification(e || 'An error occurred.', 'danger');
                });
        },

        oauth(driver, scopes) {
            return new Promise((resolve, reject) => {
                const $driver = this.drivers[driver];

                if (!$driver) {
                    reject(`Unknown Driver: ${driver}`);
                    return;
                }

                if (!scopes) {
                    reject('Scopes must be specified.');
                    return;
                }

                const oauthEndpoint = $driver?.endpoints?.oauth;

                if (!oauthEndpoint) {
                    reject('Driver is missing oauth endpoint.');
                    return;
                }

                if ($driver.poll) {
                    this.oauthWithEndpoll({ driver: $driver, scopes, reject, resolve });
                } else {
                    const endpointUrl = getOauthEndpoint(oauthEndpoint, scopes);

                    window.open(endpointUrl, '');
                    window.addEventListener(
                        'message',
                        (e) => {
                            if (!endpointUrl.match(new RegExp(e.origin))) {
                                return;
                            }

                            const data = isObject(e.data) ? e.data : {};

                            if (!data.id) {
                                reject('Something went wrong, please try again.');
                                return;
                            }

                            resolve(data);
                        },
                        false
                    );
                }
            }).then((data) => {
                // if unknown granted scopes, assume all granted
                if (!data.scopes?.length) {
                    data.scopes = scopes;
                }

                return data;
            });
        },

        oauthWithEndpoll({ driver, reject, resolve, scopes }) {
            const oauthEndpoint = driver?.endpoints?.oauth;
            const idEndpoint = 'yooessentials/auth/generate-id';

            http(idEndpoint.toString())
                .post({})
                .json((response) => {
                    const id = response.id;

                    if (!id) {
                        reject('Driver id endpoint not resolved.');
                        return;
                    }

                    const oauthEndpointUrl = getOauthEndpoint(oauthEndpoint, scopes, id);
                    const pollEndpointUrl = 'yooessentials/auth/poll';

                    const win = window.open(oauthEndpointUrl, '');

                    onWinClose(win, () => {
                        let count = 0;

                        const interval = setInterval(() => {
                            if (count > 10) {
                                clearInterval(interval);
                                reject('Sorry, something went wrong.');
                            }

                            http(pollEndpointUrl)
                                .post({
                                    id,
                                    scopes,
                                    driver: driver?.name,
                                })
                                .json((response) => {
                                    if (!isEmpty(response)) {
                                        clearInterval(interval);
                                        resolve(response);
                                    }
                                });

                            count++;
                        }, 500);
                    });
                });
        },

        validateScopes(auth, scopes) {
            return scopes?.every((scope) => includes(auth.scopes, scope));
        },
    },
};

function onWinClose(win, cb) {
    const interval = setInterval(() => {
        if (win.closed) {
            clearInterval(interval);
            cb();
        }
    }, 400);
}

function getOauthEndpoint(endpoint, scopes, id) {
    const url = new URL(endpoint);
    const params = url.searchParams;

    params.set('scopes', scopes);
    params.set('yooessentialsVersion', essentials.version);

    id && params.set('id', id);

    url.search = '?' + params.toString();

    return url.toString();
}
