// @flow
import type { Global } from 'types';

export default class Permission {
    // Private members
    _permissions: string[];
    _win: Global;

    // Public members
    GRANTED: string;
    DEFAULT: string;
    DENIED: string;

    constructor(win: Global) {
        this._win = win;
        this.GRANTED = 'granted';
        this.DEFAULT = 'default';
        this.DENIED = 'denied';
        this._permissions = [this.GRANTED, this.DEFAULT, this.DENIED];
    }

    /**
   * Requests permission for desktop notifications
   * @param {Function} onGranted - Function to execute once permission is granted
   * @param {Function} onDenied - Function to execute once permission is denied
   * @return {void, Promise}
   */
    request(onGranted: () => void, onDenied: () => void) {
        return arguments.length > 0
            ? this._requestWithCallback(...arguments)
            : this._requestAsPromise();
    }

    /**
   * Old permissions implementation deprecated in favor of a promise based one
   * @deprecated Since V1.0.4
   * @param {Function} onGranted - Function to execute once permission is granted
   * @param {Function} onDenied - Function to execute once permission is denied
   * @return {void}
   */
    _requestWithCallback(onGranted: () => void, onDenied: () => void) {
        const existing = this.get();

        var resolved = false;
        var resolve = (result = this._win.Notification.permission) => {
            if (resolved) return;
            resolved = true;
            if (typeof result === 'undefined' && this._win.webkitNotifications)
                result = this._win.webkitNotifications.checkPermission();
            if (result === this.GRANTED || result === 0) {
                if (onGranted) onGranted();
            } else if (onDenied) onDenied();
        };
        var request;

        /* Permissions already set */
        if (existing !== this.DEFAULT) {
            resolve(existing);
        } else if (
            this._win.webkitNotifications &&
            this._win.webkitNotifications.checkPermission
        ) {
            /* Safari 6+, Legacy webkit browsers */
            this._win.webkitNotifications.requestPermission(resolve);
        } else if (
            this._win.Notification &&
            this._win.Notification.requestPermission
        ) {
            /* Safari 12+ */
            /* This resolve argument will only be used in Safari */
            /* CHrome, instead, returns a Promise */
            request = this._win.Notification.requestPermission(resolve);
            if (request && request.then) {
                /* Chrome 23+ */
                request.then(resolve).catch(function() {
                    if (onDenied) onDenied();
                });
            }
        } else if (onGranted) {
            /* Let the user continue by default */
            onGranted();
        }
    }

    /**
   * Requests permission for desktop notifications in a promise based way
   * @return {Promise}
   */
    _requestAsPromise(): Promise<void> {
        const existing = this.get();

        let isGranted = result => result === this.GRANTED || result === 0;

        /* Permissions already set */
        var hasPermissions = existing !== this.DEFAULT;

        /* Safari 6+, Chrome 23+ */
        var isModernAPI =
            this._win.Notification && this._win.Notification.requestPermission;

        /* Legacy webkit browsers */
        var isWebkitAPI =
            this._win.webkitNotifications &&
            this._win.webkitNotifications.checkPermission;

        return new Promise((resolvePromise, rejectPromise) => {
            var resolved = false;
            var resolver = result => {
                if (resolved) return;
                resolved = true;
                isGranted(result) ? resolvePromise() : rejectPromise();
            };
            var request;

            if (hasPermissions) {
                resolver(existing);
            } else if (isWebkitAPI) {
                this._win.webkitNotifications.requestPermission(result => {
                    resolver(result);
                });
            } else if (isModernAPI) {
                /* Safari 12+ */
                /* This resolver argument will only be used in Safari */
                /* CHrome, instead, returns a Promise */
                request = this._win.Notification.requestPermission(resolver);
                if (request && request.then) {
                    /* Chrome 23+ */
                    request.then(resolver).catch(rejectPromise);
                }
            } else resolvePromise();
        });
    }

    /**
   * Returns whether Push has been granted permission to run
   * @return {Boolean}
   */
    has() {
        return this.get() === this.GRANTED;
    }

    /**
   * Gets the permission level
   * @return {Permission} The permission level
   */
    get() {
        let permission;

        /* Safari 6+, Chrome 23+ */
        if (this._win.Notification && this._win.Notification.permission)
            permission = this._win.Notification.permission;
        else if (
            this._win.webkitNotifications &&
            this._win.webkitNotifications.checkPermission
        )
            /* Legacy webkit browsers */
            permission = this._permissions[
                this._win.webkitNotifications.checkPermission()
            ];
        else if (navigator.mozNotification)
            /* Firefox Mobile */
            permission = this.GRANTED;
        else if (this._win.external && this._win.external.msIsSiteMode)
            /* IE9+ */
            permission = this._win.external.msIsSiteMode()
                ? this.GRANTED
                : this.DEFAULT;
        else permission = this.GRANTED;

        return permission;
    }
}
