#!/usr/bin/env python3
#--------------------------------------------------------------------------------------------------------
# Name: Linux Lite - Lite Info (Hardware Database Upload)
# Architecture: amd64
# Author: Jerry Bezencon
# Website: https://www.linuxliteos.com
# Language: Python/GTK4
# Licence: GPLv2
#--------------------------------------------------------------------------------------------------------

import os
import sys
import signal
import subprocess
import threading
from pathlib import Path
from urllib.request import Request, urlopen
from urllib.parse import urlencode

os.environ['GSK_RENDERER'] = 'cairo'

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

APP_ID = "com.linuxlite.LiteInfo"
APP_NAME = "Lite Info"
ICON_NAME = "lite-info"
UPLOAD_URL = "https://www.linuxliteos.com/hwdb/upload.php"
HWDB_PAGE = "https://www.linuxliteos.com/hardware.php"


def is_virtual_machine():
    try:
        return "hypervisor" in Path("/proc/cpuinfo").read_text()
    except Exception:
        return False


def gather_hardware_info():
    try:
        result = subprocess.run(
            ["inxi", "-c", "0", "-ACdGMNSz"],
            capture_output=True, text=True, timeout=60
        )
        if result.returncode == 0 and result.stdout.strip():
            return result.stdout
        return None
    except Exception:
        return None


def check_internet():
    """True if the machine can reach the Internet. Same lightweight probe
    the rest of the Lite suite uses. Never raises. Call OFF the GTK main
    thread — curl can block up to 10s."""
    try:
        p = subprocess.run(
            ["curl", "-sk", "google.com"], capture_output=True, timeout=10)
        return p.returncode == 0
    except Exception:
        return False


def upload_report(data):
    try:
        payload = urlencode({"report": data}).encode("utf-8")
        req = Request(UPLOAD_URL, data=payload, method="POST")
        req.add_header("Content-Type", "application/x-www-form-urlencoded")
        req.add_header("User-Agent", "LiteInfo/8.0")
        with urlopen(req, timeout=30) as resp:
            return resp.status == 200
    except Exception:
        return False


class LiteInfoApp(Gtk.Application):
    def __init__(self):
        super().__init__(application_id=APP_ID, flags=Gio.ApplicationFlags.FLAGS_NONE)
        self.window = None

    def do_activate(self):
        if self.window:
            self.window.present()
            return

        self._load_css()

        if is_virtual_machine():
            self._show_vm_dialog()
            return

        self._show_confirm_dialog()

    def _load_css(self):
        css = b"""
        .progress-label {
            font-size: 14px;
        }
        """
        provider = Gtk.CssProvider()
        provider.load_from_data(css)
        Gtk.StyleContext.add_provider_for_display(
            Gdk.Display.get_default(), provider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

    def _show_vm_dialog(self):
        self.window = Gtk.ApplicationWindow(application=self, title=APP_NAME)
        self.window.set_icon_name(ICON_NAME)
        self.window.set_default_size(420, 160)
        self.window.set_resizable(False)

        hb = Gtk.HeaderBar()
        hb.set_title_widget(Gtk.Label(label=APP_NAME))
        self.window.set_titlebar(hb)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16,
                       margin_top=24, margin_bottom=24, margin_start=24, margin_end=24)

        msg = Gtk.Label(label="Sorry, the Hardware Database does not accept\nsubmissions from a virtual machine.")
        msg.set_justify(Gtk.Justification.CENTER)
        msg.set_halign(Gtk.Align.CENTER)
        box.append(msg)

        btn = Gtk.Button(label="OK")
        btn.set_halign(Gtk.Align.CENTER)
        btn.connect("clicked", lambda b: self.quit())
        box.append(btn)

        self.window.set_child(box)
        self.window.present()

    def _show_confirm_dialog(self):
        self.window = Gtk.ApplicationWindow(application=self, title=APP_NAME)
        self.window.set_icon_name(ICON_NAME)
        self.window.set_default_size(500, 340)
        self.window.set_resizable(False)

        hb = Gtk.HeaderBar()
        hb.set_title_widget(Gtk.Label(label=APP_NAME))
        self.window.set_titlebar(hb)

        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16,
                       margin_top=24, margin_bottom=24, margin_start=24, margin_end=24)

        info_text = (
            "This tool is designed to share basic hardware information\n"
            "with the rest of the community.\n\n"
            "Information like CPU, Memory, Graphics, Sound and Network\n"
            "will be uploaded to the Linux Lite Hardware Database webpage.\n\n"
            "You must be connected to the internet before running this tool.\n\n"
            "No Private information will be gathered."
        )
        info_label = Gtk.Label(label=info_text)
        info_label.set_justify(Gtk.Justification.CENTER)
        info_label.set_halign(Gtk.Align.CENTER)
        info_label.set_wrap(True)
        box.append(info_label)

        btn_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
        btn_row.set_halign(Gtk.Align.CENTER)

        begin_btn = Gtk.Button(label="Begin")
        begin_btn.add_css_class("suggested-action")
        begin_btn.connect("clicked", lambda b: self._start_upload())
        btn_row.append(begin_btn)

        cancel_btn = Gtk.Button(label="Cancel")
        cancel_btn.connect("clicked", lambda b: self.quit())
        btn_row.append(cancel_btn)

        box.append(btn_row)
        self.window.set_child(box)
        self.window.present()

    def _start_upload(self):
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16,
                       margin_top=24, margin_bottom=24, margin_start=24, margin_end=24)

        self.progress_label = Gtk.Label(label="Checking internet connection...")
        self.progress_label.add_css_class("progress-label")
        self.progress_label.set_halign(Gtk.Align.CENTER)
        box.append(self.progress_label)

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

        self.window.set_child(box)

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

    def _update_progress(self, fraction, text):
        self.progress_bar.set_fraction(fraction)
        self.progress_label.set_label(text)

    def _upload_worker(self):
        # Verify connectivity before doing any work — otherwise the upload
        # silently waits out a 30s socket timeout and reports a generic
        # failure. A dedicated check gives the user a clear message.
        if not check_internet():
            GLib.idle_add(self._show_no_internet)
            return

        GLib.idle_add(self._update_progress, 0.1, "Collating Hardware Information...")

        hw_info = gather_hardware_info()
        if hw_info is None:
            GLib.idle_add(self._show_error,
                "Failed to gather hardware information.\nPlease ensure 'inxi' is installed.")
            return

        GLib.idle_add(self._update_progress, 0.5, "Uploading system info to the Hardware Database...")

        ok = upload_report(hw_info)

        if ok:
            GLib.idle_add(self._update_progress, 1.0, "Upload complete!")
            GLib.idle_add(self._show_success)
        else:
            GLib.idle_add(self._show_error,
                "Failed to upload hardware information.\n"
                "Please check your internet connection and try again.")

    def _show_success(self):
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16,
                       margin_top=24, margin_bottom=24, margin_start=24, margin_end=24)

        msg = Gtk.Label()
        msg.set_markup(
            "Thank you for sharing your hardware information\n"
            "with the community.\n\n"
            "Results will be available within the next hour at\n\n"
            f"<a href=\"{HWDB_PAGE}\">{HWDB_PAGE}</a>"
        )
        msg.set_justify(Gtk.Justification.CENTER)
        msg.set_halign(Gtk.Align.CENTER)
        box.append(msg)

        btn = Gtk.Button(label="Close")
        btn.set_halign(Gtk.Align.CENTER)
        btn.connect("clicked", lambda b: self.quit())
        box.append(btn)

        self.window.set_child(box)

    def _show_no_internet(self):
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16,
                       margin_top=24, margin_bottom=24, margin_start=24, margin_end=24)

        msg = Gtk.Label()
        msg.set_markup(
            "<b>No Internet Connection</b>\n\n"
            "Your computer is not connected to the Internet.\n\n"
            "Lite Info needs a connection to upload your hardware\n"
            "information to the Hardware Database. Please check your\n"
            "connection and try again.")
        msg.set_justify(Gtk.Justification.CENTER)
        msg.set_halign(Gtk.Align.CENTER)
        box.append(msg)

        btn = Gtk.Button(label="Close")
        btn.set_halign(Gtk.Align.CENTER)
        btn.connect("clicked", lambda b: self.quit())
        box.append(btn)

        self.window.set_child(box)

    def _show_error(self, message):
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16,
                       margin_top=24, margin_bottom=24, margin_start=24, margin_end=24)

        msg = Gtk.Label(label=message)
        msg.set_justify(Gtk.Justification.CENTER)
        msg.set_halign(Gtk.Align.CENTER)
        box.append(msg)

        btn = Gtk.Button(label="Close")
        btn.set_halign(Gtk.Align.CENTER)
        btn.connect("clicked", lambda b: self.quit())
        box.append(btn)

        self.window.set_child(box)


def main():
    signal.signal(signal.SIGINT, lambda s, f: sys.exit(0))
    return LiteInfoApp().run(sys.argv)


if __name__ == '__main__':
    sys.exit(main())
