CamLogin = (function ($) {
    let waitElement;
    let processElement;
    let errorElement;
    let encapForm;
    let pinTanForm;
    let eIdFields;
    let app2appButtons;
    let app2appDeeplink;
    let app2appAbort;
    let app2appDeeplinkInfo;
    let app2appShowEid;
    let isFTN;
    let isDeeplink = false;
    let app2appOnline;

    let paybuttons;
    let paybuttonApprove;
    let isPaybutton;

    let isNetbank = false;

    const csrfKey = $("meta[name='_csrf_header']").attr("content");
    const csrfValue = $("meta[name='_csrf']").attr("content");

    document.addEventListener('DOMContentLoaded', async function() {
        eIdFields = $('#eId-fields');
        encapForm = $("#encapLoginForm");
        waitElement = $('.spinner');
        processElement = $('#processStatus');
        errorElement = $('#dataError');

        uidLabel = $('#js_error-mark-label');
        uidInput = $('#encapUsername');
        app2appButtons = $('.app2app-buttons');
        app2appDeeplink = $('a[data-link-type="app2app"]');
        app2appAbort = $('.app2app-abort a');
        app2appDeeplinkInfo = $('.app2app_download_info');
        app2appShowEid = $('a[data-link-type="eid"]');

        paybuttons = $('#paybuttons');
        isPaybutton = paybuttons.length > 0;
        if(isPaybutton) {
            pinTanForm = $('[name="loginForm"]');
            paybuttonApprove = paybuttons.children('[name="btn_login"]');
        }

        isFTN = location.href.includes('/ftn/');
        if(isFTN) pinTanForm = $('#CREDENTIALS_FORM');
        if(!isPaybutton && !isFTN) {
            pinTanForm = $('#login');
            isNetbank = true;
        }

        app2appOnline = window.cbs.app2appOnline === 'true';

        uiInit();

        // ###############
        // SUBMIT HANDLERS
        // ###############

        encapForm.submit(async function(event){
            try{
                await login();
                return false;
            }catch(error){
                uiHandleEncapError(error);
            }
        });

        // ###############
        // CLICK HANDLERS
        // ###############

        app2appDeeplink.click(async function(){
            isDeeplink = true;
        });

        app2appShowEid.click(function(){
            // Switch to e-ID form if the link has the eid attribute
            isDeeplink = false;
            terminatePolling();
            uiToggleUserIdDeeplink();
            return;
        });

        app2appAbort.click(function(event){
            // Reload the page to get a new QR id
            event.preventDefault();
            location.reload();
        });

        // In Paybutton
        if(isPaybutton) {
            // let the approve button submit the PIN/TAN form
            paybuttons.children('#btn_login_pintan').click(function(e){
                pinTanForm.submit();
            });

            // if page is not opened from app
            if(!location.href.includes('#encap-result')) {
                // show the Approve button in PIN/TAN tab
                $('a[href="#ENCAP"]').click(function(e) {
                    paybuttonApprove.addClass('hidden');
                });
                $('a[href="#PIN_TAN"]').click(async function(e) {
                    paybuttonApprove.removeClass('hidden');
                    await terminatePolling();
                    waitElement.addClass('hidden');
                    uiResestEidError();
                });
            }
        }

        // ####################
        // OTHER EVENT HANDLERS
        // ####################

        // Start detecting when the user comes back from the app
        if(isMobile()) {
            document.addEventListener('visibilitychange', async function(event){
                if(isDeeplink && app2appOnline && 'visible' === document.visibilityState) {
                    requestAnimationFrame(docFocus);

                    function docFocus(){
                        if(!(document.hasFocus)){
                            return requestAnimationFrame(docFocus);
                        }

                        deeplinkLogin();
                    }
                }
            });
        }
    });

    async function deeplinkLogin(){
        app2appButtons.addClass('hidden');
        uiShowEidWaitingProcess();

        try{
            // Start polling QR verification
            const qrVerificationResult = await pollQrVerification();
            if(!(qrVerificationResult && qrVerificationResult.success)) {
                handleResponseError(qrVerificationResult);
                return;
            }

            // Authenticate, login and redirect
            await login(true);

            uiResestEidError();

        }catch(error){
            uiHandleEncapError(error);
        }
    }

    async function login(){
        if(!isDeeplink) uiShowEidWaitingProcess();

        // Authenticate providing user id
        let authStartResponse;
        if(!isDeeplink) {
            authStartResponse = await postLoginForm();
            if(typeof authStartResponse === 'text') CBSFetchUtils.documentReplace(authStartResponse);
            if(!authStartResponse
                || !authStartResponse.success
                || (isFTN && !authStartResponse.userId)) {
                handleResponseError( {
                    success: false,
                    error: authStartResponse && authStartResponse.error
                    });
                return;
            }
        }

        // Login
        const loginResult = await pollLogin(authStartResponse);
        // ticketId and userId is only used in FTN
        const ticketId = isFTN
                            ? isDeeplink
                                ? (loginResult && loginResult.response && loginResult.response.ticketId)
                                : authStartResponse.ticketId
                            : null;
        if(loginResult && loginResult.success) {
            // In Netbank & Paybutton
            if(!isFTN) {
                createSessionRedirect();
                return;
            }

            // In Netbank and FTN
            ticketLogin(cbs.contextPath, ticketId, handleResponseError);
            return;
        }else{
            handleResponseError(loginResult);
            return;
        }
    }

    function createSessionRedirect() {
        $("#encap-redirect-form").submit();
    }

    // UI functions
    async function uiInit(){
        // Generic behavior
        // If the page is a response to a pintan request, show the approve button
        if(isPaybutton && $('#PIN_TAN').attr('style') === 'display: flex;') paybuttonApprove.removeClass('hidden');

        if(isMobile() && app2appOnline) {
            // If the ABF app opens this page as a result of an encap auth for TPP,
            // only show message for customer to switch back to TPP manually
            if(location.href.includes('#encap-result') && !isNetbank) {
                const switchBackMessage = $('#switchBack').detach();
                $('#ENCAP').replaceWith(switchBackMessage);
                $('#PIN_TAN').replaceWith(switchBackMessage);
                switchBackMessage.removeClass('hidden');
                if(isPaybutton) paybuttons.addClass('hidden');
                // set the language selection href to #encap-result to show the same message in the other language
                $('.c_section__main').children().first().children('a').attr('href', '#encap-result');
                return;
            }

            // Set the download link
            const downloadLink = document.querySelector('.app2app_download_info a');
            const agent = navigator.userAgent.toLowerCase();
            const linkAddress = agent.includes('android')
                ? 'https://play.google.com/store/apps/details?id=com.alandsbanken.mobilbank.fi'
                : (agent.includes('iphone') ? 'https://apps.apple.com/se/app/%C3%A5landsbanken-finland/id1052113446' : '');
            downloadLink.href = linkAddress;

            // Prepare deeplink with
            // - callback attribute to workaround iOS "Open last tab" issue
            // - userAgent attibute to determine in what client the the page is opened
            const encodedHref = encodeURIComponent(location.href);
            app2appDeeplink.attr('href',
                `${app2appDeeplink.attr('href')}&callback=${encodedHref}&userAgent=${encodeURIComponent(agent)}`);

            // Reset all possibly ongiong polling
            terminatePolling();

            // In Paybutton page, use the text on the Approve button
            // and show the back to merchant link
            if(isPaybutton){
                app2appDeeplink.text(paybuttons.children('button').text());
                paybuttons.children('a').removeClass('hidden');
            }

            app2appButtons.removeClass('hidden');
            return;
        }

        // Desktop behavior
        eIdFields.removeClass('hidden');
        // Hide the message about where to download the app
        app2appDeeplinkInfo.addClass('hidden');
    }

    function uiToggleUserIdDeeplink(){
        if(eIdFields.hasClass('hidden')) {
            eIdFields.removeClass('hidden');
            app2appButtons.addClass('hidden');
            return;
        }

        eIdFields.addClass('hidden');
        app2appButtons.removeClass('hidden');
        paybuttons.children('button').addClass('hidden');
    }

    function uiShowEidWaitingProcess() {
        // Don't show the abort button for iPhone in Paybutton/FTN as reload will load Netbank login
        if(detectDeviceType() === 'android' || (detectDeviceType() === 'iphone' && isNetbank)) app2appAbort.removeClass('hidden');
        waitElement.removeClass('hidden');
        processElement.removeClass('hidden');
        encapForm.addClass('hidden');
    }

    function uiHandleEncapError(error) {
        waitElement.addClass('hidden');
        processElement.addClass('hidden');
        encapForm.removeClass('hidden');
        errorElement.html(error && error.name === 'response' && error.message || cbs.labels.err_unexpected_error);
        errorElement.removeClass('hidden');
        // Don't show the abort button for iPhone in Paybutton/FTN as reload will load Netbank login
        if(detectDeviceType() === 'android' || (detectDeviceType() === 'iphone' && isNetbank)) app2appAbort.removeClass('hidden');
        uidLabel.addClass('has-error');
        uidInput.addClass('is-invalid');
        uidInput.attr("aria-invalid", "true");
    }

    function uiResestEidError(){
        errorElement.addClass('hidden');
        errorElement.html('');
        uidLabel.removeClass('has-error');
        uidInput.removeClass('is-invalid');
        uidInput.removeAttr("aria-invalid", "true");
    }

    // AJAX functions
    async function pollQrVerification() {
        const qrResult = await CBSFetchUtils.polling({
            url: `${cbs.contextPath}/qr/isVerified${isFTN?'':'.ds'}?qrId=${encodeURIComponent(cbs.qrId)}`,
            options: {
                method: 'GET'
            },
            conditions: [
                'response.verified === true',
                'typeof response === "string"',
                'response && response.status && response.status.errors && response.status.errors.length'
            ],
            polling: {
                timeout: 60 * 1000
            }
        });

        const qrVerified = qrResult && qrResult.success && qrResult.response && qrResult.response.verified;

        // Replace document with backend provided html markup.
        if(!qrVerified && typeof qrResult.response === "string"){
            return {
                success: false,
                error: qrResult.response,
                documentReplace: true
            };
        }
        // Show backend provided error message or generic error message.
        if(!qrVerified || !qrResult){
            return {
                success: false,
                error: qrResult
                        && qrResult.response
                        && qrResult.response.status
                        && qrResult.response.status.errors
                        && Array.isArray(qrResult.response.status.errors)
                        && qrResult.response.status.errors.shift()
            };
        }

        return {
            success: true
        };

    }

    async function postLoginForm() {
        const body = CBSFetchUtils.formUrlEncoded(encapForm[0]);
        const url = encapForm.attr('action');
        const request = await fetch(url, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded'
            },
            body
          });

        const response = await CBSFetchUtils.requestResponseBody(request);

        return response;
    }

    async function pollLogin(authStartResponse) {
        // The endpoint, method and headers differ between FTN and Netbank/Paybutton
        // FTN: POST with headers
        // Netbank/Paybutton: GET without headers
        const method = isDeeplink && !isFTN ? 'GET' : 'POST';
        const getParams = `?qrId=${encodeURIComponent(cbs.qrId)}`;
        const postParams = isFTN
                            ? isDeeplink
                                ? JSON.stringify({qrId: encodeURIComponent(cbs.qrId)})
                                : `userId=${encodeURIComponent(authStartResponse.userId)}`
                            : undefined;
        const endpoint = isDeeplink
                            ? (isFTN ? '/auth/encap/qrEncapLogin' : '/encap/qrEncapLogin.ds')
                            : (isFTN ? '/auth/encap/finish' : '/auth/encapProcessLogin.do');
        const url = `${cbs.contextPath}${endpoint}${'GET' === method ? getParams : ''}`;
        const postContentType = isDeeplink ? 'application/json; charset=UTF-8' : 'application/x-www-form-urlencoded';
        const config = {
            url: url,
            conditions: [
                'response.prosessStatus === "COMPLETE"',
                'response.prosessStatus === "FAILED"',
                'typeof response === "string"',
                'response && response.error'
            ],
            polling: {
                timeout: 60 * 1000
            }
        };

        if('POST' === method) {
            const options = {};
            options['method'] = method;
            options['headers'] = {
                [csrfKey]: csrfValue,
                'X-Requested-With': 'XMLHttpRequest',
                'Content-Type': postContentType
            };
            options['body'] = postParams;

            config['options'] = options;
        }

        const loginResult = await CBSFetchUtils.polling(config);

        const loginSuccess = loginResult
                                && loginResult.response
                                && loginResult.response.success
                                && loginResult.response.prosessStatus === 'COMPLETE';

        // Redirect the user if it's not allowed to view the result
        if(loginResult && loginResult.response && loginResult.response.error == "USER_RESTRICTED") {
            location.href = cbs.redirectUrl;
        }

        // Replace document with backend provided html markup.
        if(!loginSuccess && typeof loginResult.response === "string"){
            return {
                success: false,
                error: loginResult.response,
                documentReplace: true
            };
        }
        // Show backend provided error message or generic error message.
        if(!loginSuccess || !loginResult){
            return {
                success: false,
                error: loginResult
                        && loginResult.response
                        && loginResult.response.error
            };
        }

        return {
            success: true,
            response: loginResult.response
        }
    }

    async function terminatePolling(){
        await CBSFetchUtils.polling({
            url: '*',
            terminate: true
        });
    }

    // Util functions
    function handleResponseError(result) {
        if(!result || !result.error)  throw new Error();
        if(result && result.documentReplace) {
            CBSFetchUtils.documentReplace(result.error);
            return;
        }

        const error = new Error(result.error);
        error.name = 'response';
        throw error;
    }

    function isMobile() {
        const agent = navigator.userAgent.toLowerCase();
        return agent.includes('mobile') && (
            agent.includes('android')
            || agent.includes('iphone')
        );
    }

    function detectDeviceType(){
        const agent = navigator.userAgent.toLowerCase();
    
        if(agent.includes('android')) return 'android';
        if(agent.includes('iphone')) return 'iphone';
    
        return 'other';
      }

}(jQuery));
