import RNS
import time

from kivy.metrics import dp,sp
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, BooleanProperty, OptionProperty, ColorProperty, Property
from kivymd.uix.list import MDList, IconLeftWidget, IconRightWidget, OneLineAvatarIconListItem
from kivymd.uix.recycleview import MDRecycleView
from kivymd.uix.menu import MDDropdownMenu
from kivymd.toast import toast
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.clock import Clock
from kivy.core.clipboard import Clipboard
from kivy.effects.scroll import ScrollEffect

from kivymd.uix.button import MDRectangleFlatButton
from kivymd.uix.dialog import MDDialog

from kivy.lang.builder import Builder

theme_text_color_options = (
    "Primary",
    "Secondary",
    "Hint",
    "Error",
    "Custom",
    "ContrastParentBackground",
)

from kivy.utils import escape_markup
if RNS.vendor.platformutils.get_platform() == "android":
    from ui.helpers import multilingual_markup
else:
    from .helpers import multilingual_markup

class NewConv(BoxLayout):
    pass

class MsgSync(BoxLayout):
    pass

class ConvSettings(BoxLayout):
    disp_name = StringProperty()
    context_dest = StringProperty()
    trusted = BooleanProperty()
    telemetry = BooleanProperty()
    allow_requests = BooleanProperty()
    is_object = BooleanProperty()
    ptt_enabled = BooleanProperty()

class ConversationEntry(OneLineAvatarIconListItem):
    app = None
    owner_screen = None
    conversation_dropdown = None
    voice_dropdown = None
    clear_dialog = None
    clear_telemetry_dialog = None
    delete_dialog = None

    icon = StringProperty()
    ti_color = OptionProperty(None, options=theme_text_color_options)
    icon_fg = Property(None, allownone=True)
    icon_bg = Property(None, allownone=True)
    
    def __init__(self):
        super().__init__()
        self.bind(on_release=self.app.conversation_action)
        self.ids.left_icon.bind(on_release=self.left_icon_action)
        self.ids.right_icon.bind(on_release=self.right_icon_action)
        self.__init_menus()

    def __init_menus(self):
        dmi_h = 40
        if ConversationEntry.voice_dropdown == None:
            dmv_items = [ { "viewclass": "OneLineListItem", "text": "Edit", "height": dp(dmi_h), "on_release": self.edit_action},
                          { "text": "Copy Identity Hash", "viewclass": "OneLineListItem", "height": dp(dmi_h), "on_release": self.copy_address_action},
                          { "text": "Delete", "viewclass": "OneLineListItem", "height": dp(dmi_h), "on_release": self.delete_conversation_action } ]

            ConversationEntry.voice_dropdown = MDDropdownMenu(caller=None, items=dmv_items, position="auto", width=dp(256), elevation=0, radius=dp(3))
            ConversationEntry.voice_dropdown.effect_cls = ScrollEffect
            ConversationEntry.voice_dropdown.md_bg_color = self.app.color_hover

        if ConversationEntry.conversation_dropdown == None:
            dm_items = [ { "viewclass": "OneLineListItem", "text": "Edit", "height": dp(dmi_h), "on_release": self.edit_action },
                         { "viewclass": "OneLineListItem", "text": "Call", "height": dp(dmi_h), "on_release": self.call_action },
                         { "text": "Copy Address", "viewclass": "OneLineListItem", "height": dp(dmi_h), "on_release": self.copy_address_action },
                         { "text": "Clear Messages", "viewclass": "OneLineListItem", "height": dp(dmi_h), "on_release": self.clear_messages_action },
                         { "text": "Clear Telemetry", "viewclass": "OneLineListItem", "height": dp(dmi_h), "on_release": self.clear_telemetry_action },
                         { "text": "Delete Conversation", "viewclass": "OneLineListItem", "height": dp(dmi_h), "on_release": self.delete_conversation_action } ]

            ConversationEntry.conversation_dropdown = MDDropdownMenu(caller=None, items=dm_items, position="auto", width=dp(256), elevation=0, radius=dp(3))
            ConversationEntry.conversation_dropdown.effect_cls = ScrollEffect
            ConversationEntry.conversation_dropdown.md_bg_color = self.app.color_hover

    def dispatch_update(self):
        self.owner_screen.update()

    def dropdown_dismiss(self):
        self.voice_dropdown.dismiss(); self.conversation_dropdown.dismiss()

    def copy_address_action(self):
        context_dest = ConversationEntry.conversation_dropdown.context_dest; caller = ConversationEntry.conversation_dropdown.caller
        Clipboard.copy(RNS.hexrep(context_dest, delimit=False))
        self.dropdown_dismiss()

    def edit_action(self):
        context_dest = ConversationEntry.conversation_dropdown.context_dest; caller = ConversationEntry.conversation_dropdown.caller
        RNS.log(f"Menu action from {caller} for {RNS.prettyhexrep(context_dest)}")
        dest = context_dest
        try:
            t_s = time.time()
            cd = self.app.sideband._db_conversation(dest)
            disp_name = self.app.sideband.raw_display_name(dest)
            is_trusted = self.app.sideband.is_trusted(dest, conv_data=cd)
            is_object = self.app.sideband.is_object(dest, conv_data=cd)
            ptt_enabled = self.app.sideband.ptt_enabled(dest, conv_data=cd)
            send_telemetry = self.app.sideband.should_send_telemetry(dest, conv_data=cd)
            allow_requests = self.app.sideband.requests_allowed_from(dest, conv_data=cd)
            yes_button = MDRectangleFlatButton(text="Save",font_size=dp(18), theme_text_color="Custom", line_color=self.app.color_accept, text_color=self.app.color_accept)
            no_button = MDRectangleFlatButton(text="Cancel",font_size=dp(18))
            dialog_content = ConvSettings(disp_name=disp_name, context_dest=RNS.hexrep(dest, delimit=False), trusted=is_trusted,
                                          telemetry=send_telemetry, allow_requests=allow_requests, is_object=is_object, ptt_enabled=ptt_enabled)
            dialog_content.ids.name_field.font_name = self.app.input_font
            dialog = MDDialog(title="Edit Conversation", text="With "+RNS.prettyhexrep(dest),
                              type="custom", content_cls=dialog_content, buttons=[yes_button, no_button])
            dialog.d_content = dialog_content
            def dl_no(s): dialog.dismiss()
            def dl_yes(s):
                try:
                    name = dialog.d_content.ids["name_field"].text
                    trusted = dialog.d_content.ids["trusted_switch"].active
                    telemetry = dialog.d_content.ids["telemetry_switch"].active
                    allow_requests = dialog.d_content.ids["allow_requests_switch"].active
                    conv_is_object = dialog.d_content.ids["is_object_switch"].active
                    ptt_is_enabled = dialog.d_content.ids["ptt_enabled_switch"].active
                    if trusted: self.app.sideband.trusted_conversation(dest)
                    else: self.app.sideband.untrusted_conversation(dest)
                    if telemetry: self.app.sideband.send_telemetry_in_conversation(dest)
                    else: self.app.sideband.no_telemetry_in_conversation(dest)
                    if allow_requests: self.app.sideband.allow_requests_from(dest)
                    else: self.app.sideband.disallow_requests_from(dest)
                    if conv_is_object: self.app.sideband.conversation_set_object(dest, True)
                    else: self.app.sideband.conversation_set_object(dest, False)
                    if ptt_is_enabled:self.app.sideband.conversation_set_ptt_enabled(dest, True)
                    else: self.app.sideband.conversation_set_ptt_enabled(dest, False)
                    self.app.sideband.named_conversation(name, dest)

                except Exception as e: RNS.log("Error while saving conversation settings: "+str(e), RNS.LOG_ERROR)

                dialog.dismiss()

                def cb(dt): self.dispatch_update()
                Clock.schedule_once(cb, 0.2)

            yes_button.bind(on_release=dl_yes)
            no_button.bind(on_release=dl_no)
            self.voice_dropdown.dismiss(); self.conversation_dropdown.dismiss()
            dialog.open()
            RNS.log("Generated edit dialog in "+str(RNS.prettytime(time.time()-t_s)), RNS.LOG_DEBUG)

        except Exception as e:
            RNS.log("Error while creating conversation settings: "+str(e), RNS.LOG_ERROR)

    def delete_conversation_action(self):
        context_dest = ConversationEntry.conversation_dropdown.context_dest; caller = ConversationEntry.conversation_dropdown.caller
        if ConversationEntry.delete_dialog == None:
            yes_button = MDRectangleFlatButton(text="Yes",font_size=dp(18), theme_text_color="Custom", line_color=self.app.color_reject, text_color=self.app.color_reject)
            no_button = MDRectangleFlatButton(text="No",font_size=dp(18))
            ConversationEntry.delete_dialog = MDDialog( title="Delete conversation?", buttons=[ yes_button, no_button ])
            def dl_yes(s):
                ConversationEntry.delete_dialog.dismiss()
                self.app.sideband.delete_conversation(ConversationEntry.conversation_dropdown.context_dest)
                def cb(dt): self.dispatch_update()
                Clock.schedule_once(cb, 0.1)
            def dl_no(s):
                self.delete_dialog.dismiss()

            yes_button.bind(on_release=dl_yes)
            no_button.bind(on_release=dl_no)

        self.dropdown_dismiss()
        ConversationEntry.delete_dialog.open()

    def call_action(self):
        context_dest = ConversationEntry.conversation_dropdown.context_dest; caller = ConversationEntry.conversation_dropdown.caller
        identity = RNS.Identity.recall(ConversationEntry.conversation_dropdown.context_dest)
        if identity: self.app.dial_action(identity.hash)
        else: toast("Can't call, identity unknown")
        self.dropdown_dismiss()

    def clear_messages_action(self):
        context_dest = ConversationEntry.conversation_dropdown.context_dest; caller = ConversationEntry.conversation_dropdown.caller
        if ConversationEntry.clear_dialog == None:
            yes_button = MDRectangleFlatButton(text="Yes",font_size=dp(18), theme_text_color="Custom", line_color=self.app.color_reject, text_color=self.app.color_reject)
            no_button = MDRectangleFlatButton(text="No",font_size=dp(18))
            ConversationEntry.clear_dialog = MDDialog(title="Clear all messages in conversation?", buttons=[ yes_button, no_button ])
            
            def dl_yes(s):
                ConversationEntry.clear_dialog.dismiss()
                self.app.sideband.clear_conversation(ConversationEntry.conversation_dropdown.context_dest)
            def dl_no(s):
                self.clear_dialog.dismiss()

            yes_button.bind(on_release=dl_yes)
            no_button.bind(on_release=dl_no)

        ConversationEntry.voice_dropdown.dismiss(); self.conversation_dropdown.dismiss()
        self.clear_dialog.open()
        RNS.log(f"Menu action from {caller} for {RNS.prettyhexrep(context_dest)}")

    def clear_telemetry_action(self):
        context_dest = ConversationEntry.conversation_dropdown.context_dest; caller = ConversationEntry.conversation_dropdown.caller
        if ConversationEntry.clear_telemetry_dialog == None:
            yes_button = MDRectangleFlatButton(text="Yes",font_size=dp(18), theme_text_color="Custom", line_color=self.app.color_reject, text_color=self.app.color_reject)
            no_button = MDRectangleFlatButton(text="No",font_size=dp(18))
            ConversationEntry.clear_telemetry_dialog = MDDialog( title="Clear all telemetry related to this peer?", buttons=[ yes_button, no_button ])
            def dl_yes(s):
                ConversationEntry.clear_telemetry_dialog.dismiss()
                self.app.sideband.clear_telemetry(self.conversation_dropdown.context_dest)
            def dl_no(s):
                ConversationEntry.clear_telemetry_dialog.dismiss()

            yes_button.bind(on_release=dl_yes)
            no_button.bind(on_release=dl_no)

        ConversationEntry.voice_dropdown.dismiss(); ConversationEntry.conversation_dropdown.dismiss()
        ConversationEntry.clear_telemetry_dialog.open()

    def left_icon_action(self, sender):
        RNS.log(f"Action from: {sender} {self.text}")
        if self.object: self.app.object_details_action(sender=self, source_dest=self.sb_uid, from_objects=True)
        else: self.app.conversation_action(self)

    def right_icon_action(self, sender):
        ConversationEntry.voice_dropdown.context_dest = self.sb_uid
        ConversationEntry.voice_dropdown.caller = self.ids.right_icon
        ConversationEntry.conversation_dropdown.caller = self.ids.right_icon
        ConversationEntry.conversation_dropdown.context_dest = self.sb_uid
        if self.conv_type == self.app.sideband.CONV_VOICE: ConversationEntry.voice_dropdown.open()
        else: ConversationEntry.conversation_dropdown.open()

class ConversationList(MDRecycleView):
    def __init__(self):
        super().__init__()
        self.data = []

class Conversations():
    def __init__(self, app):
        self.app = app
        self.context_dests = []
        self.added_item_dests = []
        self.list = None
        self.conversation_list = None
        self.ids = None

        if not self.app.root.ids.screen_manager.has_screen("conversations_screen"):
            self.screen = Builder.load_string(conv_screen_kv)
            self.screen.app = self.app
            self.ids = self.screen.ids
            self.app.root.ids.screen_manager.add_widget(self.screen)
        
        self.conversation_dropdown = None
        self.voice_dropdown = None
        self.delete_dialog = None
        self.clear_dialog = None
        self.clear_telemetry_dialog = None

        self.update()

    def update(self):
        self.context_dests = self.app.sideband.list_conversations(conversations=self.app.include_conversations, objects=self.app.include_objects)
        
        view_title = "Conversations"
        if self.app.include_conversations:
            if self.app.include_objects:
                view_title = "Conversations & Objects"
        elif self.app.include_objects:
            view_title = "Objects"
        self.screen.ids.conversations_bar.title = view_title

        self.update_widget()

        self.app.sideband.setstate("app.flags.unread_conversations", False)
        self.app.sideband.setstate("app.flags.new_conversations", False)
        self.app.sideband.setstate("wants.viewupdate.conversations", False)

    def trust_icon(self, conv):
        conv_type = conv["type"]
        context_dest = conv["dest"]
        unread = conv["unread"]
        appearance = self.app.sideband.peer_appearance(context_dest, conv=conv)
        is_trusted = conv["trust"] == 1
        appearance_from_all = self.app.sideband.config["display_style_from_all"]

        trust_icon = "account-question"
        da = self.app.sideband.DEFAULT_APPEARANCE
        if (is_trusted or appearance_from_all) and self.app.sideband.config["display_style_in_contact_list"] and appearance != None and appearance != da:
            if unread:
                trust_icon = "email"
            else:
                trust_icon = appearance[0] or da[0];
        
        else:
            if conv_type == self.app.sideband.CONV_VOICE:
                trust_icon = "phone"
            else:
                if self.app.sideband.requests_allowed_from(context_dest):
                    if unread:
                        if is_trusted:
                            trust_icon = "email-seal"
                        else:
                            trust_icon = "email"
                    else:
                        trust_icon = "account-lock-open"
                else:
                    if is_trusted:
                        if unread:
                            trust_icon = "email-seal"
                        else:
                            trust_icon = "account-check"
                    else:
                        if unread:
                            trust_icon = "email"
                        else:
                            trust_icon = "account-question"

        return trust_icon

    def get_icon(self, conv):
        context_dest = conv["dest"]
        unread = conv["unread"]
        last_activity = conv["last_activity"]
        trusted = conv["trust"] == 1
        appearance_from_all = self.app.sideband.config["display_style_from_all"]
        appearance = self.app.sideband.peer_appearance(context_dest, conv=conv)
        is_object = self.app.sideband.is_object(context_dest, conv_data=conv)
        da = self.app.sideband.DEFAULT_APPEARANCE
        ic_s = 24; ic_p = 14

        conv_icon = self.trust_icon(conv)
        fg = None; bg = None; ti_color = None

        if (trusted or appearance_from_all) and self.app.sideband.config["display_style_in_contact_list"] and appearance != None and appearance != da:
            fg = appearance[1] or da[1]; bg = appearance[2] or da[2]
            ti_color = "Custom"
        else:
            ti_color = None

        if is_object:
            def gen_rel_func():
                def x(ws):
                    self.app.object_details_action(sender=ws, from_objects=True)
                return x

            rel_func = gen_rel_func()
        else:
            rel_func = self.app.conversation_action

        iconl = IconLeftWidget(
            icon=conv_icon, theme_icon_color=ti_color,
            icon_color=fg, md_bg_color=bg,
            on_release=rel_func)
        iconl.source_dest = context_dest

        iconl._default_icon_pad = dp(ic_p)
        iconl.icon_size = dp(ic_s)
        iconl.conv_type = conv["type"]

        return iconl

    def get_icon_colors(self, conv):
        context_dest = conv["dest"]
        trusted = conv["trust"] == 1
        appearance_from_all = self.app.sideband.config["display_style_from_all"]
        appearance = self.app.sideband.peer_appearance(context_dest, conv=conv)
        da = self.app.sideband.DEFAULT_APPEARANCE
        ic_s = 24; ic_p = 14

        fg = None; bg = None; ti_color = None

        if (trusted or appearance_from_all) and self.app.sideband.config["display_style_in_contact_list"] and appearance != None and appearance != da:
            fg = appearance[1] or da[1]; bg = appearance[2] or da[2]
            ti_color = "Custom"
        else: ti_color = None

        return {"ti_color": ti_color, "fg": fg, "bg": bg}

    def update_conversation_list(self):
        us = time.time()
        ConversationEntry.app = self.app
        ConversationEntry.owner_screen = self
        entries = []
        for conv in self.context_dests:
            context_dest = conv["dest"]
            conv_type = conv["type"]
            unread = conv["unread"]
            last_activity = conv["last_activity"]
            colors = self.get_icon_colors(conv)

            peer_disp_name = multilingual_markup(escape_markup(str(self.app.sideband.peer_display_name(context_dest))).encode("utf-8")).decode("utf-8")
            existing_conv = self.app.sideband._db_conversation(context_dest)
            is_object = self.app.sideband.is_object(context_dest, conv_data=existing_conv)
            is_trusted = self.app.sideband.is_trusted(context_dest, conv_data=existing_conv)
            ptt_enabled = self.app.sideband.ptt_enabled(context_dest, conv_data=existing_conv)
            icon = self.trust_icon(conv)

            cl_entry = {"icon": icon, "text": peer_disp_name, "conv_type": conv_type,
                        "ti_color": "Primary", "icon_fg": "#FFFFFF", "icon_bg": "#00000000",
                        "conv_type": conv_type, "sb_uid": context_dest, "sb_unread": unread,
                        "trusted": is_trusted, "object": is_object, "last_activity": last_activity}

            if colors["ti_color"]: cl_entry["ti_color"] = colors["ti_color"]
            if colors["fg"]: cl_entry["icon_fg"] = colors["fg"]
            if colors["bg"]: cl_entry["icon_bg"] = colors["bg"]
            entries.append(cl_entry)

        entries.sort(key=lambda w: (w["trusted"], w["last_activity"]), reverse=True)
        self.conversation_list.data = entries

        RNS.log("Updated recycle list widgets in "+RNS.prettytime(time.time()-us), RNS.LOG_DEBUG)

    def update_widget(self):
        RNS.log("Updating conversation list widgets", RNS.LOG_DEBUG)
        if self.conversation_list == None:
            self.conversation_list = ConversationList()
            self.list = self.conversation_list
            self.ids.conversation_list_container.add_widget(self.conversation_list)
            
        self.update_conversation_list()

    def get_widget(self):
        return self.conversation_list

conv_screen_kv = """
MDScreen:
    name: "conversations_screen"
    
    BoxLayout:
        orientation: "vertical"

        MDTopAppBar:
            title: "Conversations"
            id: conversations_bar
            anchor_title: "left"
            elevation: 0
            left_action_items:
                [
                ['menu', lambda x: root.app.nav_drawer.set_state("open")],
                ]
            right_action_items:
                [
                ['access-point', lambda x: root.app.announce_now_action(self)],
                ['webhook', lambda x: root.app.connectivity_status(self)],
                ['qrcode', lambda x: root.app.ingest_lxm_action(self)],
                ['email-sync', lambda x: root.app.lxmf_sync_action(self)],
                ['account-plus', lambda x: root.app.new_conversation_action(self)],
                ]

        MDBoxLayout:
            orientation: "vertical"
            id: conversation_list_container
"""

Builder.load_string("""

<ConversationEntry>
    IconLeftWidget:
        id: left_icon
        theme_icon_color: root.ti_color
        icon_color: root.icon_fg
        md_bg_color: root.icon_bg
        icon: root.icon
        _default_icon_pad: dp(14)
        icon_size: dp(24)
    
    IconRightWidget:
        id: right_icon
        icon: "dots-vertical"

<ConversationList>:
    id: conversations_scrollview
    viewclass: "ConversationEntry"
    effect_cls: "ScrollEffect"

    RecycleBoxLayout:
        default_size: None, dp(57)
        default_size_hint: 1, None
        size_hint_y: None
        height: self.minimum_height
        orientation: "vertical"

<NewConv>
    orientation: "vertical"
    spacing: "24dp"
    size_hint_y: None
    height: dp(260)

    MDTextField:
        id: n_address_field
        max_text_length: 32
        hint_text: "Address"
        helper_text: ""
        helper_text_mode: "on_focus"
        text: ""
        font_size: dp(24)

    MDTextField:
        id: n_name_field
        hint_text: "Name"
        text: ""
        font_size: dp(24)

    MDBoxLayout:
        orientation: "horizontal"
        size_hint_y: None
        padding: [0,0,dp(8),dp(24)]
        height: dp(24)
        MDLabel:
            id: "trusted_switch_label"
            text: "Trusted"
            font_style: "H6"

        MDSwitch:
            id: n_trusted
            pos_hint: {"center_y": 0.3}
            active: False

    MDBoxLayout:
        orientation: "horizontal"
        size_hint_y: None
        padding: [0,0,dp(8),dp(24)]
        height: dp(24)
        MDLabel:
            id: "trusted_switch_label"
            text: "Voice Only"
            font_style: "H6"

        MDSwitch:
            id: n_voice_only
            pos_hint: {"center_y": 0.3}
            active: False

<ConvSettings>
    orientation: "vertical"
    spacing: "16dp"
    size_hint_y: None
    padding: [0, 0, 0, dp(8)]
    height: self.minimum_height

    MDTextField:
        id: dest_field
        hint_text: "Address"
        text: root.context_dest
        # disabled: True
        font_size: dp(18)

    MDTextField:
        id: name_field
        hint_text: "Name"
        text: root.disp_name
        font_size: dp(18)

    MDBoxLayout:
        orientation: "horizontal"
        size_hint_y: None
        padding: [0,0,dp(8),0]
        height: dp(32)
        MDLabel:
            id: trusted_switch_label
            text: "Trusted"
            font_style: "H6"

        MDSwitch:
            id: trusted_switch
            pos_hint: {"center_y": 0.43}
            active: root.trusted

    MDBoxLayout:
        orientation: "horizontal"
        size_hint_y: None
        padding: [0,0,dp(8),0]
        height: dp(32)
        MDLabel:
            id: telemetry_switch_label
            text: "Send Telemetry"
            font_style: "H6"

        MDSwitch:
            id: telemetry_switch
            pos_hint: {"center_y": 0.43}
            active: root.telemetry

    MDBoxLayout:
        orientation: "horizontal"
        size_hint_y: None
        padding: [0,0,dp(8),0]
        height: dp(32)
        MDLabel:
            id: allow_requests_label
            text: "Allow Requests"
            font_style: "H6"

        MDSwitch:
            id: allow_requests_switch
            pos_hint: {"center_y": 0.43}
            active: root.allow_requests

    MDBoxLayout:
        orientation: "horizontal"
        size_hint_y: None
        padding: [0,0,dp(8),0]
        height: dp(32)
        MDLabel:
            id: ptt_enabled_label
            text: "PTT Enabled"
            font_style: "H6"

        MDSwitch:
            id: ptt_enabled_switch
            pos_hint: {"center_y": 0.43}
            active: root.ptt_enabled

    MDBoxLayout:
        orientation: "horizontal"
        size_hint_y: None
        padding: [0,0,dp(8),0]
        height: dp(32)
        MDLabel:
            id: is_object_label
            text: "Is Object"
            font_style: "H6"

        MDSwitch:
            id: is_object_switch
            pos_hint: {"center_y": 0.43}
            active: root.is_object

<MsgSync>
    orientation: "vertical"
    spacing: "24dp"
    size_hint_y: None
    padding: [0, 0, 0, dp(16)]
    height: self.minimum_height+dp(24)

    MDLabel:
        id: node_info
        text: "Unknown propagation node"

    MDProgressBar:
        id: sync_progress
        type: "determinate"
        value: 0

    MDLabel:
        id: sync_status
        text: "Initiating sync..."


""")