#!/usr/bin/env python3
"""
POPRAVEN CLIENT со фикс за Sysmon
"""

import socket
import os
import platform
import json
import requests
from datetime import datetime, timedelta
import psutil
import time
import subprocess
import re
from collections import defaultdict


class FixedSysmonCollector:
    """Симплифициран колектор што работи без инсталиран Sysmon"""

    def __init__(self):
        self.last_collection = None

    def collect_sysmon_events(self, limit=50):
        """Симулирај Sysmon настани од процеси и мрежа"""
        events = []

        try:
            # 1. Процес настани (Event ID 1 = Process Creation)
            for proc in psutil.process_iter(['pid', 'name', 'create_time', 'username']):
                try:
                    pinfo = proc.info
                    if pinfo['create_time']:
                        create_time = datetime.fromtimestamp(pinfo['create_time'])
                        # Додади само процеси креирани во последните 5 минути
                        if not self.last_collection or create_time > self.last_collection:
                            events.append({
                                "event_id": 1,
                                "event_type": "Process Creation",
                                "message": f"Process created: {pinfo['name']} (PID: {pinfo['pid']}, User: {pinfo['username']})",
                                "timestamp": create_time.isoformat(),
                                "process_name": pinfo['name'],
                                "pid": pinfo['pid'],
                                "user": pinfo['username']
                            })
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue

                if len(events) >= limit // 2:
                    break

            # 2. Мрежни конекции (Event ID 3 = Network Connection)
            for conn in psutil.net_connections(kind='inet'):
                try:
                    if conn.raddr and conn.status in ['ESTABLISHED', 'LISTEN']:
                        proc = psutil.Process(conn.pid) if conn.pid else None
                        events.append({
                            "event_id": 3,
                            "event_type": "Network Connection",
                            "message": f"Network {conn.status}: {conn.laddr.ip if conn.laddr else 'N/A'}:{conn.laddr.port if conn.laddr else 'N/A'} -> {conn.raddr.ip}:{conn.raddr.port}",
                            "timestamp": datetime.now().isoformat(),
                            "local_address": f"{conn.laddr.ip}:{conn.laddr.port}" if conn.laddr else None,
                            "remote_address": f"{conn.raddr.ip}:{conn.raddr.port}",
                            "status": conn.status,
                            "pid": conn.pid,
                            "process_name": proc.name() if proc else "Unknown"
                        })
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue

                if len(events) >= limit:
                    break

            # 3. Системски перформанси настани
            cpu = psutil.cpu_percent(interval=0.1)
            if cpu > 80:
                events.append({
                    "event_id": "PERF_HIGH_CPU",
                    "event_type": "High CPU Usage",
                    "message": f"High CPU usage detected: {cpu:.1f}%",
                    "timestamp": datetime.now().isoformat(),
                    "cpu_percent": cpu
                })

            ram = psutil.virtual_memory()
            if ram.percent > 85:
                events.append({
                    "event_id": "PERF_HIGH_RAM",
                    "event_type": "High Memory Usage",
                    "message": f"High RAM usage: {ram.percent:.1f}%",
                    "timestamp": datetime.now().isoformat(),
                    "ram_percent": ram.percent
                })

        except Exception as e:
            print(f"FixedSysmon error: {e}")

        self.last_collection = datetime.now()
        return events

    def collect_network_connections_detailed(self):
        """Детални мрежни конекции"""
        connections = []
        try:
            for conn in psutil.net_connections(kind='inet'):
                try:
                    proc = psutil.Process(conn.pid) if conn.pid else None

                    conn_info = {
                        "pid": conn.pid,
                        "status": conn.status,
                        "family": str(conn.family),
                        "type": str(conn.type),
                        "timestamp": datetime.now().isoformat()
                    }

                    if conn.laddr:
                        conn_info["local_address"] = f"{conn.laddr.ip}:{conn.laddr.port}"
                        conn_info["local_ip"] = conn.laddr.ip
                        conn_info["local_port"] = conn.laddr.port

                    if conn.raddr:
                        conn_info["remote_address"] = f"{conn.raddr.ip}:{conn.raddr.port}"
                        conn_info["remote_ip"] = conn.raddr.ip
                        conn_info["remote_port"] = conn.raddr.port

                    if proc:
                        conn_info.update({
                            "process_name": proc.name(),
                            "process_cmdline": ' '.join(proc.cmdline()) if proc.cmdline() else None,
                            "process_username": proc.username() if hasattr(proc, 'username') else None
                        })

                    connections.append(conn_info)

                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue

                if len(connections) >= 30:
                    break

        except Exception as e:
            print(f"Network connections error: {e}")

        return connections

    def collect_all_security_data(self):
        """Собери сите безбедносни податоци (очекуваниот метод!)"""
        return {
            "sysmon_events": self.collect_sysmon_events(limit=50),
            "network_connections": self.collect_network_connections_detailed(),
            "file_changes": [],  # Празен лист, можеш да го имплементираш подоцна
            "collection_time": datetime.now().isoformat()
        }


class EnhancedFixedClient:
    def __init__(self, server_ip="192.168.1.13", port=5555, env_token=None):
        self.server_ip = server_ip
        self.port = port
        self.computer_name = socket.gethostname()
        self.user = os.getlogin()
        self.sysmon_collector = FixedSysmonCollector()
        self.env_token = env_token
        # Користи поправената верзија

    def get_detailed_info(self):
        """Собирај подробни податоци"""
        info = {
            "computer_name": self.computer_name,
            "user": self.user,
            "ip_address": socket.gethostbyname(self.computer_name),
            "os": f"{platform.system()} {platform.release()} {platform.version()}",
            "architecture": platform.architecture()[0],
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "is_sysmon_available": self._check_sysmon_installed()
        }

        info.update(self.get_performance_metrics())
        info.update(self.get_hardware_info())

        return info

    def _check_sysmon_installed(self):
        """Провери дали Sysmon е инсталиран"""
        try:
            if platform.system() == "Windows":
                result = subprocess.run(
                    ["powershell", "-Command", "Get-Process -Name 'sysmon' -ErrorAction SilentlyContinue"],
                    capture_output=True, text=True
                )
                return result.returncode == 0
        except:
            pass
        return False

    def get_performance_metrics(self):
        """Метрики за перформанси - ПОПРАВЕНО БЕЗ DEPRECATION WARNING"""
        try:
            cpu_percent = psutil.cpu_percent(interval=1)
            cpu_freq = psutil.cpu_freq()

            ram = psutil.virtual_memory()
            swap = psutil.swap_memory()

            disk = psutil.disk_usage("C:/" if platform.system() == "Windows" else "/")

            # ДОБАВИ TRY-EXCEPT за disk_io_counters
            disk_read_mb = 0
            disk_write_mb = 0
            try:
                disk_io = psutil.disk_io_counters()
                if disk_io:
                    disk_read_mb = round(disk_io.read_bytes / (1024 ** 2), 2)
                    disk_write_mb = round(disk_io.write_bytes / (1024 ** 2), 2)
            except:
                pass

            net_io = psutil.net_io_counters()

            return {
                "cpu_usage": cpu_percent,
                "cpu_cores": psutil.cpu_count(logical=True),
                "cpu_freq_current": cpu_freq.current if cpu_freq else 0,
                "ram_usage": ram.percent,
                "ram_total_gb": round(ram.total / (1024 ** 3), 2),
                "ram_used_gb": round(ram.used / (1024 ** 3), 2),
                "swap_usage": swap.percent if swap else 0,
                "disk_usage": disk.percent,
                "disk_total_gb": round(disk.total / (1024 ** 3), 2),
                "disk_used_gb": round(disk.used / (1024 ** 3), 2),
                "disk_read_mb": disk_read_mb,
                "disk_write_mb": disk_write_mb,
                "network_sent_mb": round(net_io.bytes_sent / (1024 ** 2), 2),
                "network_recv_mb": round(net_io.bytes_recv / (1024 ** 2), 2),
                "network_packets_sent": net_io.packets_sent,
                "network_packets_recv": net_io.packets_recv,
                "boot_time": datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S"),
                "uptime_hours": round((datetime.now() - datetime.fromtimestamp(psutil.boot_time())).seconds / 3600, 2)
            }
        except Exception as e:
            print(f"Performance metrics error: {e}")
            return {}

    def get_hardware_info(self):
        """Хардвер информации"""
        try:
            partitions = []
            for partition in psutil.disk_partitions():
                try:
                    usage = psutil.disk_usage(partition.mountpoint)
                    partitions.append({
                        "device": partition.device,
                        "mountpoint": partition.mountpoint,
                        "fstype": partition.fstype,
                        "total_gb": round(usage.total / (1024 ** 3), 2),
                        "used_gb": round(usage.used / (1024 ** 3), 2),
                        "free_gb": round(usage.free / (1024 ** 3), 2),
                        "percent_used": usage.percent
                    })
                except:
                    continue

            # Мрежни интерфејси
            net_if_addrs = psutil.net_if_addrs()
            interfaces = {}
            for interface, addrs in net_if_addrs.items():
                interfaces[interface] = [
                    {
                        "family": str(addr.family),
                        "address": addr.address,
                        "netmask": addr.netmask if hasattr(addr, 'netmask') else None
                    }
                    for addr in addrs if addr.address
                ]

            return {
                "cpu_physical_cores": psutil.cpu_count(logical=False),
                "cpu_logical_cores": psutil.cpu_count(logical=True),
                "disks": partitions,
                "network_interfaces": interfaces,
                "battery_percent": psutil.sensors_battery().percent if hasattr(psutil.sensors_battery(),
                                                                               'percent') else None,
                "temperatures": self._get_temperatures()
            }
        except Exception as e:
            print(f"Hardware info error: {e}")
            return {}

    def _get_temperatures(self):
        """Добиј температури на системот"""
        try:
            temps = {}
            if hasattr(psutil, 'sensors_temperatures'):
                sensors = psutil.sensors_temperatures()
                for name, entries in sensors.items():
                    if entries:
                        temps[name] = entries[0].current
            return temps
        except:
            return {}

    def get_detailed_processes(self):
        """Детални информации за процесите - ПОПРАВЕНО БЕЗ DEPRECATION WARNING"""
        processes = []
        try:
            for proc in psutil.process_iter(['pid', 'name', 'username', 'cpu_percent',
                                             'memory_percent', 'create_time', 'status']):
                try:
                    pinfo = proc.info

                    # КОРИСТИ psutil.Process() за да добиеш дополнителни информации
                    with proc.oneshot():
                        processes.append({
                            "pid": pinfo['pid'],
                            "name": pinfo['name'],
                            "user": pinfo['username'],
                            "cpu_percent": pinfo['cpu_percent'],
                            "memory_mb": round(proc.memory_info().rss / (1024 ** 2), 2),
                            "memory_percent": pinfo['memory_percent'],
                            "create_time": datetime.fromtimestamp(pinfo['create_time']).strftime("%Y-%m-%d %H:%M:%S") if
                            pinfo['create_time'] else None,
                            "status": pinfo['status'],
                            "exe": proc.exe() if hasattr(proc, 'exe') else None,
                            "cmdline": ' '.join(proc.cmdline()) if hasattr(proc, 'cmdline') else None,
                            "num_threads": proc.num_threads() if hasattr(proc, 'num_threads') else None,
                            # НЕ КОРИСТИ proc.connections() - тоа е deprecated!
                            "connections_count": 0  # Стави 0 или избриши го ова поле
                        })
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    continue

                if len(processes) >= 100:
                    break

        except Exception as e:
            print(f"Process error: {e}")

        return processes

    def collect_and_send(self):
        """Собери и испрати сите податоци"""
        print(f"\n[{datetime.now().strftime('%H:%M:%S')}] 🔍 Собирање податоци...")

        # Собери сите податоци
        data = {
            "info": self.get_detailed_info(),
            "processes": self.get_detailed_processes(),
            "security_data": self.sysmon_collector.collect_all_security_data(),  # ОВА СЕГА РАБОТИ!
            "collection_time": datetime.now().isoformat(),
            "client_version": "2.1_fixed"
        }

        # Прикажи основни информации
        info = data["info"]
        sec_data = data["security_data"]

        print(f"   💻 Компјутер: {info['computer_name']}")
        print(f"   👤 Корисник: {info['user']}")
        print(
            f"   📊 CPU: {info.get('cpu_usage', 0)}% | RAM: {info.get('ram_usage', 0)}% | Disk: {info.get('disk_usage', 0)}%")
        print(f"   🔄 Процеси: {len(data['processes'])}")
        print(f"   🛡️  Sysmon настани: {len(sec_data.get('sysmon_events', []))}")
        print(f"   🌐 Мрежни конекции: {len(sec_data.get('network_connections', []))}")

        # Испрати ги податоците
        try:
            headers = {"X-Env-Token": self.env_token} if self.env_token else {}

            response = requests.post(
                f"http://{self.server_ip}:{self.port}/receive",
                json=data,
                headers=headers,
                timeout=30
            )

            if response.status_code == 200:
                result = response.json()
                print(f"   ✅ Податоците се успешно испратени!")
                if 'event_count' in result:
                    print(f"   📝 Зачувани {result['event_count']} Sysmon настани")
                return True
            else:
                if response.status_code == 401:
                    try:
                        msg = response.json()
                    except:
                        msg = {}
                    print("   🔒 Token invalid/expired. Побарај нов token од Admin панел.")
                    print(f"   Server says: {msg}")
                else:
                    print(f"   ❌ Грешка: {response.status_code}")
                    try:
                        print("   ", response.text[:300])
                    except:
                        pass
                return False

        except Exception as e:
            print(f"   ❌ Проблем со серверот: {e}")
            return False


def main():
    """Главна програма"""
    print("=" * 70)
    print("🖥️  FIXED LAN LOG COLLECTOR v2.1")
    print("=" * 70)
    print(f"Компјутер: {socket.gethostname()}")
    print(f"Корисник: {os.getlogin()}")
    print(f"IP: {socket.gethostbyname(socket.gethostname())}")
    print("=" * 70)
    print("Податоците ќе се собираат и испраќаат на секои 30 секунди.")
    print("Притисни Ctrl+C за да го прекинеш.")
    print("=" * 70)

    # Провери дали серверот е достапен
    server_ip = input("Внеси сервер IP (default 192.168.1.13): ").strip() or "192.168.1.13"

    env_token = input("Внеси ENV TOKEN (од Admin панел): ").strip()
    if not env_token:
        print("❌ Мора ENV TOKEN за да се регистрира уредот.")
        return

    client = EnhancedFixedClient(server_ip, 5555, env_token=env_token)
    # Тест конекција
    print(f"\n🔌 Тестирање на врска со серверот {server_ip}...")
    try:
        response = requests.get(f"http://{server_ip}:5555/ping", timeout=5)
        if response.status_code == 200:
            print("   ✅ Серверот е достапен!")
        else:
            print(f"   ⚠️  Серверот одговори со: {response.status_code}")
            confirm = input("   Сакаш ли да продолжиш? (y/n): ").lower()
            if confirm != 'y':
                return
    except Exception as e:
        print(f"   ❌ Не можам да се поврзам со серверот {server_ip}: {e}")
        confirm = input("   Сакаш ли да продолжиш? (y/n): ").lower()
        if confirm != 'y':
            return

    try:
        count = 1
        while True:
            print(f"\n📤 Трансмисија #{count}")

            success = client.collect_and_send()

            count += 1
            print(f"\n⏳ Чекам 30 секунди до следната трансмисија...")

            # Countdown
            for i in range(30, 0, -1):
                if i % 10 == 0 or i <= 5:
                    print(f"\r   {i} секунди остануваат...", end="")
                time.sleep(1)
            print("\r" + " " * 40 + "\r", end="")

    except KeyboardInterrupt:
        print("\n\n🛑 Програмата е зајчана.")
        print(f"📊 Dashboard: http://{server_ip}:5555/")


if __name__ == "__main__":
    try:
        import psutil
        import requests
    except ImportError:
        print("Инсталирај ги модулите:")
        print("pip install psutil requests")
        input("Притисни ENTER за излез...")
        exit(1)

    main()