using System.IO;
using System.IO.Compression;
using NexusShell.Services;
using NexusShell.Services.Diagnostics;
using NexusShell.Services.Workspace;
using NexusShell.ViewModels;
using Xunit;

namespace NexusShell.Tests;

public class WorkspaceDiagnosticsTests
{
    [Fact]
    public void LogRedactor_masks_inline_secrets()
    {
        var input = "password=SuperSecret123 token=abc123 Authorization: Bearer live-token-value";
        var redacted = LogRedactor.Redact(input);
        Assert.DoesNotContain("SuperSecret123", redacted);
        Assert.DoesNotContain("live-token-value", redacted);
        Assert.Contains("[REDACTED]", redacted);
    }

    [Fact]
    public void LogRedactor_masks_sensitive_property_keys()
    {
        var props = new Dictionary<string, string>
        {
            ["password"] = "hidden",
            ["operation"] = "health",
        };

        var redacted = LogRedactor.RedactProperties(props)!;
        Assert.Equal("[REDACTED]", redacted["password"]);
        Assert.Equal("health", redacted["operation"]);
    }

    [Fact]
    public async Task DiagnosticSessionManager_creates_session_and_exports_zip()
    {
        var root = Path.Combine(Path.GetTempPath(), "nexus-diagnostics-test-" + Guid.NewGuid().ToString("N"));
        var exportRoot = Path.Combine(Path.GetTempPath(), "nexus-diagnostics-export-" + Guid.NewGuid().ToString("N"));
        var eventLog = new WorkspaceEventLog();
        var manager = new DiagnosticSessionManager(eventLog);
        manager.ConfigureRoot(root);
        manager.ConfigureExportRoot(exportRoot);

        var session = manager.StartSession();
        Assert.True(Directory.Exists(session.SessionDirectory));

        eventLog.BindSession(session.SessionDirectory);
        eventLog.Enqueue(new DiagnosticLogEntry(
            DateTimeOffset.UtcNow,
            DiagnosticEventCategory.Lifecycle,
            "test_event",
            "hello"));

        await eventLog.FlushAsync();
        var zipPath = await manager.ExportSessionZipAsync();
        Assert.True(File.Exists(zipPath));
        Assert.StartsWith(exportRoot, zipPath, StringComparison.OrdinalIgnoreCase);

        using (var archive = ZipFile.OpenRead(zipPath))
        {
            Assert.Contains(archive.Entries, e => e.Name == "events.jsonl");
        }

        manager.EndSession();
        await eventLog.DisposeAsync();

        try
        {
            Directory.Delete(root, true);
            Directory.Delete(exportRoot, true);
        }
        catch (IOException)
        {
            // Best-effort cleanup — zip handle may linger briefly on Windows.
        }
    }

    [Fact]
    public async Task DiagnosticsService_exports_support_summary_and_timer_snapshot()
    {
        NexusTimerRegistry.ClearForTests();
        var root = Path.Combine(Path.GetTempPath(), "nexus-diagnostics-service-" + Guid.NewGuid().ToString("N"));
        var exportRoot = Path.Combine(Path.GetTempPath(), "nexus-diagnostics-service-export-" + Guid.NewGuid().ToString("N"));
        var eventLog = new WorkspaceEventLog();
        var manager = new DiagnosticSessionManager(eventLog);
        manager.ConfigureRoot(root);
        manager.ConfigureExportRoot(exportRoot);
        var diagnostics = new DiagnosticsService(eventLog, manager);

        using var timer = NexusTimerRegistry.Register("test-timer", "tests", TimeSpan.FromSeconds(3), true);
        diagnostics.StartSession();
        var zipPath = await diagnostics.ExportSessionZipAsync();

        using (var archive = ZipFile.OpenRead(zipPath))
        {
            Assert.Contains(archive.Entries, e => e.Name == "support-summary.txt");
            Assert.Contains(archive.Entries, e => e.Name == "timers.json");
        }

        await eventLog.DisposeAsync();
        Directory.Delete(root, true);
        Directory.Delete(exportRoot, true);
        NexusTimerRegistry.ClearForTests();
    }

    [Fact]
    public async Task DiagnosticsService_logs_long_operations_over_threshold()
    {
        var root = Path.Combine(Path.GetTempPath(), "nexus-long-operation-" + Guid.NewGuid().ToString("N"));
        var eventLog = new WorkspaceEventLog();
        var manager = new DiagnosticSessionManager(eventLog);
        manager.ConfigureRoot(root);
        var diagnostics = new DiagnosticsService(eventLog, manager);

        diagnostics.StartSession();
        diagnostics.TrackOperation("slow-test", () => Thread.Sleep(15), warnAfterMs: 1);
        await eventLog.FlushAsync();

        var recent = diagnostics.GetRecentEvents(10);
        Assert.Contains(recent, e => e.EventName == "long_operation" && e.Message.Contains("slow-test"));

        await eventLog.DisposeAsync();
        Directory.Delete(root, true);
    }

    [Fact]
    public void NexusTimerRegistry_tracks_and_throttles_intervals()
    {
        NexusTimerRegistry.ClearForTests();
        using var timer = NexusTimerRegistry.Register(
            "status",
            "desktop",
            TimeSpan.FromSeconds(4),
            uiThread: true);

        timer.UpdateInterval(TimeSpan.FromSeconds(7));

        var snapshot = NexusTimerRegistry.Snapshot();
        Assert.Single(snapshot);
        Assert.Equal(7, snapshot[0].Interval.TotalSeconds);

        NexusTimerRegistry.ClearForTests();
    }

    [Fact]
    public async Task PerformanceMonitor_throttles_sampler_in_performance_mode()
    {
        NexusTimerRegistry.ClearForTests();
        var root = Path.Combine(Path.GetTempPath(), "nexus-performance-monitor-" + Guid.NewGuid().ToString("N"));
        var eventLog = new WorkspaceEventLog();
        var manager = new DiagnosticSessionManager(eventLog);
        manager.ConfigureRoot(root);
        var diagnostics = new DiagnosticsService(eventLog, manager);
        using var monitor = new PerformanceMonitorService(new SystemStatusService(), diagnostics);

        monitor.PerformanceMode = true;

        Assert.Equal(10, monitor.SampleInterval.TotalSeconds);
        Assert.Contains(NexusTimerRegistry.Snapshot(), t =>
            t.Name == "performance-sampler" && t.Interval.TotalSeconds == 10);

        await eventLog.DisposeAsync();
        Directory.Delete(root, true);
        NexusTimerRegistry.ClearForTests();
    }

    [Fact]
    public async Task WorkspaceEventLog_drops_when_queue_is_full()
    {
        var eventLog = new WorkspaceEventLog();
        var sessionDir = Path.Combine(Path.GetTempPath(), "nexus-log-bound-" + Guid.NewGuid().ToString("N"));
        eventLog.BindSession(sessionDir);

        for (var i = 0; i < 5000; i++)
        {
            eventLog.Enqueue(new DiagnosticLogEntry(
                DateTimeOffset.UtcNow,
                DiagnosticEventCategory.Performance,
                "perf",
                $"entry-{i}"));
        }

        Assert.True(eventLog.DroppedCount > 0);
        await eventLog.DisposeAsync();
        Directory.Delete(sessionDir, true);
    }

    [Fact]
    public void WorkspaceWindowManager_tracks_external_apps()
    {
        var eventLog = new WorkspaceEventLog();
        var sessionManager = new DiagnosticSessionManager(eventLog);
        var diagnostics = new DiagnosticsService(eventLog, sessionManager);
        var windowTracker = new WindowEventTracker(diagnostics);
        var launchTracker = new AppLaunchTracker(diagnostics);
        var manager = new WorkspaceWindowManager(diagnostics, windowTracker, launchTracker);

        manager.RegisterExternalProcess("terminal", "Terminal", System.Diagnostics.Process.GetCurrentProcess());
        Assert.Contains(manager.ActiveWindows, w => w.AppId == "terminal" && w.IsExternal);
    }

    [Fact]
    public void Workspace_app_item_reports_focus_and_minimized_state()
    {
        var item = new WorkspaceAppItem("nexus-files", "Nexus Files", "files", isExternal: false);

        item.IsActive = true;
        Assert.Equal("I fokus", item.StatusHint);

        item.IsMinimized = true;
        Assert.Equal("Minimeret", item.StatusHint);

        item.IsMinimized = false;
        item.IsActive = false;
        Assert.Equal("Kører", item.StatusHint);
    }
}
