#!/usr/bin/python3
import os
import subprocess
import sys
import time
import dbus
import configparser
import gettext
import locale

translation_dir = "/usr/share/locale"
current_locale, encoding = locale.getlocale()

lang = gettext.translation('astra-update-ctl', localedir=translation_dir, languages=["ru"], fallback=True)
lang.install()
_ = lang.gettext

if current_locale != "ru_RU":
    _ = lambda s: s # if locale is not russian return original string

def usage():
    # Help screen
    print(_("Usage:"))
    print('\t' + os.path.basename(__file__) + _(' [command]'))
    print(_('Available commands:'))
    print(_('\t status \t\t Get service status'))
    print(_('\t enable \t\t Enable service'))
    print(_('\t disable \t\t Disable service'))
    print(_('\t edit \t\t\t Open the config to editing'))
    print(_('\t gui-edit \t\t Open a gui editor of the config'))
    print(_('\t parameters\t\t Print a list of parameters available for editing'))
    print(_('\t set STATUS\t\t Set service to STATUS [no-updates, ready, activated, force]'))
    print(_('\t set-log-level LEVEL\t Set logging level [debug, info, warning, error], default = info'))
    return -1

def enable_service():
    process = subprocess.run(["systemctl", "enable", "astra-update-service"])
    if process.returncode != 0:
        print(_("Failed to enable unit 'astra-update-service'"))
        return 1
    else:
        process = subprocess.run(["systemctl", "start", "astra-update-service"])
        if process.returncode != 0:
            print(_("Failed to enable unit 'astra-update-service'"))
            return 2
    process = subprocess.run(["systemctl", "enable", "astra-upgrade"])
    if process.returncode != 0:
        print(_("Failed to enable unit 'astra-upgrade'"))
        return 3
    return 0

def disable_service():
    if get_status_by_dbus(False) == 0:
        set_state("disabled", True)
    else:
        print(_("Cannot set no-updates state: already disabled or broken service"))
    process = subprocess.run(["systemctl", "stop", "astra-update-service"])
    if process.returncode != 0:
        print(_("Failed to stop unit 'astra-update-service'"))
        return 1
    else:
        process = subprocess.run(["systemctl", "disable", "astra-update-service"])    
        if process.returncode != 0:
            print(_("Failed to disable unit 'astra-update-service'"))
            return 2
    process = subprocess.run(["systemctl", "disable", "astra-upgrade"])
    if process.returncode != 0:
        print(_("Failed to disable unit 'astra-upgrade'"))
        return 3
    return 0
    
def set_state(state, internal = False):
    if get_status_by_dbus(False) != 0:
        print(_("Cannot set state: service disabled or broken"))
        return 31
    if internal:
        correct_states = ["no-updates", "ready", "activated", "stopped", "force", "disabled"]
    else:
        correct_states = ["no-updates", "ready", "activated", "stopped", "force"]
    if state not in correct_states or state == "stopped":
        print(_("Wrong state"))
        return 32
    index = correct_states.index(state)
    session_bus = dbus.SystemBus()
    eth0 = session_bus.get_object('ru.astralinux.fly.astraupdateservice',
                          '/ru/astralinux/fly/astraupdateservice')
    eth0_dev_iface = dbus.Interface(eth0,
                                    dbus_interface='ru.astralinux.fly.astraupdateservice')
    props = eth0_dev_iface.getCurrentStateAsInt()
    if props == 6:
        if state in ["no-updates", "ready", "stopped"]:
            print(_("Current status is 'forced', this action prohibited"))
            return 33
        if state in ["activated", "force"]:
            print(_("Status is already 'forced'"))
            return 34
    props = eth0_dev_iface.setStateFromOutside(index)
    if (state == "activated") and (props == -5):
        print(_("Activation takes time. Please wait."))
    elif (props != 0):
        print(_("Perhaps, something went wrong. Please, pay your attention."))
    else:
        for i in range(5):
            msg = str(_("New state is being applied: ") + str(5 - i) + _(" secs"))
            print(msg)
            time.sleep(1)
    return props
    
def get_status_by_dbus(printres):
    disabled_counter = 0
    active_flag = False
    process = subprocess.Popen(["systemctl", "status", "astra-update-service"], stdout=subprocess.PIPE, text=True)
    enabled = False
    for s in process.stdout.readlines():
        if s.find("service.service; enabled") > 0:
            enabled = True
            break
    if enabled == False:
        disabled_counter += 1
    enabled = False
    process = subprocess.Popen(["systemctl", "status", "astra-upgrade"], stdout=subprocess.PIPE, text=True)
    for s in process.stdout.readlines():
        if s.find("upgrade.service; enabled") > 0:
            enabled = True
            break
    if enabled == False:
        disabled_counter += 1
    if disabled_counter == 2:
        print(_("disabled"))
        return 2
    if disabled_counter == 1:
        print(_("Partially disabled"))
        return 1
    process = subprocess.Popen(["systemctl", "is-active", "astra-update-service"], stdout=subprocess.DEVNULL, text=True)
    process.wait()
    if process.returncode == 0:
        active_flag = True
    if ( not active_flag ):
        print(_("Both services enabled, but astra-update-service not active"))
        return 3
    if printres:
        session_bus = dbus.SystemBus()
        eth0 = session_bus.get_object('ru.astralinux.fly.astraupdateservice',
                                      '/ru/astralinux/fly/astraupdateservice')
        eth0_dev_iface = dbus.Interface(eth0,
                                        dbus_interface='ru.astralinux.fly.astraupdateservice')
        props =  eth0_dev_iface.getCurrentStateAsString()
        print(props)
    return 0


def print_params():
    print(_('Available parameters:'))
    print(_('\t T_check \t\t Interval between checks for updates [m], default = 60'))
    print(_('\t T_download_min \t Minimum time before downloading updates [m], default = 0'))
    print(_('\t T_download_max \t Maximum time before downloading updates [m], default = 240'))
    print(_('\t T_delay \t\t Time to the system upgrade [d], default = 7'))
    print(_('\t T_retry \t\t Time to the next attempt to the system update [h], default = 4'))
    print(_('\t Action_on_error \t Action after update error [ Reset, Stop, Retry ]'))
    print(_('\t Snapshot \t\t Do a system snapshot if possible [false, true], default = true'))
    print(_('\t Always_new_update \t Remove downloaded updates before downloading new updates [false, true], default = false'))    
    print(_('\t Host_to_ping \t\t Host to be pinged for checking network connection, default = 77.88.8.8'))
    print(_('\t Free_space_policy \t Method of detection required free space [0, 1, 2], default = 0'))
    print(_('\t\t\t\t 0 - fixed value, 1 - calculate by packages, 2 - do not check free space'))
    print(_('\t Use_source_list_d \t Use only sources.list [0] or with sources.list.d [1], default = 0'))
    print(_('\t Extra_repos \t\t List of paths to files with extra repos'))
    print(_('\t Extra_repos_policy \t Use only extra repos without sources.list [0] or with sources.list [1], default = 0'))
    return -1

    
def edit_config():
    config_file = '/etc/astra-update-service/astra-update-daemon.conf'
    result = ensure_file_exists(config_file)
    if result == 0:
        subprocess.run(["sensible-editor", config_file])
        return 0
    else:
        return -1

def ensure_file_exists(config_file):
    try:
        config_dir=os.path.dirname(config_file)
        if not os.path.exists(config_dir):
            subprocess.run(["mkdir", config_dir])

        if not os.path.exists(config_file):
            config = configparser.ConfigParser()
            config.optionxform = str
            config.add_section('General')
            config.set('General', 'Action_on_error', "Stop")
            with open(config_file, 'w') as configfile:
                config.write(configfile)
        return 0

    except Exception:
        print(_('Permission error. Run the script with sudo'))
        return -1


def try_to_open_gui_edit():
    editor_name = "fly-update-editor"
    if is_pkg_installed(editor_name):
        launch_as_user(editor_name)
    else:
        print(_('Gui editor is not installed. Install fly-update-editor'))
    return 0

def launch_as_user(editor_name):

    # run as regular user
    if os.getuid() != 0:
        subprocess.run(
            "fly-update-editor",
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            stdin=subprocess.DEVNULL,
            preexec_fn=os.setsid,
            check=True
        )

        return

    sudo_user = os.environ.get("SUDO_USER")
    if not sudo_user:
        sudo_user = os.environ.get("USER")

    uid = int(subprocess.check_output(["id", "-u", sudo_user]).strip())
    env_vars = {
        "DISPLAY": os.environ.get("DISPLAY", ""),
        "XAUTHORITY": os.environ.get("XAUTHORITY", f"/home/{sudo_user}/.Xauthority"),
        "XDG_RUNTIME_DIR": f"/run/user/{uid}",
        "QT_QPA_PLATFORMTHEME": "qt5ct"
    }

    command = [
        "sudo", "runuser", "-u", sudo_user, "--", "env",
    ] + [f"{key}={value}" for key, value in env_vars.items()] + [
        "fly-open", "-e", f"/usr/bin/{editor_name}"
    ]

    subprocess.run(
        command,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        stdin=subprocess.DEVNULL,
        preexec_fn=os.setsid,
        check=True
    )

def is_pkg_installed(pkg_name):
    try:
        subprocess.run(
            ["dpkg", "-s", pkg_name],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            check=True
        )
        return True
    except subprocess.CalledProcessError:
        return False

def set_log_level(level):
    if level not in ['debug', 'info', 'warning', 'error']:
        print(_('Support only [debug, info, warning, error] log levels'))
        return -1

    try:
        config_file = '/etc/astra-update-service/astra-update-daemon.conf'
        result = ensure_file_exists(config_file)
        if result != 0:
            return result

        config = configparser.ConfigParser()
        config.optionxform = str
        config.read(config_file)
        config.set('General', 'Log_level', level)

        with open(config_file, 'w') as configfile:
            config.write(configfile)
    except PermissionError:
        print(_('Permission error. Run the script with sudo'))
        return -1

    return 0

def check_arg():

    try:
        if len(sys.argv) - 1 == 0:
            return usage()
        else:
            if sys.argv[1] == "status":
                return get_status_by_dbus(True)
            if sys.argv[1] == "enable":
                return enable_service()
            if sys.argv[1] == "disable":
                return disable_service()
            if sys.argv[1] == "edit":
                return edit_config()
            if sys.argv[1] == "parameters":
                return print_params()
            if sys.argv[1] == "gui-edit":
                return try_to_open_gui_edit()
            if sys.argv[1] == "set-log-level":
                if len(sys.argv) < 3:
                    return usage()
                else:
                    return set_log_level(sys.argv[2])
            if sys.argv[1] == "set":
                if len(sys.argv) < 3:
                    return usage()
                else:
                    return set_state(sys.argv[2])
            return usage()
    except KeyboardInterrupt:
        return 130

exit(check_arg())
