package org.example.core;

import org.example.persistence.PreferencesManager;
import org.example.persistence.TrafficLogger;

import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;



import java.util.concurrent.*;

public class NetworkMonitor {
    private final PreferencesManager prefs;
    private final TrafficLogger logger;
    private final ScheduledExecutorService scheduler;
    private final Map<String, InterfaceHistory> historyMap = new ConcurrentHashMap<>();
    private boolean isMonitoring = false;

    public NetworkMonitor(PreferencesManager prefs) throws IOException {
        this.prefs = prefs;
        this.logger = new TrafficLogger("logs/network_traffic.csv");
        this.scheduler = Executors.newScheduledThreadPool(1);
    }

    public void startMonitoring(TrafficUpdateListener listener) {
        if (isMonitoring) return;
        isMonitoring = true;

        scheduler.scheduleAtFixedRate(() -> {
            try {
                TrafficData data = collectNetworkData();
                SwingUtilities.invokeLater(() -> {
                    listener.onTrafficUpdate(data);
                    try {
                        logger.log(data);
                    } catch (IOException e) {
                        System.err.println("Logging failed: " + e.getMessage());
                    }
                });
            } catch (Exception e) {
                System.err.println("Monitoring error: " + e.getMessage());
            }
        }, 0, prefs.getRefreshInterval(), TimeUnit.MILLISECONDS);
    }

    private TrafficData collectNetworkData() throws IOException {
        TrafficData data = new TrafficData();

        if (System.getProperty("os.name").startsWith("Windows")) {
            collectWindowsData(data);
        } else {
            collectUnixData(data);
        }

        measureLatency(data);
        return data;
    }

    private void collectWindowsData(TrafficData data) throws IOException {
        Process process = Runtime.getRuntime().exec(
                "powershell.exe Get-NetAdapter | Where-Object {$_.Status -eq 'Up'} | " +
                        "Select-Object Name,ReceivedBytes,SentBytes");

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {

            reader.readLine(); // Skip header
            String line;
            while ((line = reader.readLine()) != null) {
                String[] parts = line.trim().split("\\s+");
                if (parts.length >= 3) {
                    String iface = parts[0];
                    long rxTotal = Long.parseLong(parts[1]);
                    long txTotal = Long.parseLong(parts[2]);

                    InterfaceHistory history = historyMap.computeIfAbsent(
                            iface, k -> new InterfaceHistory());

                    long rxDelta = history.lastReceived > 0 ? rxTotal - history.lastReceived : 0;
                    long txDelta = history.lastSent > 0 ? txTotal - history.lastSent : 0;

                    history.lastReceived = rxTotal;
                    history.lastSent = txTotal;
                    data.addInterfaceStats(iface, rxDelta, txDelta);
                }
            }
        }
    }

    private void collectUnixData(TrafficData data) throws IOException {
        // Get list of network interfaces
        Process ifconfig = Runtime.getRuntime().exec("ifconfig -a");
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(ifconfig.getInputStream()))) {

            String iface = null;
            String line;
            while ((line = reader.readLine()) != null) {
                if (!line.startsWith(" ")) {
                    // New interface found
                    iface = line.split(":")[0];
                } else if (iface != null && line.contains("RX packets")) {
                    // Parse RX/TX bytes
                    String[] parts = line.trim().split("\\s+");
                    long rxBytes = Long.parseLong(parts[4]);
                    long txBytes = Long.parseLong(parts[6]);

                    InterfaceHistory history = historyMap.computeIfAbsent(
                            iface, k -> new InterfaceHistory());

                    long rxDelta = history.lastReceived > 0 ? rxBytes - history.lastReceived : 0;
                    long txDelta = history.lastSent > 0 ? txBytes - history.lastSent : 0;

                    history.lastReceived = rxBytes;
                    history.lastSent = txBytes;
                    data.addInterfaceStats(iface, rxDelta, txDelta);
                }
            }
        }
    }

    private void measureLatency(TrafficData data) {
        try {
            String pingCmd = System.getProperty("os.name").startsWith("Windows")
                    ? "ping -n 1 8.8.8.8"
                    : "ping -c 1 8.8.8.8";

            Process ping = Runtime.getRuntime().exec(pingCmd);
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(ping.getInputStream()))) {

                String line;
                while ((line = reader.readLine()) != null) {
                    if (line.contains("time=")) {
                        int start = line.indexOf("time=") + 5;
                        int end = line.indexOf(" ms", start);
                        String latency = line.substring(start, end).trim();
                        data.setLatency(Double.parseDouble(latency));
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Ping failed: " + e.getMessage());
        }
    }

    public void stopMonitoring() {
        isMonitoring = false;
        scheduler.shutdown();
        try {
            logger.close();
        } catch (Exception e) {
            System.err.println("Error during shutdown: " + e.getMessage());
        }
    }

    public interface TrafficUpdateListener {
        void onTrafficUpdate(TrafficData data);
    }

    private static class InterfaceHistory {
        long lastReceived;
        long lastSent;
    }
}