#!/usr/bin/env python3
#--------------------------------------------------------------------------------------------------------
# Name: Linux Lite - Lite Firewall Config
# Architecture: amd64
# Author: Jerry Bezencon
# Website: https://www.linuxliteos.com
# Language: Python/GTK4
# Licence: GPLv2
#--------------------------------------------------------------------------------------------------------

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, GLib, Gio

import gettext as _gt, locale as _loc
TEXTDOMAIN = "lite-firewallconfig"
try:
    _loc.setlocale(_loc.LC_ALL, "")
except _loc.Error:
    pass
_gt.bindtextdomain(TEXTDOMAIN, "/usr/share/locale")
_gt.textdomain(TEXTDOMAIN)
_ = _gt.translation(TEXTDOMAIN, "/usr/share/locale", fallback=True).gettext

import subprocess
import threading

# Icon paths
ICON_ENABLED = "/usr/share/icons/Papirus/22x22/apps/security-high.svg"
ICON_DISABLED = "/usr/share/icons/Papirus/22x22/apps/security-low.svg"
ICON_INFO = "/usr/share/icons/Papirus/22x22/status/dialog-information.svg"
ICON_APP = "/usr/share/icons/Papirus/24x24/apps/lite-firewallconfig.png"


def run_helper(action):
    """Invoke the privileged lite-firewallconfig-helper via pkexec. The helper
    script has its own polkit policy (com.linuxliteos.lite-firewallconfig) so
    the auth dialog shows the LL shield icon and a clean message rather than
    the generic 'Authentication is needed to run /bin/sh' text.
    Returns (success, stderr)."""
    try:
        result = subprocess.run(
            ["pkexec", "/usr/bin/lite-firewallconfig-helper", action],
            capture_output=True, text=True, timeout=60
        )
        return result.returncode == 0, (result.stderr or "").strip()
    except subprocess.TimeoutExpired:
        return False, _("pkexec timed out")
    except Exception as e:
        return False, str(e)


class ProgressDialog(Gtk.Window):
    """An indeterminate-progress dialog shown while a pkexec call runs."""

    def __init__(self, parent, title, label):
        super().__init__(title=title)
        self.set_transient_for(parent)
        self.set_modal(True)
        self.set_default_size(400, 100)
        self.set_resizable(False)

        self.success = False
        self.error_detail = ""
        self._pulse_source = None

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
        box.set_margin_top(20)
        box.set_margin_bottom(20)
        box.set_margin_start(20)
        box.set_margin_end(20)

        self.status_label = Gtk.Label(label=label)
        self.status_label.set_xalign(0)
        box.append(self.status_label)

        self.progress_bar = Gtk.ProgressBar()
        box.append(self.progress_bar)

        self.set_child(box)

    def run_action(self, action, on_complete):
        """Invoke pkexec /usr/bin/lite-firewallconfig-helper <action>, pulsing
        the bar while it runs."""
        self._pulse_source = GLib.timeout_add(100, self._pulse)

        def worker():
            self.success, self.error_detail = run_helper(action)
            GLib.idle_add(self.on_finished, on_complete)

        thread = threading.Thread(target=worker, daemon=True)
        thread.start()

    def _pulse(self):
        self.progress_bar.pulse()
        return True

    def on_finished(self, callback):
        if self._pulse_source is not None:
            GLib.source_remove(self._pulse_source)
            self._pulse_source = None
        self.close()
        if callback:
            callback(self.success, self.error_detail)
        return False


class FirewallConfigApp(Gtk.Application):
    """Main FirewallD configuration application."""

    def __init__(self):
        super().__init__(application_id="com.linuxlite.firewallconfig",
                         flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.window = None

    def do_activate(self):
        """Create and show the main window."""
        if self.window is None:
            self.window = Gtk.ApplicationWindow(application=self)
            self.window.set_title(_("Firewall"))
            self.window.set_default_size(280, 200)
            self.window.set_resizable(False)

            # Try to set window icon
            try:
                self.window.set_icon_name("lite-firewallconfig")
            except:
                pass

            # Header bar (plain Gtk so the window follows the Linux-Lite theme)
            header = Gtk.HeaderBar()
            self.window.set_titlebar(header)

            # Content box
            content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
            content_box.set_margin_top(20)
            content_box.set_margin_bottom(20)
            content_box.set_margin_start(20)
            content_box.set_margin_end(20)
            content_box.set_halign(Gtk.Align.CENTER)
            content_box.set_valign(Gtk.Align.CENTER)
            content_box.set_vexpand(True)

            # Enable Firewall button
            enable_btn = self.create_button(_("Enable Firewall"), ICON_ENABLED,
                                            self.on_enable_clicked)
            content_box.append(enable_btn)

            # Disable Firewall button
            disable_btn = self.create_button(_("Disable Firewall"), ICON_DISABLED,
                                             self.on_disable_clicked)
            content_box.append(disable_btn)

            # Show Status button
            status_btn = self.create_button(_("Show Status"), ICON_INFO,
                                            self.on_status_clicked)
            content_box.append(status_btn)

            self.window.set_child(content_box)

        self.window.present()
    
    def create_button(self, label, icon_path, callback):
        """Create a button with an icon."""
        button = Gtk.Button()
        button.set_size_request(200, 40)
        
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        box.set_halign(Gtk.Align.CENTER)
        
        # Try to load icon from file, fall back to symbolic icon
        try:
            icon = Gtk.Image.new_from_file(icon_path)
        except:
            icon = Gtk.Image.new_from_icon_name("dialog-information-symbolic")
        icon.set_pixel_size(22)
        
        label_widget = Gtk.Label(label=label)

        box.append(icon)
        box.append(label_widget)
        button.set_child(box)
        button.connect("clicked", callback)
        
        return button
    
    def on_enable_clicked(self, button):
        """Enable the firewall via the pkexec'd helper."""
        dialog = ProgressDialog(self.window, _("Firewall — Enable"), _("Enabling Firewall..."))
        dialog.present()
        dialog.run_action("enable", self._on_enable_done)

    def _on_enable_done(self, success, error_detail):
        if success:
            self.show_message(_("Firewall Enabled"),
                              _("The Firewall has been enabled successfully."))
        else:
            body = _("Failed to enable the firewall.")
            if error_detail:
                body += f"\n\n{error_detail}"
            self.show_message(_("Error"), body)

    def on_disable_clicked(self, button):
        """Disable the firewall via the pkexec'd helper."""
        dialog = ProgressDialog(self.window, _("Firewall — Disable"), _("Disabling Firewall..."))
        dialog.present()
        dialog.run_action("disable", self._on_disable_done)

    def _on_disable_done(self, success, error_detail):
        if success:
            self.show_message(_("Firewall Disabled"),
                              _("The Firewall has been disabled successfully."))
        else:
            body = _("Failed to disable the firewall.")
            if error_detail:
                body += f"\n\n{error_detail}"
            self.show_message(_("Error"), body)
    
    def on_status_clicked(self, button):
        """Show the current firewall status."""
        try:
            result = subprocess.run(
                ["systemctl", "is-active", "firewalld"],
                capture_output=True,
                text=True
            )
            is_active = result.stdout.strip() == "active"
        except Exception:
            is_active = False
        
        if is_active:
            status_text = _("ENABLED")
        else:
            status_text = _("DISABLED")

        # Status window. Body text follows the theme (black/white); only the
        # ENABLED/DISABLED word carries a semantic colour via the theme's
        # .success / .error classes (adapts to Light/Dark, unlike a hardcoded
        # green/red), so it stays readable in both modes.
        win = Gtk.Window(transient_for=self.window, modal=True)
        win.set_title(_("Firewall Status"))
        win.set_default_size(320, 170)
        win.set_resizable(False)
        header = Gtk.HeaderBar()
        win.set_titlebar(header)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16,
                      margin_top=24, margin_bottom=24, margin_start=24, margin_end=24)
        intro = Gtk.Label(label=_("The Firewall is currently:"))
        box.append(intro)
        status_lbl = Gtk.Label(label=status_text)
        status_lbl.add_css_class("title-2")
        status_lbl.add_css_class("success" if is_active else "error")
        box.append(status_lbl)
        ok_btn = Gtk.Button(label=_("OK"))
        ok_btn.set_halign(Gtk.Align.CENTER)
        ok_btn.connect("clicked", lambda b: win.close())
        box.append(ok_btn)
        win.set_child(box)
        win.present()

    def show_message(self, title, message):
        """Show a simple message dialog."""
        dialog = Gtk.AlertDialog()
        dialog.set_modal(True)
        dialog.set_message(title)
        dialog.set_detail(message)
        dialog.set_buttons([_("OK")])
        dialog.set_default_button(0)
        dialog.set_cancel_button(0)
        dialog.show(self.window)


def main():
    app = FirewallConfigApp()
    return app.run(None)


if __name__ == "__main__":
    main()
