import ctypes
import ctypes.wintypes
import datetime
import hashlib
import json
import mimetypes
import os
import struct
import subprocess
import sys
import tempfile
import threading
import time
import tkinter as tk
import urllib.error
import urllib.request
import uuid
from tkinter import filedialog, messagebox, scrolledtext, ttk
import xml.etree.ElementTree as ET

# Check admin rights
def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except Exception:
        return False

def request_admin():
    if not is_admin():
        try:
            ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, ' "%s"' % ' '.join(sys.argv), None, 1)
            sys.exit(0)
        except Exception:
            messagebox.showerror("Admin Required", "This app requires administrator privileges to run properly.")
            sys.exit(1)

try:
    from PIL import ImageGrab
except ImportError:
    ImageGrab = None
    try:
        subprocess.check_call([sys.executable, "-m", "pip", "install", "pillow", "-q"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=30)
        from PIL import ImageGrab
    except Exception:
        ImageGrab = None

try:
    import wmi
except ImportError:
    try:
        subprocess.check_call([sys.executable, "-m", "pip", "install", "wmi", "-q"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=30)
        import wmi
    except Exception:
        wmi = None

try:
    import psutil
except ImportError:
    psutil = None
    try:
        subprocess.check_call([sys.executable, "-m", "pip", "install", "psutil", "-q"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=30)
        import psutil
    except Exception:
        psutil = None

DISCORD_WEBHOOK_URL = "Add your Discord webhook URL here"


class HardwareIdentifier:
    @staticmethod
    def get_hardware_id():
        if not wmi:
            return "UNKNOWN"
        try:
            c = wmi.WMI()
            cpu_id = ""
            bios_id = ""
            disk_id = ""
            motherboard_id = ""
            
            for processor in c.Win32_Processor():
                cpu_id = processor.ProcessorId.strip()
                break
            
            for bios in c.Win32_BIOS():
                bios_id = bios.SerialNumber.strip()
                break
            
            for disk in c.Win32_LogicalDisk():
                if disk.Name == "C:":
                    disk_id = disk.VolumeSerialNumber.strip()
                    break
            
            for board in c.Win32_BaseBoard():
                motherboard_id = board.SerialNumber.strip()
                break
            
            hw_string = f"{cpu_id}|{bios_id}|{disk_id}|{motherboard_id}"
            return hw_string
        except Exception:
            return "UNKNOWN"

class SystemInfo:
    @staticmethod
    def get_cpu_speed():
        if not wmi:
            return "N/A"
        try:
            c = wmi.WMI()
            for processor in c.Win32_Processor():
                current_speed = processor.CurrentClockSpeed
                max_speed = processor.MaxClockSpeed
                return f"{current_speed}MHz / {max_speed}MHz"
        except Exception:
            return "N/A"
    
    @staticmethod
    def get_antivirus_info():
        if not wmi:
            return ["Unable to determine"]
        try:
            c = wmi.WMI(namespace="root\\SecurityCenter2")
            antivirus_list = []
            for av in c.AntiVirusProduct():
                name = av.displayName
                state = av.productState
                
                # Check real-time protection status from productState
                rt_protection = "Active" if (state & 0x1000) else "Inactive"
                
                antivirus_list.append(f"{name} (Real-time protection: {rt_protection})")
            return antivirus_list if antivirus_list else ["No antivirus detected"]
        except Exception:
            return ["Unable to determine"]
    
    @staticmethod
    def get_file_hash(file_path, algorithm='sha256'):
        try:
            hash_func = hashlib.new(algorithm)
            with open(file_path, 'rb') as f:
                for chunk in iter(lambda: f.read(4096), b''):
                    hash_func.update(chunk)
            return hash_func.hexdigest()
        except Exception:
            return "ERROR"
    
    @staticmethod
    def get_file_authenticode(exe_path):
        try:
            ps_script = f'''
$sig = Get-AuthenticodeSignature -FilePath '{exe_path}'
Write-Output "$($sig.Status)|$($sig.SignerCertificate.Issuer)|$($sig.SignerCertificate.Subject)"
'''
            result = subprocess.run(["powershell", "-Command", ps_script], 
                                  capture_output=True, text=True, timeout=5)
            if result.stdout:
                parts = result.stdout.strip().split('|')
                return parts[0] if parts else "Unknown"
            return "Unsigned"
        except Exception:
            return "Unknown"

class ScreenshotCapture:
    screenshot_dir = None
    
    @staticmethod
    def init_screenshot_dir(base_dir):
        ScreenshotCapture.screenshot_dir = os.path.join(base_dir, "screenshots")
        os.makedirs(ScreenshotCapture.screenshot_dir, exist_ok=True)
    
    @staticmethod
    def capture_screenshot(prefix="screenshot"):
        if not ImageGrab or not ScreenshotCapture.screenshot_dir:
            return None
        try:
            timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3]
            filename = f"{prefix}_{timestamp}.png"
            filepath = os.path.join(ScreenshotCapture.screenshot_dir, filename)
            img = ImageGrab.grab()
            img.save(filepath)
            return filepath
        except Exception:
            return None



class WindowsProcessWindowTracker:
    def __init__(self, sample_interval=1.0, screenshot_interval=60):
        self.sample_interval = sample_interval
        self.screenshot_interval = screenshot_interval
        self.running = False
        self.lock = threading.Lock()
        self.process_log = []
        self.window_log = []
        self.virus_log = []
        self.hardware_id = HardwareIdentifier.get_hardware_id()
        self.antivirus_info = SystemInfo.get_antivirus_info()
        self.process_signatures = {}
        self.start_time = None
        self.stop_time = None
        self.thread = None
        self.previous_pids = set()
        self.previous_windows = {}
        self.screenshot_thread = None
        self.screenshot_count = 0
        self._init_native_functions()

    def _init_native_functions(self):
        self.user32 = ctypes.WinDLL("user32", use_last_error=True)
        self.kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
        self.psapi = ctypes.WinDLL("Psapi", use_last_error=True)

        self.EnumWindows = self.user32.EnumWindows
        self.EnumWindows.argtypes = [ctypes.WINFUNCTYPE(ctypes.wintypes.BOOL, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM), ctypes.wintypes.LPARAM]
        self.EnumWindows.restype = ctypes.wintypes.BOOL

        self.GetWindowTextLengthW = self.user32.GetWindowTextLengthW
        self.GetWindowTextLengthW.argtypes = [ctypes.wintypes.HWND]
        self.GetWindowTextW = self.user32.GetWindowTextW
        self.GetWindowTextW.argtypes = [ctypes.wintypes.HWND, ctypes.c_wchar_p, ctypes.c_int]
        self.GetWindowThreadProcessId = self.user32.GetWindowThreadProcessId
        self.GetWindowThreadProcessId.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(ctypes.wintypes.DWORD)]
        self.IsWindowVisible = self.user32.IsWindowVisible
        self.IsWindowVisible.argtypes = [ctypes.wintypes.HWND]

        self.OpenProcess = self.kernel32.OpenProcess
        self.OpenProcess.argtypes = [ctypes.wintypes.DWORD, ctypes.wintypes.BOOL, ctypes.wintypes.DWORD]
        self.OpenProcess.restype = ctypes.wintypes.HANDLE
        self.CloseHandle = self.kernel32.CloseHandle
        self.CloseHandle.argtypes = [ctypes.wintypes.HANDLE]
        self.QueryFullProcessImageNameW = self.kernel32.QueryFullProcessImageNameW
        self.QueryFullProcessImageNameW.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.c_wchar_p, ctypes.POINTER(ctypes.wintypes.DWORD)]
        self.QueryFullProcessImageNameW.restype = ctypes.wintypes.BOOL
        self.GetProcessTimes = self.kernel32.GetProcessTimes
        self.GetProcessTimes.argtypes = [ctypes.wintypes.HANDLE, ctypes.POINTER(ctypes.wintypes.FILETIME), ctypes.POINTER(ctypes.wintypes.FILETIME), ctypes.POINTER(ctypes.wintypes.FILETIME), ctypes.POINTER(ctypes.wintypes.FILETIME)]
        self.GetProcessTimes.restype = ctypes.wintypes.BOOL
        self.EnumProcesses = self.psapi.EnumProcesses
        self.EnumProcesses.argtypes = [ctypes.POINTER(ctypes.wintypes.DWORD), ctypes.wintypes.DWORD, ctypes.POINTER(ctypes.wintypes.DWORD)]
        self.EnumProcesses.restype = ctypes.wintypes.BOOL

    def _timestamp(self):
        return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    def start(self):
        with self.lock:
            if self.running:
                return
            self.running = True
            self.start_time = datetime.datetime.now()
            self.process_log.clear()
            self.window_log.clear()
            self.virus_log.clear()
            self.screenshot_count = 0
            self.process_signatures.clear()
            ScreenshotCapture.init_screenshot_dir(tempfile.gettempdir())
            current_processes = self._list_processes()
            current_windows = self._list_windows()
            self.previous_pids = set(current_processes.keys())
            self.previous_windows = current_windows
            timestamp = self._timestamp()
            for pid, name in current_processes.items():
                self.process_log.append((timestamp, "PROCESS_ACTIVE", pid, name))
                self._flag_unsigned_process(pid, name)
                self._flag_suspicious_process(pid, name)
            for hwnd, (pid, title) in current_windows.items():
                self.window_log.append((timestamp, "WINDOW_ACTIVE", hwnd, pid, title))
            self.thread = threading.Thread(target=self._sample_loop, daemon=True)
            self.thread.start()
            self.screenshot_thread = threading.Thread(target=self._screenshot_loop, daemon=True)
            self.screenshot_thread.start()
            init_thread = threading.Thread(target=self._init_signatures_and_detections, daemon=True)
            init_thread.start()

    def stop(self):
        with self.lock:
            if not self.running:
                return
            self.running = False
        if self.thread:
            self.thread.join(timeout=3)
            self.thread = None
        self.stop_time = datetime.datetime.now()

    def _sample_loop(self):
        while True:
            with self.lock:
                if not self.running:
                    break
            self._sample_once()
            time.sleep(self.sample_interval)

    def _screenshot_loop(self):
        while self.running:
            try:
                time.sleep(self.screenshot_interval)
                if self.running:
                    ScreenshotCapture.capture_screenshot("auto")
                    self.screenshot_count += 1
            except Exception:
                pass

    def _init_signatures_and_detections(self):
        try:
            self.collect_initial_detections()
            self._collect_process_signatures()
        except Exception:
            pass

    def _sample_once(self):
        timestamp = self._timestamp()
        current_processes = self._list_processes()
        current_pids = set(current_processes.keys())
        started = current_pids - self.previous_pids
        stopped = self.previous_pids - current_pids
        for pid in sorted(started):
            proc_name = current_processes.get(pid, "<unknown>")
            self.process_log.append((timestamp, "PROCESS_STARTED", pid, proc_name))
            self._flag_unsigned_process(pid, proc_name)
            self._flag_suspicious_process(pid, proc_name)
        for pid in sorted(stopped):
            self.process_log.append((timestamp, "PROCESS_EXITED", pid, "<ended>"))
        self.previous_pids = current_pids

        current_windows = self._list_windows()
        started_windows = {hwnd: info for hwnd, info in current_windows.items() if hwnd not in self.previous_windows}
        closed_windows = {hwnd: info for hwnd, info in self.previous_windows.items() if hwnd not in current_windows}
        for hwnd, (pid, title) in started_windows.items():
            self.window_log.append((timestamp, "WINDOW_OPENED", hwnd, pid, title))
        for hwnd, (pid, title) in closed_windows.items():
            self.window_log.append((timestamp, "WINDOW_CLOSED", hwnd, pid, title))
        self.previous_windows = current_windows

    def _list_processes(self):
        if psutil is not None:
            return self._list_processes_psutil()
        return self._list_processes_native()

    def _list_processes_psutil(self):
        processes = {}
        if psutil is None:
            return processes
        for proc in psutil.process_iter(attrs=["pid", "name"]):
            try:
                processes[proc.info["pid"]] = proc.info["name"] or "<unknown>"
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                continue
        return processes

    def _list_processes_native(self):
        processes = {}
        array_type = ctypes.wintypes.DWORD * 4096
        pid_array = array_type()
        bytes_returned = ctypes.wintypes.DWORD()
        if not self.EnumProcesses(pid_array, ctypes.sizeof(pid_array), ctypes.byref(bytes_returned)):
            return processes
        num_pids = bytes_returned.value // ctypes.sizeof(ctypes.wintypes.DWORD)
        for index in range(num_pids):
            pid = pid_array[index]
            if pid == 0:
                continue
            handle = self.OpenProcess(0x1000 | 0x0010, False, pid)
            if not handle:
                continue
            try:
                buffer = ctypes.create_unicode_buffer(260)
                size = ctypes.wintypes.DWORD(260)
                if self.QueryFullProcessImageNameW(handle, 0, buffer, ctypes.byref(size)):
                    processes[pid] = os.path.basename(buffer.value) or "<unknown>"
            finally:
                self.CloseHandle(handle)
        return processes

    def _list_windows(self):
        windows = {}
        EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.wintypes.BOOL, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM)

        def enum_proc(hwnd, lParam):
            if not self.IsWindowVisible(hwnd):
                return True
            length = self.GetWindowTextLengthW(hwnd)
            if length == 0:
                return True
            buffer = ctypes.create_unicode_buffer(length + 1)
            self.GetWindowTextW(hwnd, buffer, length + 1)
            title = buffer.value.strip()
            if not title:
                return True
            pid = ctypes.wintypes.DWORD()
            self.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
            windows[hwnd] = (pid.value, title)
            return True

        callback = EnumWindowsProc(enum_proc)
        self.EnumWindows(callback, 0)
        return windows

    def _get_process_executable_path(self, pid):
        if psutil:
            try:
                proc = psutil.Process(pid)
                return proc.exe()
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess, OSError):
                pass
        return self._get_process_executable_path_native(pid)

    def _get_process_executable_path_native(self, pid):
        PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
        handle = self.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
        if not handle:
            return None
        try:
            buffer = ctypes.create_unicode_buffer(260)
            size = ctypes.wintypes.DWORD(260)
            if self.QueryFullProcessImageNameW(handle, 0, buffer, ctypes.byref(size)):
                return buffer.value
        finally:
            self.CloseHandle(handle)
        return None

    def _has_pe_certificate(self, file_path):
        try:
            with open(file_path, "rb") as f:
                header = f.read(64)
                if len(header) < 64 or header[:2] != b"MZ":
                    return False
                e_lfanew = struct.unpack_from("<I", header, 0x3c)[0]
                f.seek(e_lfanew)
                pe_header = f.read(24)
                if len(pe_header) < 24 or pe_header[:4] != b"PE\0\0":
                    return False
                f.seek(e_lfanew + 4 + 20)
                optional_header = f.read(256)
                if len(optional_header) < 128:
                    return False
                magic = struct.unpack_from("<H", optional_header, 0)[0]
                if magic == 0x10B:
                    dir_offset = 96
                elif magic == 0x20B:
                    dir_offset = 112
                else:
                    return False
                security_entry_offset = dir_offset + 4 * 8
                if len(optional_header) < security_entry_offset + 8:
                    return False
                _, cert_size = struct.unpack_from("<II", optional_header, security_entry_offset)
                return cert_size != 0
        except OSError:
            return False

    def _is_process_signed(self, pid):
        exe_path = self._get_process_executable_path(pid)
        if not exe_path:
            return False
        return self._has_pe_certificate(exe_path)

    def _flag_unsigned_process(self, pid, name):
        try:
            if not self._is_process_signed(pid):
                self.process_log.append((self._timestamp(), "PROCESS_UNSIGNED", pid, name))
        except Exception:
            pass

    def _classify_suspicious_process(self, pid, name):
        name_lower = name.lower() if name else ""
        cheat_keywords = ["cheat", "hack", "inject", "trainer", "aimbot", "esp", "wallhack", "gamehack", "injector", "bypass", "spoof", "dllhost", "speedhack"]
        suspicious_keywords = ["cracker", "keygen", "bot", "exploit", "untrusted", "unknown", "suspicious"]
        suspicious_path_parts = ["\\temp\\", "\\downloads\\", "\\appdata\\local\\temp\\", "\\appdata\\roaming\\", "\\programdata\\", "\\tmp\\"]

        for keyword in cheat_keywords:
            if keyword in name_lower:
                return "PROCESS_CHEAT"
        for keyword in suspicious_keywords:
            if keyword in name_lower:
                return "PROCESS_SUSPICIOUS"

        exe_path = self._get_process_executable_path(pid)
        if exe_path:
            exe_lower = exe_path.lower()
            for keyword in cheat_keywords:
                if keyword in exe_lower:
                    return "PROCESS_CHEAT"
            for keyword in suspicious_keywords:
                if keyword in exe_lower:
                    return "PROCESS_SUSPICIOUS"
            for part in suspicious_path_parts:
                if part in exe_lower:
                    return "PROCESS_SUSPICIOUS"

        return None

    def _flag_suspicious_process(self, pid, name):
        try:
            event = self._classify_suspicious_process(pid, name)
            if event:
                self.process_log.append((self._timestamp(), event, pid, name))
        except Exception:
            pass

    def _get_antivirus_detections(self):
        try:
            ps_script = r"""
$logName = 'Microsoft-Windows-Windows Defender/Operational'
$events = Get-WinEvent -LogName $logName -FilterXPath "*[System[EventID=1116 or EventID=1117 or EventID=1118 or EventID=1119]]" -ErrorAction SilentlyContinue -MaxEvents 100 | Select-Object -Last 50
foreach ($event in $events) {
    $timestamp = $event.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
    $xml = [xml]$event.ToXml()
    $fields = @{}
    foreach ($data in $xml.Event.EventData.Data) {
        if ($data.Name) {
            $fields[$data.Name] = $data.'#text'
        }
    }
    $threatName = $fields['Threat Name']
    if (-not $threatName) {
        $threatName = $fields['Threat']
    }
    if (-not $threatName) {
        $threatName = "Unknown Threat"
    }
    $exeName = $fields['File Name']
    if (-not $exeName) {
        $exeName = $fields['FileName']
    }
    if (-not $exeName) {
        $exeName = $fields['Path']
    }
    if (-not $exeName) {
        $exeName = $fields['File Path']
    }
    if (-not $exeName) {
        $exeName = "Unknown EXE"
    }
    if ($threatName) {
        Write-Output "$timestamp|$threatName|$exeName"
    }
}
"""
            result = subprocess.run([
                "powershell",
                "-Command",
                ps_script
            ], capture_output=True, text=True, timeout=10)
            
            detections = []
            if result.stdout:
                for line in result.stdout.strip().split('\n'):
                    if '|' in line and line.strip():
                        parts = line.split('|', 2)
                        if len(parts) == 3:
                            timestamp = parts[0].strip()
                            threat = parts[1].strip()
                            exe_name = parts[2].strip()
                            if threat and timestamp:
                                detections.append((timestamp, threat, exe_name))
            return detections
        except Exception:
            return []

    def collect_initial_detections(self):
        detections = self._get_antivirus_detections()
        timestamp = self._timestamp()
        for det_time, threat, exe_name in detections:
            self.virus_log.append((det_time, "THREAT_DETECTED", threat, exe_name))

    def _collect_process_signatures(self):
        try:
            processes = self._list_processes()
            for pid, name in processes.items():
                exe_path = self._get_process_executable_path(pid)
                if exe_path and os.path.exists(exe_path):
                    sha256 = SystemInfo.get_file_hash(exe_path, 'sha256')
                    authenticode = SystemInfo.get_file_authenticode(exe_path)
                    self.process_signatures[name] = {
                        'path': exe_path,
                        'sha256': sha256,
                        'authenticode': authenticode,
                        'start_time': self._timestamp()
                    }
        except Exception:
            pass

    def save_report(self, path):
        with open(path, "w", encoding="utf-8") as report:
            report.write("LiveAC Process and Application Activity Log\n")
            report.write("=" * 80 + "\n")
            report.write("Generated: {}\n".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
            report.write("Start time: {}\n".format(self.start_time.strftime("%Y-%m-%d %H:%M:%S") if self.start_time else "N/A"))
            report.write("Stop time: {}\n".format(self.stop_time.strftime("%Y-%m-%d %H:%M:%S") if self.stop_time else "N/A"))
            report.write("\n")
            
            report.write("SYSTEM INFORMATION:\n")
            report.write("-" * 80 + "\n")
            report.write(f"Hardware ID: {self.hardware_id}\n")
            report.write(f"Screenshots Captured: {self.screenshot_count}\n")
            report.write(f"Antivirus Products: {', '.join(self.antivirus_info)}\n")
            report.write("\n")
            
            report.write("PROCESS SIGNATURES AT START:\n")
            report.write("-" * 80 + "\n")
            report.write("Process Name | Authenticode Status | SHA256 Hash\n")
            report.write("-" * 80 + "\n")
            for proc_name, sig_info in sorted(self.process_signatures.items()):
                report.write(f"{proc_name}\n")
                report.write(f"  Authenticode: {sig_info['authenticode']}\n")
                report.write(f"  SHA256: {sig_info['sha256']}\n")
                report.write(f"  Start: {sig_info['start_time']}\n")
            report.write("\n")
            
            report.write("Antivirus/Threat Detections:\n")
            report.write("Timestamp    | Event          | Threat Name                 | EXE Name\n")
            report.write("-------------------------------------------------------------------------------\n")
            if self.virus_log:
                for timestamp, event, threat, exe_name in self.virus_log:
                    report.write(f"{timestamp} | {event:<14} | {threat:<26} | {exe_name}\n")
            else:
                report.write("No threats detected\n")
            report.write("\n")
            report.write("Process activity events:\n")
            report.write("Timestamp    | Event          | PID     | Name\n")
            report.write("------------------------------------------------------------\n")
            for timestamp, event, pid, name in self.process_log:
                report.write(f"{timestamp} | {event:<14} | {pid:<7} | {name}\n")
            report.write("\n")
            report.write("Window/application activity events:\n")
            report.write("Timestamp    | Event          | HWND        | PID     | Title\n")
            report.write("-------------------------------------------------------------------------------\n")
            for entry in self.window_log:
                timestamp, event, hwnd, pid, title = entry
                report.write(f"{timestamp} | {event:<14} | {hwnd:<10} | {pid:<7} | {title}\n")
            report.write("\n")
            report.write("All Active Executables at Stop:\n")
            report.write("-" * 80 + "\n")
            final_procs = self._list_processes()
            for pid, name in sorted(final_procs.items(), key=lambda x: x[1].lower()):
                report.write(f"{name:<40} [PID: {pid}]\n")
            report.write("\n")
            report.write("Last Activity (Recent Events):\n")
            report.write("-" * 130 + "\n")
            report.write("Timestamp            | Event Type               | Details                              | PID    | Path/Info\n")
            report.write("-" * 130 + "\n")
            
            all_events = []
            
            # Add process events
            for timestamp, event_type, pid, name in self.process_log[-50:]:
                all_events.append((timestamp, event_type, name, str(pid), ""))
            
            # Add window events
            for timestamp, event_type, hwnd, pid, title in self.window_log[-50:]:
                all_events.append((timestamp, event_type, title, str(pid), f"HWND: 0x{hwnd:08X}"))
            
            # Add threat events
            for timestamp, event_type, threat, exe_name in self.virus_log[-50:]:
                all_events.append((timestamp, event_type, exe_name, "", threat))
            
            # Sort by timestamp (most recent first) - take last 30
            all_events.sort(key=lambda x: x[0], reverse=True)
            
            if all_events:
                for timestamp, event_type, details, pid, path_info in all_events[:30]:
                    details_str = details[:35].ljust(35)
                    pid_str = pid[:6].ljust(6)
                    line = f"{timestamp} | {event_type[:24].ljust(24)} | {details_str} | {pid_str} | {path_info}\n"
                    report.write(line)
            else:
                report.write("No activity recorded\n")
            
            report.write("-" * 130 + "\n")


class Application(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("LiveAC - System Monitor")
        self.geometry("950x600")
        self.minsize(950, 600)
        
        # Modern 2026 color scheme
        self.bg_primary = "#0d1117"      # Dark background
        self.bg_secondary = "#161b22"    # Secondary darker
        self.bg_tertiary = "#21262d"     # Tertiary darker
        self.fg_primary = "#e6edf3"      # Light text
        self.fg_secondary = "#8b949e"    # Muted text
        self.accent_blue = "#58a6ff"     # Modern blue
        self.accent_green = "#3fb950"    # Modern green
        self.accent_red = "#f85149"      # Modern red
        self.accent_yellow = "#d29922"   # Modern yellow
        
        self.configure(bg=self.bg_primary)
        
        self.tracker = WindowsProcessWindowTracker(sample_interval=1.0)
        self.admin_status = is_admin()
        self._build_ui()
        self.protocol("WM_DELETE_WINDOW", self._on_close)
        
        # Show credits on startup
        self._append_log("=" * 80)
        self._append_log("█ LiveAC - System Activity Monitor")
        self._append_log("© 2026 Kayen.ac - v1.0 Public Edition")
        self._append_log("=" * 80)
        self._append_log("Ready to monitor. Press START to begin.")
        self._append_log("")


    def _build_ui(self):
        # Header with gradient effect
        header_frame = tk.Frame(self, bg=self.bg_secondary, height=90)
        header_frame.pack(fill="x", padx=0, pady=0)
        header_frame.pack_propagate(False)
        
        # Top bar with accent
        accent_bar = tk.Frame(header_frame, bg=self.accent_blue, height=3)
        accent_bar.pack(fill="x", padx=0, pady=0)
        accent_bar.pack_propagate(False)
        
        title_frame = tk.Frame(header_frame, bg=self.bg_secondary)
        title_frame.pack(fill="both", expand=True, padx=24, pady=12)
        
        # Title with icon style
        title_label = tk.Label(
            title_frame, text="◆ LiveAC", font=("Segoe UI", 28, "bold"), 
            bg=self.bg_secondary, fg=self.accent_blue
        )
        title_label.pack(side="left", anchor="w")
        
        subtitle_label = tk.Label(
            title_frame, text="Real-Time Activity Tracking", font=("Segoe UI", 11), 
            bg=self.bg_secondary, fg=self.fg_secondary
        )
        subtitle_label.pack(side="left", padx=16, anchor="w")
        
        # Control panel with better spacing
        control_frame = tk.Frame(self, bg=self.bg_primary)
        control_frame.pack(fill="x", padx=24, pady=20)
        
        # Button group with modern styling
        button_frame = tk.Frame(control_frame, bg=self.bg_primary)
        button_frame.pack(fill="x", pady=(0, 16))
        
        self.start_button = self._create_modern_button(
            button_frame, "▶ START", self._start, 
            self.accent_green, width=13
        )
        self.start_button.pack(side="left", padx=(0, 10))

        self.stop_button = self._create_modern_button(
            button_frame, "⏹ STOP", self._stop, 
            self.accent_red, width=13, state="disabled"
        )
        self.stop_button.pack(side="left", padx=(0, 10))

        self.save_button = self._create_modern_button(
            button_frame, "💾 SAVE", self._save_report, 
            self.accent_blue, width=13, state="disabled"
        )
        self.save_button.pack(side="left")
        
        # Screenshot interval section
        screenshot_label = tk.Label(
            control_frame, text="Screenshot Interval (seconds)", 
            font=("Segoe UI", 10, "bold"),
            bg=self.bg_primary, fg=self.fg_primary
        )
        screenshot_label.pack(anchor="w", pady=(8, 6))
        
        screenshot_frame = tk.Frame(
            control_frame, bg=self.bg_tertiary, highlightthickness=1, 
            highlightbackground=self.bg_secondary, highlightcolor=self.accent_blue
        )
        screenshot_frame.pack(fill="x")
        
        self.screenshot_entry = tk.Entry(
            screenshot_frame, 
            font=("Segoe UI", 9),
            bg=self.bg_tertiary, fg=self.fg_primary,
            insertbackground=self.accent_blue,
            relief="flat", bd=0, width=10
        )
        self.screenshot_entry.pack(side="left", padx=12, pady=10)
        self.screenshot_entry.insert(0, "60")
        
        # Webhook section with modern design
        webhook_label = tk.Label(
            control_frame, text="Discord Webhook URL", 
            font=("Segoe UI", 10, "bold"),
            bg=self.bg_primary, fg=self.fg_primary
        )
        webhook_label.pack(anchor="w", pady=(8, 6))
        
        webhook_frame = tk.Frame(
            control_frame, bg=self.bg_tertiary, highlightthickness=1, 
            highlightbackground=self.bg_secondary, highlightcolor=self.accent_blue
        )
        webhook_frame.pack(fill="x")
        
        self.webhook_entry = tk.Entry(
            webhook_frame, 
            font=("Segoe UI", 9),
            bg=self.bg_tertiary, fg=self.fg_primary,
            insertbackground=self.accent_blue,
            relief="flat", bd=0
        )
        self.webhook_entry.pack(fill="x", padx=12, pady=10)
        self.webhook_entry.insert(0, DISCORD_WEBHOOK_URL)
        
        # Status section with modern indicator
        status_frame = tk.Frame(self, bg=self.bg_primary)
        status_frame.pack(fill="x", padx=24, pady=(0, 16))
        
        self.status_indicator = tk.Label(
            status_frame, text="●", font=("Arial", 16), 
            bg=self.bg_primary, fg=self.accent_yellow
        )
        self.status_indicator.pack(side="left", padx=(0, 10))
        
        self.status_label = tk.Label(
            status_frame, text="Status: Ready", 
            font=("Segoe UI", 10),
            bg=self.bg_primary, fg=self.fg_primary
        )
        self.status_label.pack(side="left", anchor="w")
        
        # Log section
        log_label = tk.Label(
            self, text="Activity Log", font=("Segoe UI", 12, "bold"),
            bg=self.bg_primary, fg=self.fg_primary
        )
        log_label.pack(anchor="w", padx=24, pady=(8, 6))
        
        # Modern notebook styling
        notebook = ttk.Notebook(self)
        notebook.pack(fill="both", expand=True, padx=24, pady=(0, 20))
        
        style = ttk.Style()
        style.theme_use('clam')
        style.configure('TNotebook', background=self.bg_primary, borderwidth=0)
        style.configure('TNotebook.Tab', background=self.bg_tertiary, foreground=self.fg_primary, padding=[12, 8])
        style.map('TNotebook.Tab', background=[('selected', self.accent_blue)], foreground=[('selected', '#ffffff')])
        
        # Activity Log Tab
        activity_frame = tk.Frame(notebook, bg=self.bg_tertiary)
        notebook.add(activity_frame, text="Activity Log")
        
        self.log_text = scrolledtext.ScrolledText(
            activity_frame, wrap="word", state="disabled", 
            font=("Consolas", 9),
            bg=self.bg_secondary, fg=self.fg_primary,
            insertbackground=self.accent_blue,
            relief="flat", bd=0, highlightthickness=0
        )
        self.log_text.pack(fill="both", expand=True, padx=10, pady=10)
        
        # Footer with modern design
        footer_frame = tk.Frame(self, bg=self.bg_secondary, height=50)
        footer_frame.pack(fill="x", side="bottom")
        footer_frame.pack_propagate(False)
        
        footer_inner = tk.Frame(footer_frame, bg=self.bg_secondary)
        footer_inner.pack(fill="both", expand=True, padx=24, pady=10)
        
        credits_label = tk.Label(
            footer_inner, text="© 2026 Kayen Security Systems", 
            font=("Segoe UI", 9, "bold"),
            bg=self.bg_secondary, fg=self.accent_blue, cursor="hand2"
        )
        credits_label.pack(side="left")
        credits_label.bind("<Button-1>", lambda e: self._open_credit_link())
        
        version_label = tk.Label(
            footer_inner, text="v1.0 Premium Edition", 
            font=("Segoe UI", 9, "bold"),
            bg=self.bg_secondary, fg=self.accent_blue
        )
        version_label.pack(side="right")

    def _create_modern_button(self, parent, text, command, color, width=14, state="normal"):
        """Create modern styled button with hover effects"""
        btn = tk.Button(
            parent, text=text, width=width, 
            font=("Segoe UI", 10, "bold"),
            bg=color, fg="white", 
            activebackground=self._lighten_color(color),
            activeforeground="white",
            command=command, relief="flat", bd=0, cursor="hand2",
            padx=12, pady=10, state=state  # type: ignore
        )
        return btn
    
    def _lighten_color(self, color):
        """Lighten a hex color for hover effect"""
        try:
            color = color.lstrip('#')
            rgb = tuple(int(color[i:i+2], 16) for i in (0, 2, 4))
            rgb = tuple(min(int(c * 1.15), 255) for c in rgb)
            return '#{:02x}{:02x}{:02x}'.format(*rgb)
        except:
            return color

    def _start(self):
        try:
            # Get screenshot interval from input
            try:
                screenshot_interval = int(self.screenshot_entry.get().strip())
                if screenshot_interval < 1:
                    messagebox.showerror("Invalid Input", "Screenshot interval must be at least 1 second.")
                    return
            except ValueError:
                messagebox.showerror("Invalid Input", "Please enter a valid number for screenshot interval.")
                return
            
            # Recreate tracker with new screenshot interval
            self.tracker = WindowsProcessWindowTracker(sample_interval=1.0, screenshot_interval=screenshot_interval)
            self.tracker.start()
            self._append_log("=" * 80)
            self._append_log("© 2026 Kayen.ac - LiveAC v1.0 Public Edition")
            self._append_log("=" * 80)
            self._append_log(f"Monitoring started (Screenshot interval: {screenshot_interval}s).")
            self.status_label.config(text="Status: Running")
            self.status_indicator.config(fg=self.accent_green)
            self.start_button.config(state="disabled")
            self.stop_button.config(state="normal")
            self.save_button.config(state="disabled")
            self.screenshot_entry.config(state="disabled")
        except Exception as e:
            messagebox.showerror("Error", f"Failed to start monitoring: {e}")
            self._append_log(f"ERROR: {e}")


    def _stop(self):
        try:
            self.tracker.stop()
            self._append_log("Monitoring stopped.")
            self.status_label.config(text="Status: Stopped")
            self.status_indicator.config(fg=self.accent_red)
            self.start_button.config(state="normal")
            self.stop_button.config(state="disabled")
            self.save_button.config(state="normal")
            self.screenshot_entry.config(state="normal")

            webhook_url = self.webhook_entry.get().strip()
            if webhook_url and webhook_url != DISCORD_WEBHOOK_URL:
                temp_path = None
                try:
                    fd, temp_path = tempfile.mkstemp(prefix="LiveAC_", suffix=".txt")
                    os.close(fd)
                    self.tracker.save_report(temp_path)
                    self._append_log("Sending report to Discord...")
                    self._send_report_to_webhook(webhook_url, temp_path)
                except Exception as exc:
                    self._append_log(f"Webhook send failed: {exc}")
                finally:
                    if temp_path and os.path.exists(temp_path):
                        try:
                            os.remove(temp_path)
                        except Exception:
                            pass
        except Exception as e:
            messagebox.showerror("Error", f"Failed to stop monitoring: {e}")
            self._append_log(f"ERROR: {e}")

    def _save_report(self):
        default_name = f"LiveAC_log_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
        path = filedialog.asksaveasfilename(
            title="Save activity log",
            defaultextension=".txt",
            filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
            initialfile=default_name,
        )
        if not path:
            return
        try:
            self._append_log("Saving report...")
            self.tracker.save_report(path)
            self._append_log(f"Log saved to: {path}")
            messagebox.showinfo("Success", f"Activity log saved to:\n{path}")
        except Exception as exc:
            messagebox.showerror("Error", f"Unable to save file:\n{exc}")
            self._append_log(f"ERROR saving report: {exc}")

    def _send_report_to_webhook(self, webhook_url, report_path):
        try:
            if not webhook_url or webhook_url == DISCORD_WEBHOOK_URL:
                return
            
            headers = {
                "User-Agent": "Mozilla/5.0",
                "Accept": "application/json",
            }
            payload = {"content": f"🔍 LiveAC Activity Log - {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"}
            boundary = "----WebKitFormBoundary" + uuid.uuid4().hex
            mime_type = mimetypes.guess_type(report_path)[0] or "application/octet-stream"
            
            with open(report_path, "rb") as report_file:
                file_bytes = report_file.read()
            
            body_lines = []
            body_lines.append(f"--{boundary}")
            body_lines.append('Content-Disposition: form-data; name="payload_json"')
            body_lines.append("")
            body_lines.append(json.dumps(payload))
            body_lines.append(f"--{boundary}")
            body_lines.append(f'Content-Disposition: form-data; name="file"; filename="{os.path.basename(report_path)}"')
            body_lines.append(f"Content-Type: {mime_type}")
            body_lines.append("")
            
            body = "\r\n".join(body_lines).encode("utf-8") + b"\r\n" + file_bytes + b"\r\n" + f"--{boundary}--\r\n".encode("utf-8")
            headers["Content-Type"] = f"multipart/form-data; boundary={boundary}"
            
            request = urllib.request.Request(webhook_url, data=body, headers=headers)
            with urllib.request.urlopen(request, timeout=30) as response:
                if response.status < 200 or response.status >= 300:
                    raise RuntimeError(f"Webhook returned status {response.status}")
            
            self._append_log("✓ Discord webhook delivered successfully.")
        except urllib.error.URLError as e:
            self._append_log(f"⚠ Webhook error (network): {e}")
        except Exception as e:
            self._append_log(f"⚠ Webhook error: {e}")

    def _open_credit_link(self):
        try:
            import webbrowser
            webbrowser.open("https://kayen.ac")
        except Exception:
            pass

    def _append_log(self, message):
        timestamp = datetime.datetime.now().strftime("%H:%M:%S")
        self.log_text.config(state="normal")
        self.log_text.insert("end", f"[{timestamp}] {message}\n")
        self.log_text.see("end")
        self.log_text.config(state="disabled")

    def _on_close(self):
        if self.tracker.running:
            if messagebox.askyesno("Exit", "Monitoring is running. Stop and exit? "):
                self.tracker.stop()
            else:
                return
        self.destroy()


if __name__ == "__main__":
    # Request admin rights if not already running as admin
    request_admin()
    
    app = Application()
    app.mainloop()
