#!/usr/bin/python3

import os, subprocess, locale
import glob
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
from locale import gettext as _

domain = 'repocreator'
locale_dir = '/usr/share/locale'
locale.bindtextdomain(domain, locale_dir)
locale.textdomain(domain)

def show_message(mtype, message, title=None, parent=None):
    if mtype == 'info':
        mtype = Gtk.MessageType.INFO
        button = Gtk.ButtonsType.OK
        title=title or _("Information")
    elif mtype == 'error':
        mtype = Gtk.MessageType.ERROR
        button = Gtk.ButtonsType.CLOSE
        title=title or _("Error")

    dialog = Gtk.MessageDialog(
        destroy_with_parent=True,
        modal=True,
        transient_for=parent or window,
        title=title,
        message_type=mtype,
        buttons=button,
        text='\n'+message)

    dialog.run()
    dialog.destroy()


def dir_chooser_dialog(title=None, parent=None):
    dialog = Gtk.FileChooserDialog(
        destroy_with_parent=True,
        modal=True,
        transient_for=parent or window,
        title=title or "",
        action=Gtk.FileChooserAction.SELECT_FOLDER)
    dialog.add_buttons(_("Cancel"), Gtk.ResponseType.CANCEL, _("Open"), Gtk.ResponseType.OK)

    response = dialog.run()
    selected_dir = None
    if response == Gtk.ResponseType.OK:
        selected_dir = dialog.get_filename()
    dialog.destroy()
    return selected_dir


def update_ui():
    while Gtk.events_pending():
        Gtk.main_iteration_do(True)


def check_repo_path():
    global repo_path

    if not repo_path:
        show_message('error', _("Please select a repository path first"))
        return False

    if not os.path.exists(repo_path):
        show_message('error', _("Path does not exist"))
        return False

    if not os.access(repo_path, os.R_OK):
        show_message('error', _("The directory is not accessible for reading"))
        return False

    return True


def check_repo_validity():
    global repo_path

    if not check_repo_path():
        return

    if not os.path.exists(repo_path + "/repodata/repomd.xml"):
        show_message('error', _("The directory is not a repository"))
        return False
    return True


def add_to_logview(message):
    end_iter = log_buffer.get_end_iter()
    log_buffer.insert(end_iter, message)
    adj.set_value(adj.get_upper() - adj.get_page_size())
    update_ui()


def run_process_with_gui_output(command):
    try:
        process = subprocess.Popen(
            command,
            stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
            text=True, bufsize=1, universal_newlines=True)

        output = ""
        while True:
            line = process.stdout.readline()
            if not line:
                break
            output += line
            add_to_logview(line)
        add_to_logview('\n')

        return_code = process.wait()
        return return_code, output

    except Exception as message:
        show_message('error', message)


def on_button_select_repo_path_clicked(widget):
    global repo_path
    global repo_name

    dialog_title = _("Choosing repository location")
    selected_dir = dir_chooser_dialog(title=dialog_title)

    if selected_dir:
        repo_path = selected_dir
        repo_name = repo_path.split('/')[-1]
        entry_repo_path.set_text(repo_path)


def on_button_create_repo_clicked(widget):
    global repo_path

    if not check_repo_path():
        return

    rpm_files = glob.glob(os.path.join(repo_path, "*.rpm"))
    if not rpm_files:
        show_message('error', _("No rpm files found in the directory"))
        return

    command = ["createrepo_c", repo_path]
    if not os.access(repo_path, os.W_OK):
        command = ["pkexec"] + command

    return_code, output = run_process_with_gui_output(command)
    if return_code == 0:
        message = _("Repository successfully created")
        show_message('info', message)
    # Ignoring error when canceling authorization in pkexec
    elif not "Not authorized" in output:
        message = _("An error has occurred. Please check the log for details.")
        show_message('error', message)



def on_button_check_for_unresolved_deps_clicked(widget):
    global repo_path
    global repo_name

    if not check_repo_validity():
        return

    while True:
        try:
            repolist = subprocess.run(["dnf", "repolist", repo_name], capture_output=True, text=True).stdout
            if not repo_name in repolist:
                break
            repo_name = repo_name + '_'

        except Exception as message:
            show_message('error', message)
            return

    command = ["dnf", "--refresh", "--repofrompath", f"{repo_name},{repo_path}",
           "repoclosure", "--check", repo_name]
    result = run_process_with_gui_output(command)
    return_code = result[0]
    if return_code == 0:
        message = _("Package dependencies are satisfied")
        show_message('info', message)
    else:
        message = _("With the current set of enabled repositories, dependencies for packages in the repository under test are not resolved. Please check the log for details.")
        show_message('error', message)


def on_button_add_repo_clicked(widget):
    global repo_name

    if not check_repo_validity():
        return

    entry_config_repo_name.set_text(repo_name)
    addrepo_window.show()


def on_button_cancel_add_repo_clicked(widget):
    global config_repo_name
    global repo_name
    config_repo_name = repo_name
    addrepo_window.hide()


def on_button_save_repo_config_clicked(widget):
    global repo_path
    global config_repo_name
    global repo_enabled

    toplevel = widget.get_toplevel()

    if not config_repo_name:
        show_message('error', _("Please enter the repository name"), parent=toplevel)
        return

    repo_config_dir = "/etc/yum.repos.d"
    repo_config_file = repo_config_dir + "/" + config_repo_name + ".repo"
    if os.path.exists(repo_config_file):
        message = _("The configuration file already exists")
        show_message('error', message, parent=toplevel)
        return

    # Check if repo_path exists in existing repositories
    for root, dirs, files in os.walk(repo_config_dir):
        for filename in files:
            file_path = root + '/' + filename
            try:
                with open(file_path, 'r') as file:
                    file_content = file.read()
                    if f"baseurl={repo_path}" in file_content:
                        log_message = _("The path {repo_path} already exists in the file {file_path}\n").format(repo_path=repo_path, file_path=file_path)
                        add_to_logview(log_message)
                        message = _("The repository to be added is already exists in one of the configuration files. Please check the log for details.")
                        show_message('error', message)
                        addrepo_window.hide()
                        return
            except Exception as message:
                show_message('error', message, parent=toplevel)
                return

    config_content = f"""\
[{config_repo_name}]
name=ROSA $releasever - {config_repo_name}
baseurl={repo_path}
gpgcheck=0
enabled={1 if checkbutton_enable_repo.get_active() else 0}
"""
    message = _("The configuration file is saved in") + " " + repo_config_file
    command = ["sh", "-c", f"echo '{config_content}' > '{repo_config_file}' && echo '{message}'"]

    if not os.access(repo_config_dir, os.W_OK):
        command = ["pkexec"] + command

    return_code, output = run_process_with_gui_output(command)
    if return_code == 0:
        message = _("The configuration file has been successfully saved")
        show_message('info', message, parent=toplevel)
        addrepo_window.hide()

    # Ignoring error when canceling authorization in pkexec
    elif not "Not authorized" in output:
        message = _("An error has occurred. Please check the log for details.")
        show_message('error', message)


def on_button_clear_log_clicked(widget):
    buffer = logview.get_buffer()
    buffer.set_text("")


def on_entry_repo_path_changed(widget):
    global repo_path
    global repo_name
    repo_path = widget.get_text()
    repo_name = repo_path.split('/')[-1]


def on_entry_config_repo_name_changed(widget):
    global config_repo_name
    config_repo_name = widget.get_text()


def on_entry_config_repo_name_key_press_event(widget, event):
    if event.keyval == Gdk.KEY_Return:
        button_save_repo_config.clicked()


def on_menuitem_about_activate(menuitem):
        show_message('info',
                     _("Repocreator - program for creating a local RPM repository, checking it for unresolved dependencies and adding it to the system. For this purpose the program uses the createrepo_c and dnf utilities.") + "\n\n" +
                     _("Support email address: support@rosalinux.ru") + "\n\n" +
                     _("License: GPLv3") + "\n\n" +
                     _("Version: ") + "0.1",
                     title=_("About the program"))


if os.path.exists("./repocreator.ui"):
    ui = "./repocreator.ui"
else:
    ui = "/usr/share/repocreator/repocreator.ui"

repo_path = ""
repo_name = ""
config_repo_name = ""

builder = Gtk.Builder()
builder.add_from_file(ui)

window = builder.get_object("main_window")
window.connect("destroy", Gtk.main_quit)
window.set_title("Repocreator")

menuitem_quit = builder.get_object("menuitem_quit")
menuitem_quit.connect("activate", Gtk.main_quit)

menuitem_about = builder.get_object("menuitem_about")
menuitem_about.connect("activate", on_menuitem_about_activate)

button_select_repo_path = builder.get_object("button_select_repo_path")
button_select_repo_path.connect("clicked", on_button_select_repo_path_clicked)

button_create_repo = builder.get_object("button_create_repo")
button_create_repo.connect("clicked", on_button_create_repo_clicked)

button_check_for_unresolved_deps = builder.get_object("button_check_for_unresolved_deps")
button_check_for_unresolved_deps.connect("clicked", on_button_check_for_unresolved_deps_clicked)

button_add_repo = builder.get_object("button_add_repo")
button_add_repo.connect("clicked", on_button_add_repo_clicked)

button_clear_log = builder.get_object("button_clear_log")
button_clear_log.connect("clicked", on_button_clear_log_clicked)

entry_repo_path = builder.get_object("entry_repo_path")
entry_repo_path.connect("changed", on_entry_repo_path_changed)

logview = builder.get_object("logview")
log_buffer = logview.get_buffer()

adj = builder.get_object('scroll_area').get_vadjustment()

addrepo_window = builder.get_object("addrepo_window")
addrepo_window.connect("delete-event", lambda widget, event: widget.hide() or True)

button_save_repo_config = builder.get_object("button_save_repo_config")
button_save_repo_config.connect("clicked", on_button_save_repo_config_clicked)

button_cancel_add_repo = builder.get_object("button_cancel_add_repo")
button_cancel_add_repo.connect("clicked", on_button_cancel_add_repo_clicked)

entry_config_repo_name = builder.get_object("entry_config_repo_name")
entry_config_repo_name.connect("changed", on_entry_config_repo_name_changed)
entry_config_repo_name.connect("key_press_event", on_entry_config_repo_name_key_press_event)

checkbutton_enable_repo = builder.get_object("checkbutton_enable_repo")


Gtk.main()
