{% load static %}
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login passkey</title>
    <script src="{% static 'js/jquery-3.6.1.min.js' %}?v=10"></script>
</head>
<body>
<form action="{% url 'api-auth:passkey-auth' %}" method="post" id="loginForm">
    {% csrf_token %}
    <input type="hidden" name="passkeys" id="passkeys"/>
</form>
</body>
<script>
    const loginUrl = "/core/auth/login/";
    window.conditionalUI = false;
    window.conditionUIAbortController = new AbortController();
    window.conditionUIAbortSignal = conditionUIAbortController.signal;

    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'

    // Use a lookup table to find the index.
    const lookup = new Uint8Array(256)
    for (let i = 0; i < chars.length; i++) {
        lookup[chars.charCodeAt(i)] = i
    }

    const encode = function (arraybuffer) {
        const bytes = new Uint8Array(arraybuffer)
        let i;
        const len = bytes.length;
        let base64url = ''

        for (i = 0; i < len; i += 3) {
            base64url += chars[bytes[i] >> 2]
            base64url += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]
            base64url += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]
            base64url += chars[bytes[i + 2] & 63]
        }

        if ((len % 3) === 2) {
            base64url = base64url.substring(0, base64url.length - 1)
        } else if (len % 3 === 1) {
            base64url = base64url.substring(0, base64url.length - 2)
        }
        return base64url
    }

    const decode = function (base64string) {
        const bufferLength = base64string.length * 0.75
        const len = base64string.length;
        let i;
        let p = 0
        let encoded1;
        let encoded2;
        let encoded3;
        let encoded4

        const bytes = new Uint8Array(bufferLength)

        for (i = 0; i < len; i += 4) {
            encoded1 = lookup[base64string.charCodeAt(i)]
            encoded2 = lookup[base64string.charCodeAt(i + 1)]
            encoded3 = lookup[base64string.charCodeAt(i + 2)]
            encoded4 = lookup[base64string.charCodeAt(i + 3)]

            bytes[p++] = (encoded1 << 2) | (encoded2 >> 4)
            bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2)
            bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63)
        }
        return bytes.buffer
    }

    function checkConditionalUI(form) {
        if (!navigator.credentials) {
            alert('WebAuthn is not supported in this browser')
            return
        }
        if (window.PublicKeyCredential && PublicKeyCredential.isConditionalMediationAvailable) {
            // Check if conditional mediation is available.
            PublicKeyCredential.isConditionalMediationAvailable().then((result) => {
                window.conditionalUI = result;
                if (!window.conditionalUI) {
                    alert("Conditional UI is not available. Please use the legacy UI.");
                } else {
                    return true
                }
            });
        }
    }

    const publicKeyCredentialToJSON = pubKeyCred => {
        if (pubKeyCred instanceof Array) {
            const arr = []
            for (const i of pubKeyCred) {
                arr.push(publicKeyCredentialToJSON(i))
            }
            return arr
        }

        if (pubKeyCred instanceof ArrayBuffer || pubKeyCred instanceof Uint8Array) {
            return encode(pubKeyCred)
        }

        if (pubKeyCred instanceof Object) {
            const obj = {}

            for (const key in pubKeyCred) {
                obj[key] = publicKeyCredentialToJSON(pubKeyCred[key])
            }

            return obj
        }

        return pubKeyCred
    }

    function GetAssertReq(getAssert) {
        getAssert.publicKey.challenge = decode(getAssert.publicKey.challenge)

        for (const allowCred of getAssert.publicKey.allowCredentials) {
            allowCred.id = decode(allowCred.id)
        }
        return getAssert
    }

    function startAuthn(form, conditionalUI = false) {
        window.loginForm = form
        fetch('/api/v1/authentication/passkeys/auth/', {method: 'GET'}).then(function (response) {
            if (response.ok) {
                return response.json().then(function (req) {
                    return GetAssertReq(req)
                })
            }
            throw new Error('No credential available to authenticate!')
        }).then(function (options) {
            if (conditionalUI) {
                options.mediation = 'conditional'
                options.signal = window.conditionUIAbortSignal
            } else {
                window.conditionUIAbortController.abort()
            }
            return navigator.credentials.get(options)
        }).then(function (assertion) {
            const pk = $('#passkeys')
            if (pk.length === 0) {
                retry("Did you add the 'passkeys' hidden input field")
                return
            }
            pk.val(JSON.stringify(publicKeyCredentialToJSON(assertion)))
            const x = document.getElementById(window.loginForm)
            if (x === null || x === undefined) {
                console.error('Did you pass the correct form id to auth function')
                return
            }
            x.submit()
        }).catch(function (err) {
            retry(err)
        })
    }

    function safeStartAuthn(form) {
        checkConditionalUI('loginForm')
        const errorMsg = "{% trans 'This page is not served over HTTPS. Please use HTTPS to ensure security of your credentials.' %}"
        const isSafe = window.location.protocol === 'https:'
        if (!isSafe && location.hostname !== 'localhost') {
            alert(errorMsg)
            window.location.href = loginUrl
        } else {
            setTimeout(() => startAuthn('loginForm'), 100)
        }
    }

    function retry(error) {
        const fullError = "{% trans 'Error' %}" + ': ' + error + "\n\n " + "{% trans 'Do you want to retry ?' %}"
        const result = confirm(fullError)
        if (result) {
            safeStartAuthn()
        } else {
            window.location.href = loginUrl
        }
    }

    {% if not error %}
        window.onload = function () {
            safeStartAuthn()
        }
    {% else %}
        const error = "{{ error }}"
        retry(error)
    {% endif %}
</script>
</html>
