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

from PyQt5.QtCore import QCoreApplication, QTranslator, QLocale


app = QCoreApplication(sys.argv)
translator = QTranslator()
locale = QLocale.system().name()
translation_dir = "/usr/share/locale/ru/LC_MESSAGES"

if translator.load(f"astra-update-ctl_ru", translation_dir):
    app.installTranslator(translator)
else:
    print("Warning: translation file not loaded!")

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

def enable_service():
    process = subprocess.run(["systemctl", "enable", "astra-update-service"])
    if process.returncode != 0:
        print(QCoreApplication.translate("MainContext", "Failed to enable unit 'astra-update-service'"))
        return 1
    else:
        process = subprocess.run(["systemctl", "start", "astra-update-service"])
        if process.returncode != 0:
            print(QCoreApplication.translate("MainContext", "Failed to enable unit 'astra-update-service'"))
            return 2
    process = subprocess.run(["systemctl", "enable", "astra-upgrade"])
    if process.returncode != 0:
        print(QCoreApplication.translate("MainContext", "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(QCoreApplication.translate("MainContext", "Cannot set no-updates state: already disabled or broken service"))
    process = subprocess.run(["systemctl", "stop", "astra-update-service"])
    if process.returncode != 0:
        print(QCoreApplication.translate("MainContext", "Failed to stop unit 'astra-update-service'"))
        return 1
    else:
        process = subprocess.run(["systemctl", "disable", "astra-update-service"])    
        if process.returncode != 0:
            print(QCoreApplication.translate("MainContext", "Failed to disable unit 'astra-update-service'"))
            return 2
    process = subprocess.run(["systemctl", "disable", "astra-upgrade"])
    if process.returncode != 0:
        print(QCoreApplication.translate("MainContext", "Failed to disable unit 'astra-upgrade'"))
        return 3
    return 0
    
def set_state(state, internal = False):
    if get_status_by_dbus(False) != 0:
        print(QCoreApplication.translate("MainContext", "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(QCoreApplication.translate("MainContext", "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(QCoreApplication.translate("MainContext", "Current status is 'forced', this action prohibited"))
            return 33
        if state in ["activated", "force"]:
            print(QCoreApplication.translate("MainContext", "Status is already 'forced'"))
            return 34
    props = eth0_dev_iface.setStateFromOutside(index)
    if (state == "activated") and (props == -5):
        print(QCoreApplication.translate("MainContext", "Activation takes time. Please wait."))
    elif (props != 0):
        print(QCoreApplication.translate("MainContext", "Perhaps, something went wrong. Please, pay your attention."))
    else:
        for i in range(5):
            msg = str(QCoreApplication.translate("MainContext", "New state QCoreApplicationlying: ") + str(5 - i) + QCoreApplication.translate("MainContext", " 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(QCoreApplication.translate("MainContext", "disabled"))
        return 2
    if disabled_counter == 1:
        print(QCoreApplication.translate("MainContext", "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(QCoreApplication.translate("MainContext", "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(QCoreApplication.translate("MainContext", 'Available parameters:'))
    print(QCoreApplication.translate("MainContext", '\t T_check \t\t Interval between checks for updates [m], default = 60'))
    print(QCoreApplication.translate("MainContext", '\t T_download_min \t Minimum time before downloading updates [m], default = 0'))
    print(QCoreApplication.translate("MainContext", '\t T_download_max \t Maximum time before downloading updates [m], default = 240'))
    print(QCoreApplication.translate("MainContext", '\t T_delay \t\t Time to the system upgrade [d], default = 7'))
    print(QCoreApplication.translate("MainContext", '\t T_retry \t\t Time to the next attempt to the system update [h], default = 4'))
    print(QCoreApplication.translate("MainContext", '\t Action_on_error \t Action after update error [ Reset, Stop, Retry ]'))
    print(QCoreApplication.translate("MainContext", '\t Snapshot \t\t Do a system snapshot if possible [false, true], default = true'))
    print(QCoreApplication.translate("MainContext", '\t Always_new_update \t Remove downloaded updates before downloading new updates [false, true], default = false'))    
    print(QCoreApplication.translate("MainContext", '\t Host_to_ping \t\t Host to be pinged for checking network connection, default = 77.88.8.8'))
    print(QCoreApplication.translate("MainContext", '\t Free_space_policy \t Method of detection required free space [0, 1, 2], default = 0'))
    print(QCoreApplication.translate("MainContext", '\t\t\t\t 0 - fixed value, 1 - calculate by packages, 2 - do not check free space'))
    print(QCoreApplication.translate("MainContext", '\t Use_source_list_d \t Use only sources.list [0] or with sources.list.d [1], default = 0'))
    print(QCoreApplication.translate("MainContext", '\t Extra_repos \t\t List of paths to files with extra repos'))
    print(QCoreApplication.translate("MainContext", '\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(QCoreApplication.translate("MainContext", '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(QCoreApplication.translate("MainContext", 'Gui editor is not installed. Install fly-update-editor'))
    return 0

def launch_as_user(editor_name):
    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 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":
                if len(sys.argv) < 3:
                    return usage()
                else:
                    return set_state(sys.argv[2])
            return usage()
    except KeyboardInterrupt:
        return 130

exit(check_arg())
