using ITNexusAgent.Models;
using ITNexusAgent.Services;
using Newtonsoft.Json;

namespace ITNexusAgent;

public class AgentWorker
{
    private const string Version = "2.5.0";
    private const string DataDir = @"C:\ProgramData\IT Nexus Agent";
    private const string ConfigPath = @"C:\ProgramData\IT Nexus Agent\config.json";
    private const string StatusPath = @"C:\ProgramData\IT Nexus Agent\status.json";
    private const string LogPath = @"C:\ProgramData\IT Nexus Agent\agent.log";

    private readonly CancellationToken _ct;
    private AgentConfig? _config;
    private ApiService? _api;
    private NotificationService? _notifier;
    private string _exePath = "";

    public AgentWorker(CancellationToken ct) => _ct = ct;

    public async Task RunAsync()
    {
        _exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule!.FileName;
        Directory.CreateDirectory(DataDir);

        if (!File.Exists(ConfigPath))
        {
            Log("ERROR: config.json nicht gefunden");
            return;
        }

        SecureConfigFile(ConfigPath);

        _config = AgentConfig.Load(ConfigPath);
        _api = new ApiService(_config.ServerUrl, _config.AgentKey);
        _notifier = new NotificationService(_exePath, DataDir);

        Log($"Agent v{Version} gestartet");

        // WebSocket Shell-Service im Hintergrund starten
        var shellService = new ShellService(_config.ServerUrl, _config.AgentKey, SystemInfoService.GetHostname());
        _ = shellService.RunAsync(_ct);

        // WebRTC Remote Desktop Service im Hintergrund starten
        var rtcService = new RtcService(_config.ServerUrl, _config.AgentKey, SystemInfoService.GetHostname());
        _ = rtcService.RunAsync(_ct);

        while (!_ct.IsCancellationRequested)
        {
            await RunCycleAsync();
            await Task.Delay(TimeSpan.FromMinutes(1), _ct).ContinueWith(_ => { });
        }
    }

    private async Task RunCycleAsync()
    {
        try
        {
            Log("Sammle Systemdaten...");
            var payload = CollectData();
            var response = await _api!.CheckinAsync(payload);

            Log($"OK: Checkin erfolgreich (Hostname: {payload.Hostname})");

            // Status-Cache für Dashboard schreiben
            WriteStatusCache(payload, response, true);

            // Auto-Update prüfen
            if (!string.IsNullOrEmpty(response.AgentVersion) &&
                System.Version.TryParse(response.AgentVersion, out var serverVer) &&
                System.Version.TryParse(Version, out var localVer) &&
                serverVer > localVer)
            {
                Log($"UPDATE: Neue Agent-Version verfügbar: {response.AgentVersion} (aktuell: {Version})");
                await new CommandExecutor(_api, payload.Hostname, DataDir, _exePath)
                    .ExecuteAsync(new PatchCommand { Id = 0, Command = "update_agent" });
                return;
            }

            // Patch-Commands ausführen
            var executor = new CommandExecutor(_api, payload.Hostname, DataDir, _exePath);
            foreach (var cmd in response.Commands)
            {
                Log($"PATCH: Command empfangen: {cmd.Command}");
                await executor.ExecuteAsync(cmd);
            }

            // Ankündigungen anzeigen + sofort ACKen damit Server sie nicht mehr schickt
            if (response.Announcements.Count > 0)
            {
                _notifier!.ShowAnnouncements(response.Announcements);
                foreach (var ann in response.Announcements)
                {
                    Log($"ANNOUNCEMENT: Dialog für {SystemInfoService.GetLastUser()} gestartet - ID {ann.Id}");
                    try { await _api!.AckAnnouncementAsync(ann.Id, payload.Hostname); }
                    catch (Exception ex) { Log($"ANNOUNCEMENT ACK Fehler: {ex.Message}"); }
                }
            }
        }
        catch (Exception ex)
        {
            Log($"ERROR: {ex.Message}");
            WriteStatusCache(null, null, false);
        }
    }

    private CheckinPayload CollectData()
    {
        var (cpuModel, cpuCores) = SystemInfoService.GetCpuInfo();
        var (ramTotal, ramUsed) = SystemInfoService.GetRamInfo();
        var (diskTotal, diskFree) = SystemInfoService.GetDiskInfo();
        var (tpmPresent, tpmVersion, tpmV2) = SystemInfoService.GetTpmInfo();
        var (defEnabled, defSigAge, _) = SecurityInfoService.GetDefenderStatus();

        return new CheckinPayload
        {
            Hostname = SystemInfoService.GetHostname(),
            AgentVersion = Version,
            Os = SystemInfoService.GetOs(),
            OsVersion = SystemInfoService.GetOsVersion(),
            Domain = SystemInfoService.GetDomain(),
            IpAddress = SystemInfoService.GetIpAddress(),
            MacAddress = SystemInfoService.GetMacAddress(),
            LastUser = SystemInfoService.GetLastUser(),
            CpuModel = cpuModel,
            CpuCores = cpuCores,
            CpuUsage = SystemInfoService.GetCpuUsage(),
            RamTotal = ramTotal,
            RamUsed = ramUsed,
            DiskTotal = diskTotal,
            DiskFree = diskFree,
            UptimeHours = SystemInfoService.GetUptimeHours(),
            PendingUpdates = SystemInfoService.GetPendingUpdates(),
            TpmPresent = tpmPresent,
            TpmVersion = tpmVersion,
            TpmV2 = tpmV2,
            SecureBoot = SystemInfoService.GetSecureBoot(),
            Win11Ready = SystemInfoService.GetWin11Readiness(tpmV2, SystemInfoService.GetSecureBoot(), cpuCores, ramTotal),
            BitlockerStatus = SecurityInfoService.GetBitlockerStatus(),
            DefenderEnabled = defEnabled,
            DefenderSignaturesAge = defSigAge >= 0 ? defSigAge : null,
            HardwareSerial = SystemInfoService.GetHardwareSerial(),
            InstalledSoftware = SystemInfoService.GetInstalledSoftware(),
        };
    }

    private void WriteStatusCache(CheckinPayload? p, CheckinResponse? r, bool online)
    {
        try
        {
            var status = new StatusCache
            {
                Hostname = p?.Hostname ?? SystemInfoService.GetHostname(),
                LastCheckin = DateTime.Now,
                AgentVersion = Version,
                ServerUrl = _config?.ServerUrl ?? "",
                Online = online,
                CpuUsage = p?.CpuUsage,
                RamTotal = p?.RamTotal,
                RamUsed = p?.RamUsed,
                DiskTotal = p?.DiskTotal,
                DiskFree = p?.DiskFree,
                PendingUpdates = p?.PendingUpdates ?? 0,
                BitlockerStatus = p?.BitlockerStatus ?? "",
                DefenderEnabled = p?.DefenderEnabled ?? false,
                DefenderSignaturesAge = p?.DefenderSignaturesAge,
                HardwareSerial = p?.HardwareSerial ?? "",
                Os = p?.Os ?? "",
                LastUser = p?.LastUser ?? "",
                InstalledSoftware = p?.InstalledSoftware ?? [],
            };
            File.WriteAllText(StatusPath, JsonConvert.SerializeObject(status, Formatting.Indented));
        }
        catch { }
    }

    private static void SecureConfigFile(string path)
    {
        try
        {
            var fi = new System.IO.FileInfo(path);
            var acl = fi.GetAccessControl();
            acl.SetAccessRuleProtection(true, false); // Vererbung entfernen
            // Nur SYSTEM und Administratoren
            acl.AddAccessRule(new System.Security.AccessControl.FileSystemAccessRule(
                "SYSTEM", System.Security.AccessControl.FileSystemRights.FullControl,
                System.Security.AccessControl.AccessControlType.Allow));
            acl.AddAccessRule(new System.Security.AccessControl.FileSystemAccessRule(
                "Administrators", System.Security.AccessControl.FileSystemRights.FullControl,
                System.Security.AccessControl.AccessControlType.Allow));
            fi.SetAccessControl(acl);
        }
        catch { }
    }

    public static void Log(string msg)
    {
        try
        {
            var line = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {msg}";
            File.AppendAllText(LogPath, line + Environment.NewLine, System.Text.Encoding.UTF8);
        }
        catch { }
    }
}
