#!/usr/bin/env python3
# --------------------------------------------
# Description: Linux Firewall Config Dialogue
# Authors: Jerry Bezencon (original), converted to Python/GTK4
# Website: https://www.linuxliteos.com
# --------------------------------------------

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw, GLib, Gio
import subprocess
import threading

# Icon paths
ICON_ENABLED = "/usr/share/icons/Papirus-Adapta/22x22/emblems/vcs-normal.svg"
ICON_DISABLED = "/usr/share/icons/Papirus-Adapta/22x22/emblems/vcs-conflicting.svg"
ICON_INFO = "/usr/share/icons/Papirus/22x22/status/dialog-information.svg"
ICON_APP = "/usr/share/icons/Papirus/24x24/apps/firewall-config.svg"


def get_sudo_password():
    """Prompt for password using zenity."""
    try:
        result = subprocess.run(
            ["zenity", "--password", "--title=Authentication Required",
             "--window-icon=" + ICON_APP],
            capture_output=True,
            text=True
        )
        if result.returncode == 0:
            return result.stdout.strip()
    except Exception as e:
        print(f"Zenity error: {e}")
    return None


def run_sudo_command(command, password):
    """Run a command with sudo using the provided password."""
    try:
        process = subprocess.Popen(
            ["sudo", "-S"] + command.split(),
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        stdout, stderr = process.communicate(input=password + "\n", timeout=30)
        return process.returncode == 0
    except Exception as e:
        print(f"Command error: {e}")
        return False


class ProgressDialog(Gtk.Window):
    """A progress dialog for firewall operations."""
    
    def __init__(self, parent, title, steps, password):
        super().__init__(title=title)
        self.set_transient_for(parent)
        self.set_modal(True)
        self.set_default_size(400, 100)
        self.set_resizable(False)
        
        self.steps = steps
        self.password = password
        self.current_step = 0
        self.cancelled = False
        self.success = True
        
        # Main container
        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)
        
        # Status label
        self.status_label = Gtk.Label(label="Starting...")
        self.status_label.set_xalign(0)
        box.append(self.status_label)
        
        # Progress bar
        self.progress_bar = Gtk.ProgressBar()
        self.progress_bar.set_show_text(True)
        box.append(self.progress_bar)
        
        self.set_child(box)
        
    def run_steps(self, on_complete):
        """Execute steps in a background thread."""
        def worker():
            for i, (command, message) in enumerate(self.steps):
                if self.cancelled:
                    break
                    
                # Update UI from main thread
                progress = (i + 1) / len(self.steps)
                GLib.idle_add(self.update_progress, progress, message)
                
                # Execute command with sudo
                if not run_sudo_command(command, self.password):
                    self.success = False
                
                # Small delay for visual feedback
                GLib.usleep(1500000)  # 1.5 seconds
            
            # Complete
            GLib.idle_add(self.on_finished, on_complete)
        
        thread = threading.Thread(target=worker, daemon=True)
        thread.start()
    
    def update_progress(self, fraction, message):
        """Update progress bar and label."""
        self.progress_bar.set_fraction(fraction)
        self.progress_bar.set_text(f"{int(fraction * 100)}%")
        self.status_label.set_label(message)
        return False
    
    def on_finished(self, callback):
        """Called when all steps complete."""
        self.close()
        if callback:
            callback(self.success)
        return False


class FirewallConfigApp(Adw.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 = Adw.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("firewall-config")
            except:
                pass
            
            # Main container
            main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
            
            # Header bar
            header = Adw.HeaderBar()
            header.set_show_end_title_buttons(True)
            main_box.append(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)
            
            main_box.append(content_box)
            self.window.set_content(main_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 with progress dialog."""
        password = get_sudo_password()
        if password is None:
            return
        
        steps = [
            ("systemctl unmask --now firewalld", "Unmasking Firewall..."),
            ("systemctl enable firewalld", "Enabling Firewall..."),
            ("systemctl start firewalld", "Starting Firewall..."),
        ]
        
        dialog = ProgressDialog(self.window, "FirewallD - Enable", steps, password)
        dialog.present()
        dialog.run_steps(lambda success: self.show_message(
            "Firewall Enabled" if success else "Error",
            "The Firewall has been enabled successfully." if success 
            else "Failed to enable the firewall. Check your password."
        ))
    
    def on_disable_clicked(self, button):
        """Disable the firewall with progress dialog."""
        password = get_sudo_password()
        if password is None:
            return
        
        steps = [
            ("systemctl stop firewalld", "Stopping Firewall..."),
            ("systemctl disable firewalld", "Disabling Firewall..."),
            ("systemctl mask --now firewalld", "Masking Firewall..."),
        ]
        
        dialog = ProgressDialog(self.window, "FirewallD - Disable", steps, password)
        dialog.present()
        dialog.run_steps(lambda success: self.show_message(
            "Firewall Disabled" if success else "Error",
            "The Firewall has been disabled successfully." if success
            else "Failed to disable the firewall. Check your password."
        ))
    
    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"
        
        # Create status dialog
        dialog = Adw.MessageDialog(
            transient_for=self.window,
            modal=True,
            heading="Firewall Status",
        )
        
        # Create styled label for status
        status_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
        
        intro_label = Gtk.Label(label="The Firewall is currently:")
        status_box.append(intro_label)
        
        status_label = Gtk.Label()
        status_label.set_markup(
            f"<b><span foreground='{'green' if is_active else 'red'}' "
            f"size='large'>{status_text}</span></b>"
        )
        status_box.append(status_label)
        
        dialog.set_extra_child(status_box)
        dialog.add_response("ok", "OK")
        dialog.set_default_response("ok")
        dialog.present()
    
    def show_message(self, title, message):
        """Show a simple message dialog."""
        dialog = Adw.MessageDialog(
            transient_for=self.window,
            modal=True,
            heading=title,
            body=message
        )
        dialog.add_response("ok", "OK")
        dialog.set_default_response("ok")
        dialog.present()


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


if __name__ == "__main__":
    main()
