import io
import os
import re
import json
import time
import base64
import hashlib
import binascii
import datetime
from browser import aio
from browser import ajax
from browser import bind
from browser import html
from browser import timer
from browser import window
from javascript import JSON
from browser import document
from browser import local_storage
from browser import session_storage
from browser.widgets.dialog import EntryDialog

n_hex = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + \
        'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + \
        'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D' + 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F' + \
        '83655D23DCA3AD961C62F356208552BB9ED529077096966D' + '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' + \
        'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9' + 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510' + \
        '15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64' + 'ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' + \
        'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B' + 'F12FFA06D98A0864D87602733EC86A64521F2B18177B200C' + \
        'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31' + '43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF'
g_hex = '2'
info_bits = bytes('Caldera Derived Key', 'utf-8')
NEW_PASSWORD_REQUIRED_CHALLENGE = 'NEW_PASSWORD_REQUIRED'
PASSWORD_VERIFIER_CHALLENGE = 'PASSWORD_VERIFIER'
session_storage.storage['pool_id'] = "us-east-1_ZxGPPth1x"
session_storage.storage['client_id'] = "4e85b2hvbu13i5k6jmvm9e8v3o"
_typing_timer = ""
_report_timer = ""


def get_current_language():
    current_host = window.location.hostname
    lang_selection = current_host.split("qrcertify.lynkfast.com")[0][:-1]
    if lang_selection:
        return lang_selection
    else:
        return "en"


def load_page(new_page, old_page="/"):
    new_page_main = new_page.split("/")[0]
    new_page_section = new_page.split("/")[1]
    if old_page == "/" or old_page == "loading/loading":
        old_main = document['loading_main']
        new_main = document['{}_main'.format(new_page_main)]
        new_section = document['{}_container'.format(new_page_section)]
        old_main.attrs['class'] = "main_passive"
        new_main.attrs['class'] = "main_active"
        new_section.attrs['class'] = "div_active"
        old_page_main = "loading"
        old_page_section = "loading"
    elif new_page == "loading/loading":
        old_page_main = old_page.split("/")[0]
        old_page_section = old_page.split("/")[1]
        old_main = document['{}_main'.format(old_page_main)]
        old_section = document['{}_container'.format(old_page_section)]
        new_main = document['loading_main']
        old_main.attrs['class'] = "main_passive"
        old_section.attrs['class'] = "div_passive"
        new_main.attrs['class'] = "main_active"
    else:
        old_page_main = old_page.split("/")[0]
        old_page_section = old_page.split("/")[1]
        old_main = document['{}_main'.format(old_page_main)]
        old_section = document['{}_container'.format(old_page_section)]
        new_main = document['{}_main'.format(new_page_main)]
        new_section = document['{}_container'.format(new_page_section)]
        old_main.attrs['class'] = "main_passive"
        old_section.attrs['class'] = "div_passive"
        new_main.attrs['class'] = "main_active"
        new_section.attrs['class'] = "div_active"
    window.history.pushState(new_page, "", "/{}".format(new_page))
    session_storage.storage['current_page'] = new_page


def page_navigator(event):
    old_page = session_storage.storage['current_page']
    load_page(event.state, old_page)


@bind(document['logout'], "click")
def logout(event):
    def logout_user_callback(request):
        del local_storage.storage['AuthenticationTime']
        del local_storage.storage['RefreshToken']
        del session_storage.storage['AuthenticationStatus']
        del session_storage.storage['AccessToken']
        del session_storage.storage['AuthenticationTime']
        del session_storage.storage['IdToken']
        del session_storage.storage['SALT']
        del session_storage.storage['SECRET_BLOCK']
        del session_storage.storage['SRP_B']
        del session_storage.storage['USERNAME']
        del session_storage.storage['USER_ID_FOR_SRP']
        load_page("authentication/login", "loading/loading")

    old_page = session_storage.storage['current_page']
    access_token = session_storage.storage['AccessToken']
    ajax.post(
        "https://cognito-idp.us-east-1.amazonaws.com/",
        blocking=False,
        headers={
            "X-Amz-Target": "AWSCognitoIdentityProviderService.GlobalSignOut",
            "Content-Type": "application/x-amz-json-1.1"
        },
        data=json.dumps(
            {
                "AccessToken": access_token
            }
        ),
        oncomplete=logout_user_callback
    )
    load_page("loading/loading", old_page)



@bind(document['app_logo_container'], "click")
def navigate_page_back(event):
    document['upload_report'].attrs['style'] = "display:flex;"
    document['add_bulk'].attrs['style'] = "display:flex;"
    document['add_cable_to_bulk'].attrs['style'] = "display:none;"
    old_page = session_storage.storage['current_page']
    load_page("application/dashboard", old_page)


@bind(document['upload_report'], "click")
def navigate_upload_report(event):
    document['upload_report'].attrs['style'] = "display:none;"
    document['add_bulk'].attrs['style'] = "display:none;"
    document['add_cable_to_bulk'].attrs['style'] = "display:none;"
    old_page = session_storage.storage['current_page']
    load_page("application/reportprocessing", old_page)


@bind(document['report_file_input'], "change")
def activate_processing_button(file_event):
    current_language = get_current_language()

    def upload_report_processing(process_event):
        upload_button = process_event.target
        upload_button.attrs['class'] = "button_passive"
        if current_language == "en":
            upload_button.text = "Processing.."
        elif current_language == "tr":
            upload_button.text = "İşleniyor.."
        elif current_language == "cmn":
            upload_button.text = "处理中.."
        elif current_language == "yue":
            upload_button.text = "處理中.."
        upload_button.unbind("click")
        url_to_upload = None

        def normalize_button():
            # Failsafe: clear the tracking interval if it exists
            global track_progress_timer
            try:
                timer.clear_interval(track_progress_timer)
            except:
                pass
            upload_button.attrs['class'] = "button_passive"
            if current_language == "en":
                upload_button.text = "Upload Report"
                document.select_one("#report_processing_form h2").text = "Upload Report"
            elif current_language == "tr":
                upload_button.text = "Raporu Yükleyin"
                document.select_one("#report_processing_form h2").text = "Raporu Yükleyin"
            elif current_language == "cmn":
                upload_button.text = "上传报告"
                document.select_one("#report_processing_form h2").text = "上传报告"
            elif current_language == "yue":
                upload_button.text = "上傳報告"
                document.select_one("#report_processing_form h2").text = "上傳報告"
            document['process_report_progress'].attrs['value'] = 0
            timer.set_timeout(load_page, 1000, "application/dashboard", "application/reportprocessing")

        def one_time_url_callback(request):
            if request.status == 200:
                global url_to_upload
                one_time_url = request.json['link']
                url_to_upload = one_time_url
                upload_id = request.json['upload_id']
                session_storage.storage['report_processing_upload_id'] = upload_id
            else:
                raise Exception("Error! Please try again.")

        def one_time_url_request():
            ajax.get(
                "https://api.qrcertify.lynkfast.com/cable",
                headers={
                    "Authorization": session_storage.storage['IdToken']
                },
                blocking=True,
                oncomplete=one_time_url_callback
            )

        def file_upload_callback(request):
            if request.target.status == 200:
                document['process_report_progress'].attrs['value'] = 0
                if current_language == "en":
                    document.select_one("#report_processing_form h2").text = "Upload Completed"
                elif current_language == "tr":
                    document.select_one("#report_processing_form h2").text = "Yükleme Tamamlandı"
                elif current_language == "cmn":
                    document.select_one("#report_processing_form h2").text = "上传完成"
                elif current_language == "yue":
                    document.select_one("#report_processing_form h2").text = "上傳完成"
                request.target.upload.unbind("progress", progress_callback)
                request.target.unbind("load", file_upload_callback)
            else:
                upload_button.attrs['class'] = "button_error"
                if current_language == "en":
                    upload_button.text = "Error!"
                elif current_language == "tr":
                    upload_button.text = "Hata!"
                elif current_language == "cmn":
                    upload_button.text = "错误！"
                elif current_language == "yue":
                    upload_button.text = "錯誤！"

            file_event.target.value = ""
            timer.set_timeout(normalize_button, 3000)

        def progress_callback(progress_event):
            if progress_event.lengthComputable:
                document['process_report_progress'].attrs['max'] = progress_event.total
                document['process_report_progress'].attrs['value'] = progress_event.loaded
            else:
                print("Length not computable")

        def upload_file(file_to_upload):
            global url_to_upload
            req = window.XMLHttpRequest.new()
            req.open('PUT', url_to_upload, True)
            req.setRequestHeader("Content-Type", "application/pdf")
            req.setRequestHeader("Content-Disposition", 'attachment; filename="fileupload.pdf"')
            req.upload.bind("progress", progress_callback)
            req.bind("load", file_upload_callback)
            req.send(file_to_upload)

        one_time_url_request()
        file_to_be_uploaded = file_event.target.files[0]
        upload_file(file_to_be_uploaded)

    form_submit_button = document['process_report_button']
    file_event.stopPropagation()
    file_event.preventDefault()
    assert len(file_event.target.files) == 1, "No files selected"
    form_submit_button.bind("click", upload_report_processing)
    form_submit_button.attrs['class'] = "button_active"


@bind(document['report_file_input'], "cancel")
def cancel_report_upload(event):
    form_submit_button = document['process_report_button']
    form_submit_button.unbind("click")
    form_submit_button.attrs['class'] = "button_passive"

@bind(document['assessment_print'], "click")
@bind(document['assessment_label_print'], "click")
def print_assessment(event):
    """Handle printing for QR code labels or compact text labels.

    When triggered from `assessment_print` this behaves like the original
    QR label print (printing `#assessment_output`). When triggered from
    `assessment_label_print` it builds a temporary 1x2 table containing
    concatenated cable id fragments and prints it as a 32x9mm label.
    """
    # Used to track temporary DOM created for label printing so we can
    # clean it up once the browser print dialog is closed.
    label_table = None

    try:
        # Ensure the target section is visible before any print logic
        try:
            document['assessment_output'].style.display = ''
        except Exception:
            pass

        # Determine which button triggered the event
        source_id = ""
        try:
            source_id = getattr(event.target, 'id', '') or getattr(event.currentTarget, 'id', '')
        except Exception:
            try:
                source_id = event.target.id
            except Exception:
                source_id = ""

        is_label_print = (source_id == 'assessment_label_print')

        # Build print CSS and (for label printing) a temporary DOM
        if is_label_print:
            # Helper to build two-line label text from bold spans
            # Line 1: first fragment followed by a dash (e.g. "ABC-")
            # Line 2: remaining fragments joined with dashes (e.g. "123-XYZ")
            def build_label_lines(p_id):
                try:
                    p_elem = document[p_id]
                except Exception:
                    return ["", ""]

                try:
                    spans = p_elem.select('.font-roboto-black')
                except Exception:
                    spans = []

                parts = []
                for span in spans:
                    try:
                        txt = span.text.strip()
                    except Exception:
                        txt = ""
                    if txt:
                        parts.append(txt)

                if parts:
                    if len(parts) == 1:
                        # Only a single fragment available
                        return [parts[0], ""]

                    first_line = parts[0] + "-"
                    second_line = "-".join(parts[1:])
                    return [first_line, second_line]

                # Fallback: derive two lines from full paragraph text
                try:
                    full_text = p_elem.text.strip()
                except Exception:
                    return ["", ""]

                if not full_text:
                    return ["", ""]

                if "-" in full_text:
                    before, after = full_text.split("-", 1)
                    return [before + "-", after]

                # No dash present; put everything on the first line
                return [full_text, ""]

            id1_line1, id1_line2 = build_label_lines('assessment_output_id_1')
            id2_line1, id2_line2 = build_label_lines('assessment_output_id_2')

            # Create temporary 1x2 label table inside existing assessment_output
            try:
                assessment_output = document['assessment_output']
            except Exception:
                assessment_output = None

            if assessment_output is not None:
                label_table = html.TABLE(Id="assessment_label_table")
                row = html.TR()

                cell1 = html.TD()
                cell2 = html.TD()

                # First ID: two lines (first fragment + dash, remaining fragments)
                if id1_line1 or id1_line2:
                    cell1 <= html.SPAN(id1_line1, Class="font-roboto-black")
                    if id1_line2:
                        cell1 <= html.BR()
                        cell1 <= html.SPAN(id1_line2, Class="font-roboto-black")

                # Second ID: two lines (first fragment + dash, remaining fragments)
                if id2_line1 or id2_line2:
                    cell2 <= html.SPAN(id2_line1, Class="font-roboto-black")
                    if id2_line2:
                        cell2 <= html.BR()
                        cell2 <= html.SPAN(id2_line2, Class="font-roboto-black")

                row <= cell1
                row <= cell2
                label_table <= row

                # Attach label table to the existing assessment_output div
                assessment_output <= label_table

            # Label-specific print stylesheet targeting assessment_output
            print_css = """
                        @media print {
                            @page {
                                size: 32mm 9mm;
                                margin: 0 !important;
                            }

                            html, body {
                                width: 32mm !important;
                                height: 9mm !important;
                                margin: 0 !important;
                                padding: 0 !important;
                                overflow: hidden !important;
                            }

                            body {
                                margin: 0 !important;
                                padding: 0 !important;
                            }

                            body * {
                                visibility: hidden !important;
                            }

                            #assessment_output, #assessment_output * {
                                visibility: visible !important;
                            }

                            #assessment_output {
                                transform: scale(1.50);
                                transform-origin: top left;
                                position: fixed !important;
                                left: 0 !important;
                                top: 0 !important;
                                width: 32mm !important;
                                height: 9mm !important;
                                margin: 0 !important;
                                overflow: hidden !important;
                                box-sizing: border-box !important;
                                display: flex !important;
                                align-items: center !important;
                                justify-content: center !important;
                                page-break-inside: avoid !important;
                                page-break-after: avoid !important;
                            }

                            /* Hide original QR layout table during label printing */
                            #assessment_output_table {
                                display: none !important;
                            }

                            #assessment_label_table {
                                width: 100% !important;
                                height: 100% !important;
                                border-collapse: collapse !important;
                                border: 0.2mm dashed #000000 !important;
                            }

                            #assessment_label_table tr {
                                height: 100% !important;
                            }

                            #assessment_label_table td {
                                text-align: center !important;
                                vertical-align: middle !important;
                                font-size: 4mm !important;
                                line-height: 4.1mm !important;
                                color: #000000 !important;
                                padding: 0 !important;
                                border: none !important;
                            }

                            /* Solid vertical border between the two label columns */
                            #assessment_label_table td:first-child {
                                border-right: 1mm solid #000000 !important;
                            }
                        } \
                        """
        else:
            # Original QR print stylesheet targeting assessment_output
            print_css = """
                        @media print {
                            @page {
                                size: 32mm 20mm;
                                margin: 0;
                            }

                            html, body {
                                width: 32mm !important;
                                height: 20mm !important;
                                margin: 0 !important;
                                padding: 0 !important;
                                overflow: hidden !important;
                            }

                            body {
                                margin: 0 !important;
                                padding: 0 !important;
                            }

                            body * {
                                visibility: hidden !important;
                            }

                            #assessment_output, #assessment_output * {
                                visibility: visible !important;
                            }

                            #assessment_output {
                                transform: scale(1.50);
                                transform-origin: top left;
                                position: fixed !important;
                                left: 0 !important;
                                top: 0 !important;
                                width: 32mm !important;
                                height: 20mm !important;
                                margin: 0 !important;
                                overflow: hidden !important;
                                box-sizing: border-box !important;
                                page-break-inside: avoid !important;
                                page-break-after: avoid !important;
                            }

                            /* Printable area elements */
                            #assessment_output_table {
                                display: table !important;
                                border-width: 0.1mm !important;
                                padding: 0.1mm !important;
                            }

                            #assessment_output_table > tbody {
                                display: inline !important;
                            }

                            /* First row of the table */
                            #assessment_output_table > tr:first-child,
                            #assessment_output_table > tbody > tr:first-child {
                                width: 31.68mm !important;
                                height: 15.6mm !important;
                                margin: 0 !important;
                                padding: 0 !important;
                                display: flex !important;
                            }

                            #assessment_output_table > tr:first-child > td,
                            #assessment_output_table > tbody > tr:first-child > td {
                                border-right-width: 0.2mm !important;
                                width: 15.6mm !important;
                                height: 15.6mm !important;
                            }

                            /* QR containers */
                            #assessment_output_qr_1,
                            #assessment_output_qr_2 {
                                width: 14mm !important;
                                height: 14mm !important;
                                margin: 0.9mm !important;
                            }

                            #assessment_output_qr_1 > img,
                            #assessment_output_qr_2 > img {
                                width: 14mm !important;
                                height: 14mm !important;
                                margin: 0 !important;
                            }

                            /* Second row of the table */
                            #assessment_output_table > tr:nth-child(2),
                            #assessment_output_table > tbody > tr:nth-child(2) {
                                width: 31.68mm !important;
                                height: 3.98mm !important;
                                margin: 0 !important;
                                padding: 0 !important;
                                display: flex !important;
                            }

                            #assessment_output_table > tr:nth-child(2) > td,
                            #assessment_output_table > tbody > tr:nth-child(2) > td {
                                height: 3.98mm !important;
                                width: 15.6mm !important;
                                border-right-width: 0.2mm !important;
                            }

                            /* IDs font sizes */
                            #assessment_output_id_1,
                            #assessment_output_id_2 {
                                font-size: 1.45mm !important;
                            }
                        } \
                        """

        style_tag = html.STYLE(print_css)
        document <= style_tag

        # Ensure cleanup after printing
        def after_print(ev):
            try:
                style_tag.remove()
            except Exception:
                pass

            # Remove temporary label content after printing
            if label_table is not None:
                try:
                    label_table.remove()
                except Exception:
                    pass

            try:
                window.unbind("afterprint", after_print)
            except Exception:
                pass

        window.bind("afterprint", after_print)
        window.print()
    except Exception as _e:
        print(_e)

@bind(document['assessment_print_page'], "click")
def activate_print_page(event):
    document['assessment_output_qr_1'].clear()
    document['assessment_output_qr_2'].clear()
    document['assessment_output_id_1'].clear()
    document['assessment_output_id_2'].clear()
    document['assessment_info_container']['style'] = "display:none;"
    document['assessment_qr_code_container']['style'] = "display:none;"
    document['assessment_button_container']['style'] = "display:none;"
    document['assessment_output']['style'] = ""
    document['assessment_output_buttons']['style'] = ""
    try:
        qr_url_1_value = session_storage.storage['qr_url_1']
        qr_id_1 = session_storage.storage['cable_id_1']
        generate_qr_code(qr_url_1_value, document['assessment_output_qr_1'], "#000000")
        part1_a = qr_id_1.split("-")[0]
        part1_bold = qr_id_1.split("-")[1]
        part1_rest = "-".join(qr_id_1.split("|")[0].split("-")[2:])
        part1_before_last4 = qr_id_1.split("|")[-1][:-4]
        part1_last4 = qr_id_1.split("|")[-1][-4:]
        p1 = document['assessment_output_id_1']
        p1 <= html.SPAN(part1_a + "-", Class="")
        p1 <= html.SPAN(part1_bold, Class="font-roboto-black")
        p1 <= html.SPAN("-" + part1_rest + "|", Class="")
        p1 <= html.SPAN(part1_before_last4, Class="")
        p1 <= html.SPAN(part1_last4, Class="font-roboto-black")
    except:
        print("Cable 1 is not available.")
    try:
        qr_url_2_value = session_storage.storage['qr_url_2']
        qr_id_2 = session_storage.storage['cable_id_2']
        generate_qr_code(qr_url_2_value, document['assessment_output_qr_2'],"#000000")
        part2_a = qr_id_2.split("-")[0]
        part2_bold = qr_id_2.split("-")[1]
        part2_rest = "-".join(qr_id_2.split("|")[0].split("-")[2:])
        part2_before_last4 = qr_id_2.split("|")[-1][:-4]
        part2_last4 = qr_id_2.split("|")[-1][-4:]
        p2 = document['assessment_output_id_2']
        p2 <= html.SPAN(part2_a + "-", Class="")
        p2 <= html.SPAN(part2_bold, Class="font-roboto-black")
        p2 <= html.SPAN("-" + part2_rest + "|", Class="")
        p2 <= html.SPAN(part2_before_last4, Class="")
        p2 <= html.SPAN(part2_last4, Class="font-roboto-black")
    except:
        print("Cable 2 is not available.")

@bind(document['assessment_passed'], "click")
def assessment_pass(event):
    current_language = get_current_language()
    target_button = event.target
    header_text = document.select_one("#assessment_button_container h1")
    cable_bulk = target_button.attrs["cable_bulk"]
    cable_id = target_button.attrs["cable_id"]
    cable_number = target_button.attrs["cable_number"]
    bulk_count = target_button.attrs["cable_count"]

    def callback(request):
        if request.status == 200:
            if current_language == "en":
                header_text.text = "Test Result (Success)"
            elif current_language == "tr":
                header_text.text = "Test Sonucu (Başarılı)"
            elif current_language == "cmn":
                header_text.text = "测试结果 (成功)"
            elif current_language == "yue":
                header_text.text = "測試結果 (成功)"
            qr_url = request.json['qr_response']
            _cable_id = request.json['cable_id']
            # Store qr_url in session storage with key qr_url_{cable_number}
            try:
                session_storage.storage[f'qr_url_{cable_number}'] = qr_url
                session_storage.storage[f'cable_id_{cable_number}'] = _cable_id
            except Exception as _e:
                # Brython session_storage may raise if unavailable; ignore silently
                print(f"Error storing qr_url in session storage: {_e}")
            generate_qr_code(_cable_id, document['assessment_qr_code'], "#007F42")
            if int(cable_number) == int(bulk_count):
                document['assessment_back_to_list'].attrs['value'] = cable_bulk
                document['assessment_print_page']['style'] = ""
            else:
                document['assessment_next_page'].attrs['cable_bulk'] = cable_bulk
                document['assessment_next_page'].attrs['cable_id'] = cable_id
                document['assessment_next_page'].attrs['cable_number'] = int(cable_number) + 1
                document['assessment_next_page'].attrs['cable_count'] = bulk_count
                document['assessment_next_page']['style'] = ""
        else:
            if current_language == "en":
                header_text.text = "Error!"
            elif current_language == "tr":
                header_text.text = "Hata!"
            elif current_language == "cmn":
                header_text.text = "错误！"
            elif current_language == "yue":
                header_text.text = "錯誤！"
            document['assessment_passed']['style'] = ""
            document['assessment_failed']['style'] = ""

    document['assessment_passed'].style = "display:none;"
    document['assessment_failed'].style = "display:none;"
    ajax.post(
        "https://api.qrcertify.lynkfast.com/cable",
        headers={
            "Authorization": session_storage.storage['IdToken']
        },
        data=json.dumps(
            {
                "test_result": "SUCCESS",
                "cable_bulk": cable_bulk,
                "cable_id": cable_id,
                "cable_no": cable_number
            }
        ),
        oncomplete=callback
    )
    document['assessment_qr_code'].attach(
        html.DIV(
            id="loading_logo_inbound",
            style="margin-left: 18.75rem;margin-top: 18.75rem;"
        )
    )


def generate_qr_code(qr_url, _doc, color):
    _doc.clear()
    qr_code_library = window.QRCode
    qr_code_library.new(
        _doc,
        {
            "text": qr_url,
            "width": 750,
            "height": 750,
            "colorDark": color,
            "colorLight": "#FFFFFF",
            "borderColor": "#00000",
            "backgroundColor": "#FFFFFF",
            "imageScale": 0.5,
            "imageBorderWidth": 6,
            "html": "true",
            "inVersion": "true",
            "pixelSize": 15,
            "quietZone": 10,
            "errorCorrection": "L"
        }
    )


@bind(document['assessment_failed'], "click")
def assessment_fail(event):
    current_language = get_current_language()
    target_button = event.target
    header_text = document.select_one("#assessment_button_container h1")
    cable_bulk = target_button.attrs["cable_bulk"]
    cable_id = target_button.attrs["cable_id"]
    cable_number = target_button.attrs["cable_number"]
    bulk_count = target_button.attrs["cable_count"]

    def callback(request):
        if request.status == 202:
            if current_language == "en":
                header_text.text = "Test Result (Failed)"
            elif current_language == "tr":
                header_text.text = "Test Sonucu (Başarısız)"
            elif current_language == "cmn":
                header_text.text = "测试结果 (失败)"
            elif current_language == "yue":
                header_text.text = "測試結果 (失敗)"
            if int(cable_number) == int(bulk_count):
                document['assessment_back_to_list'].attrs['value'] = cable_bulk
                document['assessment_print_page']['style'] = ""
            else:
                document['assessment_next_page'].attrs['cable_bulk'] = cable_bulk
                document['assessment_next_page'].attrs['cable_id'] = cable_id
                document['assessment_next_page'].attrs['cable_number'] = int(cable_number) + 1
                document['assessment_next_page'].attrs['cable_count'] = bulk_count
                document['assessment_next_page']['style'] = ""
        else:
            if current_language == "en":
                header_text.text = "Error!"
            elif current_language == "tr":
                header_text.text = "Hata!"
            elif current_language == "cmn":
                header_text.text = "错误！"
            elif current_language == "yue":
                header_text.text = "錯誤！"
            document['assessment_passed']['style'] = ""
            document['assessment_failed']['style'] = ""

    document['assessment_passed'].style = "display:none;"
    document['assessment_failed'].style = "display:none;"
    ajax.post(
        "https://api.qrcertify.lynkfast.com/cable",
        headers={
            "Authorization": session_storage.storage['IdToken']
        },
        data=json.dumps(
            {
                "test_result": "FAILED",
                "cable_bulk": cable_bulk,
                "cable_id": cable_id,
                "cable_no": cable_number
            }
        ),
        oncomplete=callback
    )


@bind(document['assessment_next_page'], "click")
def switch_to_next_page(event):
    current_language = get_current_language()
    target_button = event.target
    target_button.attrs['style'] = "display:none;"
    information_header = document.select_one("#assessment_info_container h1")
    cable_bulk = target_button.attrs["cable_bulk"]
    cable_id = target_button.attrs["cable_id"]
    cable_number = target_button.attrs["cable_number"]
    cable_count = target_button.attrs["cable_count"]
    information_header.text = "Cable {} {}/{}".format(cable_id, cable_number, cable_count)
    header_text = document.select_one("#assessment_button_container h1")
    if current_language == "en":
        header_text.text = "Test Result"
    elif current_language == "tr":
        header_text.text = "Test Sonucu"
    elif current_language == "cmn":
        header_text.text = "测试结果"
    elif current_language == "yue":
        header_text.text = "測試結果"
    pass_button = document['assessment_passed']
    pass_button.attrs['cable_bulk'] = cable_bulk
    pass_button.attrs['cable_id'] = cable_id
    pass_button.attrs['cable_number'] = cable_number
    pass_button.attrs['cable_count'] = str(cable_count)
    pass_button.attrs['style'] = ""
    fail_button = document['assessment_failed']
    fail_button.attrs['cable_bulk'] = cable_bulk
    fail_button.attrs['cable_id'] = cable_id
    fail_button.attrs['cable_number'] = cable_number
    fail_button.attrs['cable_count'] = str(cable_count)
    fail_button.attrs['style'] = ""
    document['assessment_qr_code'].clear()


@bind(document['assessment_back_to_list'], "click")
def get_back_to_cable_list(event):
    document['upload_report'].attrs['style'] = "display:none;"
    document['add_bulk'].attrs['style'] = "display:none;"
    document['add_cable_to_bulk'].attrs['style'] = "display:flex;"
    document['add_cable_to_bulk'].attrs['value'] = event.target.attrs['value']
    get_cable_list(event.target.attrs['value'])
    load_page("loading/loading", "application/assessment")


def open_assessment_screen(event):
    current_language = get_current_language()
    aforementioned_tr = event.target.parent
    _cable_id = aforementioned_tr.attrs['cable_id']
    cable_id = _cable_id.split(" ")[-1]
    cable_bulk = aforementioned_tr.attrs['cable_bulk']

    # Cleanup any existing qr_url_* keys in session storage when opening assessment screen
    try:
        # session_storage.storage is dict-like; iterate over a list copy of keys
        for k in list(session_storage.storage.keys()):
            if str(k).startswith('qr_url_'):
                try:
                    del session_storage.storage[k]
                except Exception:
                    print("Could not delete key {} from session storage".format(k))
    except Exception:
        print("Could not access session storage")

    def callback(request):
        if request.status == 200:
            response = request.json
            bulk_count = response['bulk_count']
            pin_count = response['pin_count']
            length = response['length']
            cable_category = response['cable_category']
            cable_type = response['cable_type']
            cable_color = response['cable_color']
            pin_configuration = response['pin_configuration']
            made_in = response['made_in']
            information_container = document['assessment_info_container']
            information_container.clear()
            header = html.H1("Cable {} 1/{}".format(cable_id, bulk_count))
            information_container.attach(header)
            if current_language == "en":
                pin_count_text = "Pin Count:"
                length_text = "Length:"
                cable_category_text = "Category:"
                cable_type_text = "Type:"
                cable_color_text = "Color:"
                pin_configuration_text = "Configuration:"
                made_in_text = "Made in:"
            elif current_language == "tr":
                pin_count_text = "Pin Sayısı:"
                length_text = "Uzunluk:"
                cable_category_text = "Kategori:"
                cable_type_text = "Tip:"
                cable_color_text = "Renk:"
                pin_configuration_text = "Konfigürasyon:"
                made_in_text = "Üretim Yeri:"
            elif current_language == "cmn":
                pin_count_text = "针数:"
                length_text = "针数:"
                cable_category_text = "电缆类别:"
                cable_type_text = "类型:"
                cable_color_text = "颜色:"
                pin_configuration_text = "配置:"
                made_in_text = "产地："
            elif current_language == "yue":
                pin_count_text = "針數"
                length_text = "針數:"
                cable_category_text = "電脷類別:"
                cable_type_text = "類型:"
                cable_color_text = "顏色:"
                pin_configuration_text = "配置:"
                made_in_text = "產地："
            information_container.attach(
                html.DL().attach(
                    html.DT(
                        pin_count_text
                    )
                    +
                    html.DD(
                        pin_count
                    )
                )
            )
            information_container.attach(
                html.DL().attach(
                    html.DT(
                        length_text
                    )
                    +
                    html.DD(
                        length
                    )
                )
            )
            information_container.attach(
                html.DL().attach(
                    html.DT(
                        cable_category_text
                    )
                    +
                    html.DD(
                        cable_category
                    )
                )
            )
            information_container.attach(
                html.DL().attach(
                    html.DT(
                        cable_type_text
                    )
                    +
                    html.DD(
                        cable_type
                    )
                )
            )
            information_container.attach(
                html.DL().attach(
                    html.DT(
                        cable_color_text
                    )
                    +
                    html.DD(
                        cable_color
                    )
                )
            )
            information_container.attach(
                html.DL().attach(
                    html.DT(
                        pin_configuration_text
                    )
                    +
                    html.DD(
                        pin_configuration
                    )
                )
            )
            information_container.attach(
                html.DL().attach(
                    html.DT(
                        made_in_text
                    )
                    +
                    html.DD(
                        made_in
                    )
                )
            )
            button_container = document['assessment_button_container']
            button_header = button_container.select_one("h1")
            if current_language == "en":
                button_header.text = "Test Result"
            elif current_language == "tr":
                button_header.text = "Test Sonucu"
            elif current_language == "cmn":
                button_header.text = "测试结果"
            elif current_language == "yue":
                button_header.text = "測試結果"
            pass_button = document['assessment_passed']
            pass_button.attrs['cable_bulk'] = cable_bulk
            pass_button.attrs['cable_id'] = cable_id
            pass_button.attrs['cable_number'] = "1"
            pass_button.attrs['cable_count'] = str(bulk_count)
            pass_button.attrs['style'] = ""
            fail_button = document['assessment_failed']
            fail_button.attrs['cable_bulk'] = cable_bulk
            fail_button.attrs['cable_id'] = cable_id
            fail_button.attrs['cable_number'] = "1"
            fail_button.attrs['cable_count'] = str(bulk_count)
            fail_button.attrs['style'] = ""
            document['assessment_next_page'].attrs['style'] = "display:none;"
            document['assessment_print_page'].attrs['style'] = "display:none;"
            document['assessment_output'].attrs['style'] = "display:none;"
            document['assessment_output_buttons'].attrs['style'] = "display:none;"
            document['assessment_info_container'].attrs['style'] = ""
            document['assessment_qr_code_container'].attrs['style'] = ""
            document['assessment_button_container'].attrs['style'] = ""
            document['add_cable_to_bulk'].attrs['style'] = "display:none;"
            document['assessment_qr_code'].clear()
            load_page("application/assessment", "loading/loading")
        else:
            load_page("application/cablelist", "loading/loading")

    document['assessment_qr_code'].clear()
    ajax.get(
        "https://api.qrcertify.lynkfast.com/cable/{}_{}".format(cable_bulk, cable_id),
        headers={
            "Authorization": session_storage.storage['IdToken']
        },
        oncomplete=callback
    )
    load_page("loading/loading", "application/cablelist")


def get_cable_list(cable_name):
    def callback(request):
        load_page("application/cablelist", "loading/loading")

        def natural_key(s: str):
            return [int(part) if part.isdigit() else part.lower()
                    for part in re.split(r'(\d+)', s)]

        if request.status == 200:
            _cable_list = request.json['cable_list']
            cable_list = sorted(_cable_list, key=lambda d: natural_key(d["name"]))
            bundle_count = request.json['bundle_count']
            cable_list_container = document['cablelist_container']
            new_table = html.TABLE()
            first_row_container = html.THEAD()
            first_row = html.TR()
            first_row.attach(html.TH(""))
            for i in range(bundle_count):
                first_row.attach(
                    html.TH("#{}".format(i + 1))
                )
            first_row_container.attach(first_row)
            new_table.attach(first_row_container)
            body_container = html.TBODY()
            for cable in cable_list:
                row = html.TR()
                row.attrs['cable_id'] = cable['name']
                row.attrs['cable_bulk'] = cable_name
                row.bind("dblclick", open_assessment_screen)
                row.attach(
                    html.TD(cable['name'])
                )
                for bundle in cable['results']:
                    row.attach(
                        html.TD(bundle, Class="cable_list_{}".format(bundle.lower()))
                    )
                body_container.attach(row)
            new_table.attach(body_container)
            cable_list_container.clear()
            cable_list_container.attach(new_table)
        else:
            cable_list_container = document['cablelist_container']
            cable_list_container.clear()
            cable_list_container.attach(html.H1("Error!"))

    ajax.get(
        "https://api.qrcertify.lynkfast.com/cablelist/{}".format(cable_name),
        headers={
            "Authorization": session_storage.storage['IdToken']
        },
        oncomplete=callback
    )


@bind(document['add_cable_to_bulk'], "click")
def add_new_cable_prompt(event):
    current_language = get_current_language()
    if current_language == "en":
        entry_dialogue_text = "How many cable bulks do you want to add?"
    elif current_language == "tr":
        entry_dialogue_text = "Kaç kablo tomarı eklemek istiyorsunuz?"
    elif current_language == "cmn":
        entry_dialogue_text = "你想添加多少电缆批量？"
    elif current_language == "yue":
        entry_dialogue_text = "你想添加多少電纜批量？"
    entry_dialogue = EntryDialog(
        entry_dialogue_text,
        default_css=False
    )
    entry_dialogue.bind("entry", add_new_cable)
    entry_dialogue.close_button.bind("click", re_bind_new_cable_prompt)
    entry_dialogue.ok_button.bind("click", re_bind_new_cable_prompt)
    entry_dialogue.cancel_button.bind("click", re_bind_new_cable_prompt)
    document['add_cable_to_bulk'].unbind("click")


def re_bind_new_cable_prompt(event):
    document['add_cable_to_bulk'].bind("click", add_new_cable_prompt)


def add_new_cable(event):
    count = event.target.value
    button_value = document['add_cable_to_bulk'].attrs['value']

    def callback(request):
        if request.status == 200:
            get_cable_list(button_value)
        else:
            get_cable_list(button_value)
            print("Error! Please try again.")

    try:
        count = int(count)
        event.target.close()
        ajax.post(
            "https://api.qrcertify.lynkfast.com/cablelist/{}".format(button_value),
            headers={
                "Authorization": session_storage.storage['IdToken']
            },
            data=json.dumps(
                {
                    "count": count
                }
            ),
            oncomplete=callback
        )
        load_page("loading/loading", "application/cablelist")
        document['add_cable_to_bulk'].bind("click", add_new_cable_prompt)
    except:
        event.target.entry.value = ""


def insert_content_inline_loading(document_id):
    loading_span = document.select_one("#{} .content_loading_passive".format(document_id))
    loading_span.attrs['class'] = "content_loading_active"


def remove_content_inline_loading(document_id):
    loading_span = document.select_one("#{} .content_loading_active".format(document_id))
    loading_span.attrs['class'] = "content_loading_passive"


@bind(document['cp_button'], "click")
def change_your_password(event):
    current_language = get_current_language()

    def cp_successful_login():
        load_page("loading/loading", "authentication/cp")
        login_handler()

    def cp_button_normalization():
        cp_button = document['cp_button']
        if current_language == "en":
            cp_button.text = "Log in"
        elif current_language == "tr":
            cp_button.text = "Giriş"
        elif current_language == "cmn":
            cp_button.text = "登录"
        elif current_language == "yue":
            cp_button.text = "登入"
        cp_button.attrs['class'] = "button_active"
        cp_button.bind("click", change_your_password)

    def set_new_password(password):
        cp_button = document['cp_button']
        client_id = session_storage.storage['client_id']
        session = session_storage.storage['NPRSession']
        username = session_storage.storage['USERNAME']
        cp_body = {
            "ClientId": client_id,
            "ChallengeName": "NEW_PASSWORD_REQUIRED",
            "Session": session,
            "ChallengeResponses": {
                "NEW_PASSWORD": password,
                "USERNAME": username
            }
        }
        ajax.post(
            "https://cognito-idp.us-east-1.amazonaws.com/",
            blocking=True,
            headers={
                "X-Amz-Target": "AWSCognitoIdentityProviderService.RespondToAuthChallenge",
                "Content-Type": "application/x-amz-json-1.1"
            },
            data=JSON.stringify(cp_body),
            oncomplete=password_verifier_callback
        )
        cp_response = session_storage.storage['AuthenticationStatus']
        if cp_response == "Successful":
            if current_language == "en":
                cp_button.text = "Successful!"
            elif current_language == "tr":
                cp_button.text = "Başarılı!"
            elif current_language == "cmn":
                cp_button.text = "成功！"
            elif current_language == "yue":
                cp_button.text = "成功！"
            cp_button.attrs['class'] = "button_success"
            cp_button.unbind("click")
            timer.set_timeout(cp_successful_login, 2000)
        else:
            if current_language == "en":
                cp_button.text = "Error"
            elif current_language == "tr":
                cp_button.text = "Hata!"
            elif current_language == "cmn":
                cp_button.text = "错误！"
            elif current_language == "yue":
                cp_button.text = "錯誤！"
            cp_button.attrs['class'] = "button_error"
            cp_button.unbind("click")
            timer.set_timeout(cp_button_normalization, 5000)

    cp_button = document['cp_button']
    password_1 = document['cp_new_password'].value
    password_2 = document['cp_new_password_again'].value
    if password_1 != password_2:
        if current_language == "en":
            cp_button.text = "Passwords must match!"
        elif current_language == "tr":
            cp_button.text = "Parolalar Eşleşmedi!"
        elif current_language == "cmn":
            cp_button.text = "密码必须匹配！"
        elif current_language == "yue":
            cp_button.text = "密碼必須匹配！"
        cp_button.attrs['class'] = "button_error"
        cp_button.unbind("click")
        timer.set_timeout(cp_button_normalization, 1000)
    else:
        password = document['cp_new_password'].value
        if len(password) < 8:
            if current_language == "en":
                cp_button.text = "Password too short!"
            elif current_language == "tr":
                cp_button.text = "Parolalar çok kısa!"
            elif current_language == "cmn":
                cp_button.text = "密码太短！"
            elif current_language == "yue":
                cp_button.text = "密碼太短！"
            cp_button.attrs['class'] = "button_error"
            cp_button.unbind("click")
            password_1 = ""
            password_2 = ""
            timer.set_timeout(cp_button_normalization, 1000)
        else:
            if re.match('((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*]))', password):
                if current_language == "en":
                    cp_button.text = "Processing.."
                elif current_language == "tr":
                    cp_button.text = "İşleniyor.."
                elif current_language == "cmn":
                    cp_button.text = "处理中.."
                elif current_language == "yue":
                    cp_button.text = "處理中.."
                cp_button.attrs['class'] = "button_passive"
                cp_button.unbind("click")
                set_new_password(password)
            else:
                if current_language == "en":
                    cp_button.text = "Password not strong enough!"
                elif current_language == "tr":
                    cp_button.text = "Parola yeterince güçlü değil! "
                elif current_language == "cmn":
                    cp_button.text = "密码强度不足！"
                elif current_language == "yue":
                    cp_button.text = "密碼強度不足！"
                cp_button.attrs['class'] = "button_error"
                cp_button.unbind("click")
                password_1 = ""
                password_2 = ""
                timer.set_timeout(cp_button_normalization, 1000)


@bind(document['cfyp_button'], "click")
def cfyp_set(event):
    current_language = get_current_language()

    def cfyp_button_normalization():
        cfyp_button = document['cfyp_button']
        if current_language == "en":
            cfyp_button.text = "Change Your Password"
        elif current_language == "tr":
            cfyp_button.text = "Şifreni Değiştir"
        elif current_language == "cmn":
            cfyp_button.text = "更改您的密码"
        elif current_language == "yue":
            cfyp_button.text = "更改你嘅密碼"
        cfyp_button.attrs['class'] = "button_active"
        cfyp_button.bind("click", cfyp_set)

    def cfyp_to_login():
        load_page("authentication/login", "authentication/cfyp")

    def cfyp_callback(request):
        cfyp_button = document['cfyp_button']
        if request.status == 200:
            if current_language == "en":
                cfyp_button.text = "Password Changed!"
            elif current_language == "tr":
                cfyp_button.text = "Şifre Değişti!"
            elif current_language == "cmn":
                cfyp_button.text = "密码已更改！"
            elif current_language == "yue":
                cfyp_button.text = "密碼已更改！"
            cfyp_button.attrs['class'] = "button_success"
            timer.set_timeout(cfyp_to_login, 1500)
        else:
            if current_language == "en":
                cfyp_button.text = "Password Change Error"
            elif current_language == "tr":
                cfyp_button.text = "Şifre Değişiminde Hata"
            elif current_language == "cmn":
                cfyp_button.text = "密码更改错误"
            elif current_language == "yue":
                cfyp_button.text = "密碼更改錯誤"
            cfyp_button.attrs['class'] = "button_error"
            timer.set_timeout(cfyp_button_normalization, 5000)

    def cfyp_call(otp, password):
        email_address = session_storage.storage['fyp_email']
        client_id = session_storage.storage['client_id']
        cfyp_payload = {
            "ClientId": client_id,
            "Username": email_address,
            "ConfirmationCode": otp,
            "Password": password
        }
        ajax.post(
            "https://cognito-idp.us-east-1.amazonaws.com/",
            blocking=False,
            headers={
                "X-Amz-Target": "AWSCognitoIdentityProviderService.ConfirmForgotPassword",
                "Content-Type": "application/x-amz-json-1.1"
            },
            data=JSON.stringify(cfyp_payload),
            oncomplete=cfyp_callback
        )

    cfyp_button = document['cfyp_button']
    password_1 = document['cfyp_new_password'].value
    password_2 = document['cfyp_new_password_again'].value
    if document['cfyp_code'].value:
        otp_code = document['cfyp_code'].value
        if password_1 != password_2:
            if current_language == "en":
                cfyp_button.text = "Passwords must match!"
            elif current_language == "tr":
                cfyp_button.text = "Parolalar Eşleşmedi!"
            elif current_language == "cmn":
                cfyp_button.text = "密码必须匹配！"
            elif current_language == "yue":
                cfyp_button.text = "密碼必須匹配！"
            cfyp_button.attrs['class'] = "button_error"
            cfyp_button.unbind("click")
            timer.set_timeout(cfyp_button_normalization, 1000)
        else:
            password = document['cfyp_new_password'].value
            if len(password) < 8:
                if current_language == "en":
                    cfyp_button.text = "Password too short!"
                elif current_language == "tr":
                    cfyp_button.text = "Parolalar çok kısa!"
                elif current_language == "cmn":
                    cfyp_button.text = "密码太短！"
                elif current_language == "yue":
                    cfyp_button.text = "密碼太短！"
                cfyp_button.attrs['class'] = "button_error"
                cfyp_button.unbind("click")
                password_1 = ""
                password_2 = ""
                timer.set_timeout(cfyp_button_normalization, 1000)
            else:
                if re.match('((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*]))', password):
                    if current_language == "en":
                        cfyp_button.text = "Processing.."
                    elif current_language == "tr":
                        cfyp_button.text = "İşleniyor.."
                    elif current_language == "cmn":
                        cfyp_button.text = "处理中.."
                    elif current_language == "yue":
                        cfyp_button.text = "處理中.."
                    cfyp_button.attrs['class'] = "button_passive"
                    cfyp_button.unbind("click")
                    cfyp_call(otp_code, password)
                else:
                    if current_language == "en":
                        cfyp_button.text = "Password not strong enough!"
                    elif current_language == "tr":
                        cfyp_button.text = "Parola yeterince güçlü değil! "
                    elif current_language == "cmn":
                        cfyp_button.text = "密码强度不足！"
                    elif current_language == "yue":
                        cfyp_button.text = "密碼強度不足！"
                    cfyp_button.attrs['class'] = "button_error"
                    cfyp_button.unbind("click")
                    password_1 = ""
                    password_2 = ""
                    timer.set_timeout(cfyp_button_normalization, 1000)
    else:
        if current_language == "en":
            cfyp_button.text = "OTP Cannot be Empty"
        elif current_language == "tr":
            cfyp_button.text = "Teyit Kodu Boş Kalamaz"
        elif current_language == "cmn":
            cfyp_button.text = "次性密码不能为空"
        elif current_language == "yue":
            cfyp_button.text = "次性密碼不能為空"
        cfyp_button.attrs['class'] = "button_error"
        cfyp_button.unbind("click")
        timer.set_timeout(cfyp_button_normalization, 1000)


@bind(document['fyp_submit_button'], "click")
def fyp_call(event):
    client_id = session_storage.storage['client_id']
    email_address = session_storage.storage['fyp_email']
    fyp_payload = {
        "ClientId": client_id,
        "Username": email_address
    }
    ajax.post(
        "https://cognito-idp.us-east-1.amazonaws.com/",
        blocking=False,
        headers={
            "X-Amz-Target": "AWSCognitoIdentityProviderService.ForgotPassword",
            "Content-Type": "application/x-amz-json-1.1"
        },
        data=JSON.stringify(fyp_payload)
    )
    load_page("authentication/cfyp", "authentication/fyp")


@bind(document['fyp_button'], "click")
def login_to_fyp(event):
    current_language = get_current_language()

    def login_button_normalization():
        send_button = document['login_button']
        if current_language == "en":
            send_button.text = "Log in"
        elif current_language == "tr":
            send_button.text = "Giriş"
        elif current_language == "cmn":
            send_button.text = "登录"
        elif current_language == "yue":
            send_button.text = "登入"
        send_button.attrs['class'] = "button_active"
        send_button.bind("click", login_user_button)

    email_address = document['login_username'].value
    email_regex = re.compile(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})+')
    if re.match(email_regex, email_address):
        document['fyp_content_text'].clear()
        if current_language == "en":
            document['fyp_content_text'].attach(
                "This action will send {} an email with new password!".format(email_address))
        elif current_language == "tr":
            document['fyp_content_text'].attach(
                "Bu işlem {}'a e-posta yoluyla yeni şifreyi iletecektir!".format(email_address))
        elif current_language == "cmn":
            document['fyp_content_text'].attach("此操作将向{}发送一封包含新密码的电子邮件!".format(email_address))
        elif current_language == "yue":
            document['fyp_content_text'].attach("此操作會向{}發送一封包含新密碼嘅電郵!".format(email_address))
        session_storage.storage['fyp_email'] = email_address
        load_page("authentication/fyp", "authentication/login")
    else:
        send_button = document['login_button']
        if current_language == "en":
            send_button.text = "Enter a valid Email"
        elif current_language == "tr":
            send_button.text = "Geçerli Bir E-Posta Adresi Girin"
        elif current_language == "cmn":
            send_button.text = "输入有效的电子邮件"
        elif current_language == "yue":
            send_button.text = "輸入有效的電郵"
        send_button.attrs['class'] = "button_warning"
        send_button.unbind("click")
        timer.set_timeout(login_button_normalization, 2000)


@bind(document['fyp_cancel_button'], "click")
def fyp_to_login_cancelled(event):
    load_page("authentication/login", "authentication/fyp")


def load_cable_list_page(event):
    document['upload_report'].attrs['style'] = "display:none;"
    document['add_bulk'].attrs['style'] = "display:none;"
    document['add_cable_to_bulk'].attrs['style'] = "display:flex;"
    # Safely resolve the value attribute from the event target or its parents/currentTarget
    resolved_value = None
    try:
        resolved_value = event.target.attrs['value']
    except Exception:
        # Try the element the handler was bound to (usually the DIV)
        try:
            resolved_value = event.currentTarget.attrs['value']
        except Exception:
            # Walk up the DOM tree to find a parent with a 'value' attribute
            try:
                node = event.target
                # Limit traversal depth to prevent infinite loops in unexpected structures
                for _ in range(8):
                    if node is None:
                        break
                    try:
                        resolved_value = node.attrs['value']
                        if resolved_value is not None:
                            break
                    except Exception:
                        pass
                    # Brython elements expose parent
                    try:
                        node = node.parent
                    except Exception:
                        node = None
            except Exception:
                resolved_value = None
    if not resolved_value:
        return
    document['add_cable_to_bulk'].attrs['value'] = resolved_value
    get_cable_list(resolved_value)
    load_page("loading/loading", "application/dashboard")


def get_bulk_list_and_details():
    current_language = get_current_language()

    def use_key_mapping(key):
        if current_language == "en":
            pin_count_text = "Pin Count"
            length_text = "Length"
            cable_category_text = "Category"
            cable_type_text = "Type"
            cable_color_text = "Color"
            pin_configuration_text = "Configuration"
            made_in_text = "Made in"
        elif current_language == "tr":
            pin_count_text = "Pin Sayısı"
            length_text = "Uzunluk"
            cable_category_text = "Kategori"
            cable_type_text = "Tip"
            cable_color_text = "Renk"
            pin_configuration_text = "Konfigürasyon"
            made_in_text = "Üretim Yeri"
        elif current_language == "cmn":
            pin_count_text = "针数"
            length_text = "针数"
            cable_category_text = "电缆类别"
            cable_type_text = "类型"
            cable_color_text = "颜色"
            pin_configuration_text = "配置"
            made_in_text = "产地"
        elif current_language == "yue":
            pin_count_text = "針數"
            length_text = "針數"
            cable_category_text = "電脷類別"
            cable_type_text = "類型"
            cable_color_text = "顏色"
            pin_configuration_text = "配置"
            made_in_text = "產地"
        mapping = {
            "made_in": made_in_text,
            "color": cable_color_text,
            "length": length_text,
            "pin_config": pin_configuration_text,
            "material": cable_type_text,
            "pin_count": pin_count_text,
            "category": cable_category_text
        }
        return mapping[key]

    def use_value_mapping(key, value):
        if current_language == "en":
            blue_text = "Blue"
            black_text = "Black"
            grey_text = "Grey"
            white_text = "White"
        elif current_language == "tr":
            blue_text = "Mavi"
            black_text = "Siyah"
            grey_text = "Gri"
            white_text = "Beyaz"
        elif current_language == "cmn":
            blue_text = "蓝色"
            black_text = "黑色"
            grey_text = "灰色"
            white_text = "白色"
        elif current_language == "yue":
            blue_text = "藍色"
            black_text = "黑色"
            grey_text = "灰色"
            white_text = "白色"
        mapping = {
            "made_in": {
                "CN": "China"
            },
            "color": {
                "BL": blue_text,
                "WH": white_text,
                "BK": black_text,
                "GY": grey_text
            },
            "length": {
                value: value
            },
            "pin_config": {
                value: value
            },
            "material": {
                "1": "Plenum",
                "2": "Riser"
            },
            "pin_count": {
                value: value
            },
            "category": {
                "6": "CAT6",
                "6A": "CAT6A",
                "7": "CAT7"
            }
        }
        return mapping[key][value]

    def callback(request):
        if request.status == 200:
            dashboard = document['dashboard_container']
            bulk_list = request.json['bulk_list']
            content_array = []
            for bulk in bulk_list:
                storage_value = {
                    "name": bulk['name'],
                    "img_src": bulk['img_src']
                }
                for feature in bulk['features']:
                    storage_value[list(feature.keys())[0]] = list(feature.values())[0]
                local_storage.storage[bulk['name']] = json.dumps(storage_value)
                container = html.DIV(value=bulk['name'])
                container.bind("dblclick", load_cable_list_page)
                container.attach(html.H1(bulk['name']))
                container.attach(html.IMG(src=bulk['img_src']))
                dl_list = html.DL()
                for feature in bulk['features']:
                    dl_list.attach(
                        html.DT("{}:".format(use_key_mapping(list(feature.keys())[0])))
                        +
                        html.DD(use_value_mapping(list(feature.keys())[0], list(feature.values())[0]))
                    )
                container.attach(dl_list)
                edit_button = html.BUTTON(
                    "✏",
                    id="bulk_edit",
                    value=bulk['name']
                )
                edit_button.bind("click", open_bulk_edit_screen)
                container.attach(
                    edit_button
                )
                delete_button = html.BUTTON(
                    "🗑",
                    id="bulk_delete",
                    value=bulk['name']
                )
                delete_button.bind("click", open_bulk_delete_screen)
                container.attach(
                    delete_button
                )
                content_array.append(container)
            dashboard.clear()
            for items in content_array:
                dashboard.attach(items)
        else:
            dashboard = document['dashboard_container']
            dashboard.clear()
            dashboard.attach(html.H1("Error!"))

    ajax.get(
        "https://api.qrcertify.lynkfast.com/bulk",
        headers={
            "Authorization": session_storage.storage['IdToken']
        },
        oncomplete=callback
    )


@bind(document['add_bulk'], "click")
def open_bulk_add_screen(event):
    document['add_bulk'].attrs['style'] = "display:none;"
    load_page("application/addbulk", session_storage.storage['current_page'])


@bind(document['cancel_create_cable_bulk'], "click")
def cancel_create_cable_bulk(event):
    document['add_bulk'].attrs['style'] = "display:flex;"
    load_page("application/dashboard", "application/addbulk")


@bind(document['update_bulk_image'], "change")
@bind(document['create_bulk_image'], "change")
def change_background_image(event):
    def file_import_callback(file_event):
        encoded_image_value = file_event.target.result
        input_field.attrs['style'] = "background-image:url({});".format(encoded_image_value)
        input_field.attrs['value'] = encoded_image_value.split("base64,")[-1]

    input_field = event.target
    event.stopPropagation()
    event.preventDefault()
    individual_file = event.target.files[0]
    file_reader = window.FileReader.new()
    file_reader.readAsDataURL(individual_file)
    file_reader.bind("load", file_import_callback)


@bind(document['create_cable_submit'], "click")
def submit_cable_bulk(event):
    def callback(request):
        if request.status == 200:
            get_bulk_list_and_details()
            timer.set_timeout(load_page, 2500, "application/dashboard", "application/addbulk")
            timer.set_timeout(remove_content_inline_loading, 2500, "bulk_add_container")
            document['add_bulk'].attrs['style'] = "display:flex;"
            document['create_pin_count'].value = ""
            document['create_length'].value = ""
            for radio in document.select("#create_cable_radio_container input"):
                radio.checked = False
            document['create_cable_material'].selectedIndex = 0
            document['create_cable_color'].selectedIndex = 0
            document['create_cable_pin_config'].selectedIndex = 0
            document['create_cable_made_in'].selectedIndex = 0
        else:
            remove_content_inline_loading("bulk_add_container")

    pin_count = document['create_pin_count'].value
    length = document['create_length'].value
    category_checked = document.select("#create_cable_radio_container input:checked")
    material = document['create_cable_material'].options[document['create_cable_material'].selectedIndex].value
    color = document['create_cable_color'].options[document['create_cable_color'].selectedIndex].value
    pin_config = document['create_cable_pin_config'].options[document['create_cable_pin_config'].selectedIndex].value
    made_in = document['create_cable_made_in'].options[document['create_cable_made_in'].selectedIndex].value
    image_source = document['create_bulk_image'].attrs['value']

    has_error = False

    if not pin_count:
        document['create_pin_count'].style.border = "0.2rem solid #FF0000"
        document['create_pin_count'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['create_pin_count'].style.border = ""
        document['create_pin_count'].style.boxShadow = ""

    if not length:
        document['create_length'].style.border = "0.2rem solid #FF0000"
        document['create_length'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['create_length'].style.border = ""
        document['create_length'].style.boxShadow = ""

    if not category_checked:
        document['create_cable_radio_container'].style.boxShadow = "0 0 0.5rem #FF0000"
        document['create_cable_radio_container'].style.borderRadius = "0.5rem"
        has_error = True
    else:
        document['create_cable_radio_container'].style.boxShadow = ""
        document['create_cable_radio_container'].style.borderRadius = ""

    if material == "0":
        document['create_cable_material'].style.border = "0.2rem solid #FF0000"
        document['create_cable_material'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['create_cable_material'].style.border = ""
        document['create_cable_material'].style.boxShadow = ""

    if color == "00":
        document['create_cable_color'].style.border = "0.2rem solid #FF0000"
        document['create_cable_color'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['create_cable_color'].style.border = ""
        document['create_cable_color'].style.boxShadow = ""

    if pin_config == "00":
        document['create_cable_pin_config'].style.border = "0.2rem solid #FF0000"
        document['create_cable_pin_config'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['create_cable_pin_config'].style.border = ""
        document['create_cable_pin_config'].style.boxShadow = ""

    if made_in == "00":
        document['create_cable_made_in'].style.border = "0.2rem solid #FF0000"
        document['create_cable_made_in'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['create_cable_made_in'].style.border = ""
        document['create_cable_made_in'].style.boxShadow = ""

    if has_error:
        return

    ajax.post(
        "https://api.qrcertify.lynkfast.com/bulk",
        headers={
            "Authorization": session_storage.storage['IdToken']
        },
        data=json.dumps(
            {
                "pin_count": pin_count,
                "length": length,
                "category": category_checked[0].value,
                "material": material,
                "color": color,
                "pin_config": pin_config,
                "made_in": made_in,
                "image_source": image_source
            }
        ),
        oncomplete=callback
    )
    insert_content_inline_loading("bulk_add_container")


def open_bulk_edit_screen(event):
    document['add_bulk'].attrs['style'] = "display:none;"
    bulk_details = json.loads(local_storage.storage[event.target.value])
    timer.set_timeout(load_page, 500, "application/editbulk", "application/dashboard")
    document['update_bulk_image'].attrs['style'] = "background-image:url({});background-size:contain;".format(
        bulk_details['img_src'])
    document['update_bulk_image'].attrs['value'] = bulk_details['img_src'].split("base64,")[-1]
    document['update_pin_count'].value = bulk_details['pin_count']
    document['update_length'].value = bulk_details['length']
    document['update_cable_radio_cat{}'.format(bulk_details['category'])].checked = True
    document['update_cable_material'].value = bulk_details['material']
    document['update_cable_color'].value = bulk_details['color']
    document['update_cable_pin_config'].value = bulk_details['pin_config']
    document['update_cable_made_in'].value = bulk_details['made_in']
    document['update_cable_submit'].attrs['value'] = event.target.value


@bind(document['cancel_update_cable_bulk'], "click")
def cancel_update_cable_bulk(event):
    document['add_bulk'].attrs['style'] = "display:flex;"
    load_page("application/dashboard", "application/editbulk")


@bind(document['update_cable_submit'], "click")
def submit_cable_bulk_update(event):
    aio.run(
        patch_cable_bulk(
            event
        )
    )


async def patch_cable_bulk(event):
    pin_count = document['update_pin_count'].value
    length = document['update_length'].value
    category_checked = document.select("#update_cable_radio_container input:checked")
    material = document['update_cable_material'].options[document['update_cable_material'].selectedIndex].value
    color = document['update_cable_color'].options[document['update_cable_color'].selectedIndex].value
    pin_config = document['update_cable_pin_config'].options[document['update_cable_pin_config'].selectedIndex].value
    made_in = document['update_cable_made_in'].options[document['update_cable_made_in'].selectedIndex].value
    image_source = document['update_bulk_image'].attrs['value']

    has_error = False

    if not pin_count:
        document['update_pin_count'].style.border = "0.2rem solid #FF0000"
        document['update_pin_count'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['update_pin_count'].style.border = ""
        document['update_pin_count'].style.boxShadow = ""

    if not length:
        document['update_length'].style.border = "0.2rem solid #FF0000"
        document['update_length'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['update_length'].style.border = ""
        document['update_length'].style.boxShadow = ""

    if not category_checked:
        document['update_cable_radio_container'].style.boxShadow = "0 0 0.5rem #FF0000"
        document['update_cable_radio_container'].style.borderRadius = "0.5rem"
        has_error = True
    else:
        document['update_cable_radio_container'].style.boxShadow = ""
        document['update_cable_radio_container'].style.borderRadius = ""

    if material == "0":
        document['update_cable_material'].style.border = "0.2rem solid #FF0000"
        document['update_cable_material'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['update_cable_material'].style.border = ""
        document['update_cable_material'].style.boxShadow = ""

    if color == "00":
        document['update_cable_color'].style.border = "0.2rem solid #FF0000"
        document['update_cable_color'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['update_cable_color'].style.border = ""
        document['update_cable_color'].style.boxShadow = ""

    if pin_config == "00":
        document['update_cable_pin_config'].style.border = "0.2rem solid #FF0000"
        document['update_cable_pin_config'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['update_cable_pin_config'].style.border = ""
        document['update_cable_pin_config'].style.boxShadow = ""

    if made_in == "00":
        document['update_cable_made_in'].style.border = "0.2rem solid #FF0000"
        document['update_cable_made_in'].style.boxShadow = "0 0 0.5rem #FF0000"
        has_error = True
    else:
        document['update_cable_made_in'].style.border = ""
        document['update_cable_made_in'].style.boxShadow = ""

    if has_error:
        return

    insert_content_inline_loading("bulk_edit_container")
    response = await aio.ajax(
        method="PATCH",
        url="https://api.qrcertify.lynkfast.com/bulk",
        headers={
            "Authorization": session_storage.storage['IdToken']
        },
        data=json.dumps(
            {
                "id": event.target.value,
                "pin_count": pin_count,
                "length": length,
                "category": category_checked[0].value,
                "material": material,
                "color": color,
                "pin_config": pin_config,
                "made_in": made_in,
                "image_source": image_source
            }
        ),
        cache=False
    )
    if response.status == 200:
        get_bulk_list_and_details()
        timer.set_timeout(load_page, 2500, "application/dashboard", "application/editbulk")
        timer.set_timeout(remove_content_inline_loading, 2500, "bulk_edit_container")
        document['add_bulk'].attrs['style'] = "display:flex;"
    else:
        remove_content_inline_loading("bulk_edit_container")


def open_bulk_delete_screen(event):
    current_language = get_current_language()

    def delete_bulk(delete_event):
        bulk_value = delete_event.target.value

        def callback(request):
            if request.status == 200:
                parent_node.remove()
                load_page("application/dashboard", "loading/loading")
            else:
                load_page("application/dashboard", "loading/loading")

        ajax.delete(
            "https://api.qrcertify.lynkfast.com/bulk/{}".format(bulk_value),
            headers={
                "Authorization": session_storage.storage['IdToken']
            },
            oncomplete=callback
        )
        are_you_sure_div = parent_node.select_one(".are_you_sure_dialogue")
        are_you_sure_div.remove()
        load_page("loading/loading", "application/dashboard")

    def cancel_delete_bulk(cancel_delete_event):
        are_you_sure_div = parent_node.select_one(".are_you_sure_dialogue")
        are_you_sure_div.remove()

    node_value = event.target.value
    parent_node = event.target.parent
    are_you_sure_dialog = html.DIV(Class="are_you_sure_dialogue")
    if current_language == "en":
        are_you_sure_text = "Are you sure?"
    elif current_language == "tr":
        are_you_sure_text = "Emin misiniz?"
    elif current_language == "cmn":
        are_you_sure_text = "你确定吗？"
    elif current_language == "yue":
        are_you_sure_text = "你確定嗎？"
    are_you_sure_dialog.attach(
        html.H1(are_you_sure_text)
    )
    are_you_sure_dialog.attach(
        html.BUTTON(
            "👍",
            id="are_you_sure_yes",
            value=node_value
        )
    )
    are_you_sure_dialog.attach(
        html.BUTTON(
            "👎",
            id="are_you_sure_no",
            value=node_value
        )
    )
    parent_node.attach(are_you_sure_dialog)
    document["are_you_sure_yes"].bind("click", delete_bulk)
    document["are_you_sure_no"].bind("click", cancel_delete_bulk)


def login_handler():
    get_bulk_list_and_details()
    timer.set_timeout(load_page, 5000, "application/dashboard", "loading/loading")


@bind(document['login_button'], "click")
def login_user_button(event):
    current_language = get_current_language()

    def new_password_entry():
        timer.set_timeout(login_button_normalization, 2000)
        load_page("authentication/cp", "authentication/login")

    def cfyp_entry_from_login():
        load_page("authentication/cfyp", "authentication/login")
        timer.set_timeout(login_button_normalization, 2000)

    def login_user(username, password):
        authentication_object = AWSSRP()
        login_response = authentication_object.authenticate_user(username, password)
        if login_response['statusCode'] == 200:
            timer.set_timeout(enter_value_successful, 1)
            timer.set_timeout(successful_login, 2000)
        elif login_response['statusCode'] == 202:
            if current_language == "en":
                timer.set_timeout(enter_value_warning, 1, "Need New Password")
            elif current_language == "tr":
                timer.set_timeout(enter_value_warning, 1, "Yeni Şifreyi Belirleyin")
            elif current_language == "cmn":
                timer.set_timeout(enter_value_warning, 1, "需要新密码")
            elif current_language == "yue":
                timer.set_timeout(enter_value_warning, 1, "需要新密碼")
            timer.set_timeout(new_password_entry, 1500)
        elif login_response['statusCode'] == 400:
            if current_language == "en":
                timer.set_timeout(enter_value_warning, 1, "Password Reset Required")
            elif current_language == "tr":
                timer.set_timeout(enter_value_warning, 1, "Şifre Sıfırlama Gerekli")
            elif current_language == "cmn":
                timer.set_timeout(enter_value_warning, 1, "需要重置密码")
            elif current_language == "yue":
                timer.set_timeout(enter_value_warning, 1, "需要重置密碼")
            timer.set_timeout(cfyp_entry_from_login, 1500)
        elif login_response['statusCode'] == 403:
            send_button = document['login_button']
            if current_language == "en":
                timer.set_timeout(enter_value_error, 1, "Authentication Failed")
            elif current_language == "tr":
                timer.set_timeout(enter_value_error, 1, "Giriş Başarısız")
            elif current_language == "cmn":
                timer.set_timeout(enter_value_error, 1, "身份验证失败")
            elif current_language == "yue":
                timer.set_timeout(enter_value_error, 1, "身份驗證失敗")
            timer.set_timeout(failed_login, 3000, send_button)
        else:
            send_button = document['login_button']
            if current_language == "en":
                timer.set_timeout(enter_value_error, 1, "Error")
            elif current_language == "tr":
                timer.set_timeout(enter_value_error, 1, "Hata")
            elif current_language == "cmn":
                timer.set_timeout(enter_value_error, 1, "错误！")
            elif current_language == "yue":
                timer.set_timeout(enter_value_error, 1, "錯誤！")
            timer.set_timeout(failed_login, 3000, send_button)

    def enter_value_successful():
        send_button = document['login_button']
        if current_language == "en":
            send_button.text = "Successful!"
        elif current_language == "tr":
            send_button.text = "Başarılı!"
        elif current_language == "cmn":
            send_button.text = "成功！"
        elif current_language == "yue":
            send_button.text = "成功！"
        send_button.attrs['class'] = "button_success"

    def enter_value_error(text):
        send_button = document['login_button']
        send_button.text = text
        send_button.attrs['class'] = "button_error"

    def enter_value_warning(text):
        document['login_button'].text = text
        document['login_button'].attrs['class'] = "button_warning"
        document['login_button'].unbind("click")

    def login_button_normalization():
        send_button = document['login_button']
        if current_language == "en":
            send_button.text = "Log in"
        elif current_language == "tr":
            send_button.text = "Giriş"
        elif current_language == "cmn":
            send_button.text = "登录"
        elif current_language == "yue":
            send_button.text = "登入"
        send_button.attrs['class'] = "button_active"
        send_button.bind("click", login_user_button)

    def failed_login(send_button):
        if current_language == "en":
            send_button.text = "Log in"
        elif current_language == "tr":
            send_button.text = "Giriş"
        elif current_language == "cmn":
            send_button.text = "登录"
        elif current_language == "yue":
            send_button.text = "登入"
        send_button.attrs['class'] = "button_active"
        send_button.bind("click", login_user_button)

    def processing_login_button():
        send_button = document['login_button']
        def on_transition_end(event):
            send_button.unbind("transitionend", on_transition_end)
        send_button.attrs['class'] = "button_passive"
        if current_language == "en":
            send_button.text = "Processing.."
        elif current_language == "tr":
            send_button.text = "İşleniyor.."
        elif current_language == "cmn":
            send_button.text = "处理中.."
        elif current_language == "yue":
            send_button.text = "處理中.."
        send_button.unbind("click")
        send_button.bind("transitionend", on_transition_end)

    def successful_login():
        load_page("loading/loading", "authentication/login")
        login_handler()
        timer.set_timeout(login_button_normalization, 2000)

    processing_login_button()
    try:
        username = document['login_username'].value
        if not (username):
            raise Exception()
        password = document['login_password'].value
        if not (password):
            raise Exception()
    except:
        timer.set_timeout(enter_value_warning, 1, "Please Enter Value")
        timer.set_timeout(login_button_normalization, 2000)
        return True
    timer.set_timeout(lambda: login_user(username, password), 100)


def init_auth_callback(req):
    current_language = get_current_language()

    def failed_login(send_button):
        if current_language == "en":
            send_button.text = "Log in"
        elif current_language == "tr":
            send_button.text = "Giriş"
        elif current_language == "cmn":
            send_button.text = "登录"
        elif current_language == "yue":
            send_button.text = "登入"
        send_button.attrs['class'] = "button_active"
        send_button.bind("click", login_user_button)

    if req.json['ChallengeName'] == PASSWORD_VERIFIER_CHALLENGE:
        session_storage.storage['AuthenticationStatus'] = "SRP_A_PASS"
        session_storage.storage['SALT'] = req.json['ChallengeParameters']['SALT']
        session_storage.storage['SECRET_BLOCK'] = req.json['ChallengeParameters']['SECRET_BLOCK']
        session_storage.storage['SRP_B'] = req.json['ChallengeParameters']['SRP_B']
        session_storage.storage['USERNAME'] = req.json['ChallengeParameters']['USERNAME']
        session_storage.storage['USER_ID_FOR_SRP'] = req.json['ChallengeParameters']['USER_ID_FOR_SRP']
    else:
        send_button = document['login_button']
        if current_language == "en":
            send_button.text = "Error"
        elif current_language == "tr":
            send_button.text = "Hata!"
        elif current_language == "cmn":
            send_button.text = "错误！"
        elif current_language == "yue":
            send_button.text = "錯誤！"
        send_button.attrs['class'] = "button_error"
        timer.set_timeout(failed_login, 3000, send_button)


def password_verifier_callback(req):
    if 'ChallengeName' not in req.json and '__type' not in req.json:
        try:
            current_time = str(int(time.time()))
            session_storage.storage['AccessToken'] = req.json['AuthenticationResult']['AccessToken']
            session_storage.storage['IdToken'] = req.json['AuthenticationResult']['IdToken']
            local_storage.storage['RefreshToken'] = req.json['AuthenticationResult']['RefreshToken']
            session_storage.storage['AuthenticationStatus'] = "Successful"
            session_storage.storage['AuthenticationTime'] = current_time
            local_storage.storage['AuthenticationTime'] = current_time
        except:
            return {
                'statusCode': 403,
                'body': 'Unable to store the data for login'
            }
    elif "ChallengeName" in req.json:
        if req.json['ChallengeName'] == NEW_PASSWORD_REQUIRED_CHALLENGE:
            try:
                current_time = str(int(time.time()))
                session_storage.storage['AuthenticationStatus'] = "NewPasswordRequired"
                session_storage.storage['NPRSession'] = req.json['Session']
                session_storage.storage['AuthenticationTime'] = current_time
            except:
                return {
                    'statusCode': 403,
                    'body': 'Unable to store the NPR data for login'
                }
        else:
            return {
                'statusCode': 403,
                'body': 'Unable to process the challenge'
            }
    elif "__type" in req.json:
        if req.json['__type'] == "NotAuthorizedException":
            try:
                session_storage.storage['AuthenticationStatus'] = "NotAuthorized"
            except:
                return {
                    'statusCode': 403,
                    'body': 'Unable to store the NA data for login'
                }
        elif req.json['__type'] == "PasswordResetRequiredException":
            session_storage.storage['AuthenticationStatus'] = "PasswordResetRequired"
        else:
            return {
                'statusCode': 403,
                'body': 'Unknown Authorization Issue'
            }
    else:
        return {
            'statusCode': 403,
            'body': 'Unable understand the challenge'
        }


def fast_mod_exp(base, exp, modular):
    res = base
    exponentiation = format(exp, 'b')[1:]
    for boolean in exponentiation:
        if bool(int(boolean)):
            res = (res ** 2) % modular
            res = (res * base) % modular
        else:
            res = (res ** 2) % modular
    return res


def hmac_digest(key, message):
    trans_5C = bytes((x ^ 0x5C) for x in range(256))
    trans_36 = bytes((x ^ 0x36) for x in range(256))
    inner = hashlib.sha256()
    outer = hashlib.sha256()
    blocksize = getattr(inner, 'block_size', 64)
    if len(key) > blocksize:
        key = hashlib.sha256(key).digest()
    key = key + b'\x00' * (blocksize - len(key))
    inner.update(key.translate(trans_36))
    outer.update(key.translate(trans_5C))
    inner.update(message)
    outer.update(inner.digest())
    return outer.digest()


def hash_sha256(buf):
    a = hashlib.sha256(buf).hexdigest()
    return (64 - len(a)) * '0' + a


def hex_hash(hex_string):
    return hash_sha256(bytes.fromhex(hex_string))


def hex_to_long(hex_string):
    return int(hex_string, 16)


def long_to_hex(long_num):
    return '%x' % long_num


def get_random(nbytes):
    random_hex = binascii.hexlify(os.urandom(nbytes))
    return hex_to_long(random_hex)


def pad_hex(long_int):
    if not isinstance(long_int, str):
        hash_str = long_to_hex(long_int)
    else:
        hash_str = long_int
    if len(hash_str) % 2 == 1:
        hash_str = '0%s' % hash_str
    elif hash_str[0] in '89ABCDEFabcdef':
        hash_str = '00%s' % hash_str
    return hash_str


def compute_hkdf(ikm, salt):
    prk = hmac_digest(salt, ikm)
    info_bits_update = info_bits + bytes(chr(1), 'utf-8')
    hmac_hash = hmac_digest(prk, info_bits_update)
    return hmac_hash[:16]


def calculate_u(big_a, big_b):
    u_hex_hash = hex_hash(pad_hex(big_a) + pad_hex(big_b))
    return hex_to_long(u_hex_hash)


class AWSSRP(object):
    def __init__(self):
        self.pool_id = session_storage.storage['pool_id']
        self.client_id = session_storage.storage['client_id']
        self.big_n = hex_to_long(n_hex)
        self.g = hex_to_long(g_hex)
        self.k = hex_to_long(hex_hash('00' + n_hex + '0' + g_hex))
        self.small_a_value = self.generate_random_small_a()
        self.large_a_value = self.calculate_a()

    def generate_random_small_a(self):
        random_long_int = get_random(128)
        return random_long_int % self.big_n

    def calculate_a(self):
        big_a = fast_mod_exp(self.g, self.small_a_value, self.big_n)
        if (big_a % self.big_n) == 0:
            raise ValueError('Safety check for A failed')
        return big_a

    def get_password_authentication_key(self, username, password, server_b_value, salt):
        u_value = calculate_u(self.large_a_value, server_b_value)
        if u_value == 0:
            raise ValueError('U cannot be zero.')
        username_password = '{}{}:{}'.format(self.pool_id.split('_')[1], username, password)
        username_password_hash = hash_sha256(username_password.encode('utf-8'))
        x_value = hex_to_long(hex_hash(pad_hex(salt) + username_password_hash))
        g_mod_pow_xn = fast_mod_exp(self.g, x_value, self.big_n)
        int_value2 = server_b_value - self.k * g_mod_pow_xn
        s_value = fast_mod_exp(int_value2, self.small_a_value + u_value * x_value, self.big_n)
        hkdf = compute_hkdf(bytes.fromhex(pad_hex(s_value)), bytes.fromhex(pad_hex(long_to_hex(u_value))))
        return hkdf

    def get_auth_params(self):
        auth_params = {
            'USERNAME': self.username,
            'SRP_A': long_to_hex(self.large_a_value)
        }
        return auth_params

    def process_challenge(self):
        user_id_for_srp = session_storage.storage['USER_ID_FOR_SRP']
        salt_hex = session_storage.storage['SALT']
        srp_b_hex = session_storage.storage['SRP_B']
        secret_block_b64 = session_storage.storage['SECRET_BLOCK']
        _timestamp = datetime.datetime.now(datetime.UTC).strftime("%a %b %d %H:%M:%S UTC %Y")
        if _timestamp[8] == "0":
            timestamp = _timestamp[:8] + _timestamp[9:]
        else:
            timestamp = _timestamp
        hkdf = self.get_password_authentication_key(user_id_for_srp, self.password, hex_to_long(srp_b_hex), salt_hex)
        secret_block_bytes = base64.standard_b64decode(secret_block_b64)
        msg = bytes(self.pool_id.split('_')[1], 'utf-8') + bytes(user_id_for_srp, 'utf-8') + bytes(
            secret_block_bytes) + bytes(timestamp, 'utf-8')
        hmac_obj = hmac_digest(hkdf, msg)
        signature_string = base64.standard_b64encode(hmac_obj)
        response = {
            'TIMESTAMP': timestamp,
            'USERNAME': user_id_for_srp,
            'PASSWORD_CLAIM_SECRET_BLOCK': secret_block_b64,
            'PASSWORD_CLAIM_SIGNATURE': signature_string.decode('utf-8')
        }
        return response

    def authenticate_user(self, username, password):
        self.username = username
        self.password = password
        auth_params = self.get_auth_params()
        try:
            authentication_data_A = {
                "AuthFlow": "USER_SRP_AUTH",
                "AuthParameters": auth_params,
                "ClientId": self.client_id
            }
            ajax.post(
                "https://cognito-idp.us-east-1.amazonaws.com/",
                blocking=True,
                headers={
                    "X-Amz-Target": "AWSCognitoIdentityProviderService.InitiateAuth",
                    "Content-Type": "application/x-amz-json-1.1"
                },
                data=JSON.stringify(authentication_data_A),
                oncomplete=init_auth_callback
            )
            srp_a_response = session_storage.storage['AuthenticationStatus']
        except:
            return {
                'statusCode': 400,
                'body': 'Unexpected Error Occured'
            }
        if srp_a_response == "SRP_A_PASS":
            challenge_response = self.process_challenge()
            try:
                authentication_data_B = {
                    "ClientId": self.client_id,
                    "ChallengeName": PASSWORD_VERIFIER_CHALLENGE,
                    "ChallengeResponses": challenge_response
                }
                ajax.post(
                    "https://cognito-idp.us-east-1.amazonaws.com/",
                    blocking=True,
                    headers={
                        "X-Amz-Target": "AWSCognitoIdentityProviderService.RespondToAuthChallenge",
                        "Content-Type": "application/x-amz-json-1.1"
                    },
                    data=JSON.stringify(authentication_data_B),
                    oncomplete=password_verifier_callback
                )
                srp_b_response = session_storage.storage['AuthenticationStatus']
            except:
                return {
                    'statusCode': 403,
                    'body': 'Authentication Failed'
                }
            if srp_b_response == "Successful":
                return {
                    'statusCode': 200,
                    'body': 'Authentication Successful'
                }
            elif srp_b_response == "NewPasswordRequired":
                return {
                    'statusCode': 202,
                    'body': 'New Password Required'
                }
            elif srp_b_response == "PasswordResetRequired":
                session_storage.storage['fyp_username'] = self.username
                return {
                    'statusCode': 400,
                    'body': 'Password Reset Required'
                }
            elif srp_b_response == "NotAuthorized":
                return {
                    'statusCode': 403,
                    'body': 'Incorrect Email or Password!'
                }
        else:
            return {
                'statusCode': 403,
                'body': "Unknown Issue Occured"
            }


window.addEventListener("popstate", page_navigator)

if "AuthenticationStatus" in session_storage.storage and "RefreshToken" in local_storage.storage and "AuthenticationTime" in local_storage.storage:
    if session_storage.storage['AuthenticationStatus'] == "Successful" and (
            int(local_storage.storage['AuthenticationTime']) - int(time.time())) < 86400:
        login_handler()
    else:
        del session_storage.storage['AuthenticationStatus']
        load_page("authentication/login")
else:
    load_page("authentication/login")
