/**
 * D4Science boot component that handles keykloak authz
 * <d4s-boot-2
 *  /@url: keycloak url, for example: https://accounts.pre.d4science.org/auth]
 *  /@realm: is the realm [defaults to: d4science]
 *  /@gateway: is the client id, for example: "pre.d4science.org"
 *  /@context: is the audience, for example: "%2Fpred4s%2Fpreprod%2FpreVRE"
 *  /@redirect-url: where to go on logout
 */
window.customElements.define('d4s-boot-2', class extends HTMLElement {

    #keycloak = null
    #authorization = null
    #url = null
    #realm = "d4science"
    #clientId = null
    #redirectUrl = null
    #audience = null

    // loading attempts nr and timer between attempts
    #attempts = 6
    #timer = 500

    constructor() {
        super()
    }

    connectedCallback() {
        if (typeof Keycloak === 'undefined') {
            const script = document.createElement('script')
            script.src = this.#url + '/js/keycloak.js'
            script.type = 'text/javascript'
            script.addEventListener('load', () => {
                this.initKeycloak()
            })
            document.head.appendChild(script)
        } else {
            this.initKeycloak()
        }
    }

    initKeycloak() {
        this.#keycloak = new Keycloak({
            url: this.#url,
            realm: this.#realm,
            clientId: this.#clientId
        })

        const keycloak = this.#keycloak
        const url = this.#url

        keycloak.init({
            onLoad: 'login-required'
            , checkLoginIframe: false

        }).then((authenticated) => {
            console.log(authenticated ? 'Authenticated in ' + this.#realm : 'Not authenticated')
            if (typeof KeycloakAuthorization === 'undefined') {
                const authz = document.createElement('script')
                authz.src = url + '/js/keycloak-authz.js'
                authz.type = 'text/javascript'
                authz.addEventListener('load', () => {
                    this.#authorization = new KeycloakAuthorization(keycloak)
                })
                document.head.appendChild(authz)
            } else {
                this.#authorization = new KeycloakAuthorization(keycloak)
            }
            //console.log("expires: " + this.expirationDate(keycloak.tokenParsed.exp))

        }).catch(function(err) {
            console.error("Failed to initialize d4science boot component")
            console.error(err)
        })
    }

    parseJwt(token) {
        var base64Url = token.split('.')[1]
        var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
        var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
        }).join(''))
    
        return JSON.parse(jsonPayload);
    }

    expirationDate(utc) {
        let d = new Date(0)
        d.setUTCSeconds(utc)
        return d
    }

    checkContext() {
        const parseJwt = this.parseJwt
        const expDt = this.expirationDate
        const audience = encodeURIComponent(this.#audience)
        this.#authorization.entitlement(audience).then(function (rpt) {
            // onGrant callback function.
            // If authorization was successful you'll receive an RPT
            // with the necessary permissions to access the resource server
            console.log(rpt)
            //console.log("rpt expires: " + expDt(parseJwt(rpt).exp))
        })
    }

    service(endpoint, method, params, onSuccess, onForbidden) {
        if (this.#authorization == null) {
            if (this.#attempts-- > 0) {
                setTimeout(() => this.service(endpoint, method, params, onSuccess, onForbidden), this.#timer)
                return
            } else {
                console.error("Impossible to initialize D4Science authorization component")
                throw "Fatal error"
            }
        }
        this.#keycloak.updateToken(30).then(() => {
            const audience = encodeURIComponent(this.#audience)
            this.#authorization.entitlement(audience).then(function (rpt) {
                var req = new XMLHttpRequest()
                req.open(method, endpoint, true)
                req.setRequestHeader('Accept', 'application/json')
                req.setRequestHeader('Authorization', 'Bearer ' + rpt)
    
                req.onreadystatechange = function () {
                    if (req.readyState == 4) {
                        if (req.status == 200) {
                            onSuccess(req.response)
                        } else if (req.status == 403) {
                            onForbidden(req.statusText)
                        }
                    }
                }
                req.send(params)
            })
        }).catch(function() {
            onForbidden('Failed to refresh token')
        })
    }

    download(endpoint, name) {
        this.#keycloak.updateToken(30).then(() => {
            const audience = encodeURIComponent(this.#audience)
            this.#authorization.entitlement(audience).then(function (rpt) {
                fetch(endpoint, {
                    method: 'GET',
                    headers: {
                        'Authorization': 'Bearer ' + rpt
                    }
                })
                .then(response => response.blob())
                .then(blob => {
                    const objectURL = URL.createObjectURL(blob)
                    var tmplnk = document.createElement("a")
                    tmplnk.download = name
                    tmplnk.href = objectURL
                    document.body.appendChild(tmplnk)
                    tmplnk.click()
                    document.body.removeChild(tmplnk)
                })
            })
        }).catch(function() {
            onForbidden('Failed to refresh token')
        })
    }

    logout() {
        if (this.#keycloak) {
            if (!this.#redirectUrl) {
                console.error("Missing required @redirectUrl attribute in d4s-boot")
            } else {
                this.#keycloak.logout({
                    redirectUri: this.#redirectUrl
                })
            }
        }
    }

    static get observedAttributes() {
        return ["url", "realm", "gateway", "redirect-url", "context"];
    }

    attributeChangedCallback(name, oldValue, newValue) {
        if (oldValue !== newValue) {
            switch (name) {
                case "url":
                    this.#url = newValue
                    break
                case "realm":
                    this.#realm = newValue
                    break
                case "gateway":
                    this.#clientId = newValue
                    break
                case "redirect-url":
                    this.#redirectUrl = newValue
                    break
                case "context":
                    this.#audience = newValue
                    break
            }
        }
    }
    
    get url() {
        return this.#url
    }

    set url(url) {
        this.#url = url
        this.setAttribute("url", url)
    }

    get realm() {
        return this.#realm
    }

    set realm(realm) {
        this.#realm = realm
        this.setAttribute("realm", realm)
    }

    get clientId() {
        return this.#clientId
    }

    set clientId(clientId) {
        this.#clientId = clientId
        this.setAttribute("gateway", clientId)
    }

    get redirectUrl() {
        return this.#redirectUrl
    }

    set redirectUrl(redirectUrl) {
        this.#redirectUrl = redirectUrl
        this.setAttribute("redirect-url", redirectUrl)
    }

    get context() {
        return this.#audience
    }

    set context(context) {
        this.#audience = context
        this.setAttribute("context", context)
    }
})