using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using System.Windows;
using System.Windows.Threading;
using Nexus.Contracts.Apps;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using NexusShell.Agent;
using NexusShell.Services;
using NexusShell.Services.Diagnostics;
using NexusShell.Services.Policy;
using NexusShell.Services.Workspace;

namespace NexusShell.ViewModels;

public sealed class DesktopViewModel : INotifyPropertyChanged
{
    private readonly ProfileService _profileService;
    private readonly AppCatalogService _catalog;
    private readonly AppLaunchService _launcher;
    private readonly SystemStatusService _systemStatus;
    private readonly UpdateService _updateService;
    private readonly UserSettingsService _userSettings;
    private readonly string _identityAuthority;
    private readonly NexusAgentLocalClient _agentClient;
    private readonly NexusPolicyService _policy;
    private readonly WorkspaceWindowManager _workspaceWindows;
    private readonly PerformanceMonitorService _performance;
    private readonly DiagnosticsService _diagnostics;
    private readonly DockNavItem[] _allDockItems;
    private readonly NexusTimerRegistration _clockTimerRegistration;
    private readonly NexusTimerRegistration _statusTimerRegistration;
    private bool _notificationsCleared;
    private int _statusRefreshBusy;
    private int _latencyRefreshBusy;
    private int _agentRefreshBusy;
    private readonly DispatcherTimer _clockTimer;
    private readonly DispatcherTimer _statusTimer;
    private string _clockText = DateTime.Now.ToString("HH:mm");
    private string _calendarDateText = DateTime.Now.ToString("d. MMMM");
    private string _calendarDayText = DateTime.Now.ToString("dddd");

    private string _activeDockLabel = "Home";
    private string _dockDateText = DateTime.Now.ToString("dd-MM-yyyy");
    public static IReadOnlyList<DockNavItem> PrimaryDockCatalog { get; } =
    [
        new DockNavItem("Home", "nexus", true),
        new DockNavItem("Search", "search", false),
        new DockNavItem("App Hub", "apps", false),
        new DockNavItem("Files", "files", false),
        new DockNavItem("Browser", "browser", false),
        new DockNavItem("Settings", "settings", false),
        new DockNavItem("Diagnostics", "activity", false),
    ];

    public DesktopViewModel(
        ProfileService profileService,
        AppCatalogService catalog,
        AppLaunchService launcher,
        SystemStatusService systemStatus,
        UpdateService updateService,
        UserSettingsService userSettings,
        string identityAuthority,
        NexusAgentLocalClient agentClient,
        NexusPolicyService policy,
        WorkspaceWindowManager workspaceWindows,
        PerformanceMonitorService performance,
        DiagnosticsService diagnostics)
    {
        _profileService = profileService;
        _catalog = catalog;
        _launcher = launcher;
        _systemStatus = systemStatus;
        _updateService = updateService;
        _userSettings = userSettings;
        _identityAuthority = identityAuthority;
        _agentClient = agentClient;
        _policy = policy;
        _workspaceWindows = workspaceWindows;
        _performance = performance;
        _diagnostics = diagnostics;

        _userSettings.SettingsChanged += (_, _) =>
        {
            RefreshAppearance();
            ApplyPerformanceSettings();
        };
        _policy.PolicyChanged += (_, _) => RefreshDockItems();
        _workspaceWindows.ActiveWindowsChanged += (_, _) => SyncWorkspaceApps();

        OpenLauncherCommand = new RelayCommand(_ => OpenLauncherRequested?.Invoke(this, EventArgs.Empty));
        OpenSettingsCommand = new RelayCommand(_ => OpenSettingsRequested?.Invoke(this, EventArgs.Empty));
        OpenUpdateInfoCommand = new RelayCommand(_ => OpenUpdateInfoRequested?.Invoke(this, EventArgs.Empty));
        OpenDiagnosticsCommand = new RelayCommand(_ => OpenDiagnosticsRequested?.Invoke(this, EventArgs.Empty));
        OpenNotificationCenterCommand = new RelayCommand(_ => OpenNotificationCenterRequested?.Invoke(this, EventArgs.Empty));
        ClearNotificationsCommand = new RelayCommand(_ => ClearNotifications());
        DockItemCommand = new RelayCommand(p => HandleDockItem(p as string));

        Notifications = [];

        _allDockItems = PrimaryDockCatalog.ToArray();
        DockItems = new ObservableCollection<DockNavItem>();
        WorkspaceApps = new ObservableCollection<WorkspaceAppItem>();
        RefreshDockItems();
        SyncWorkspaceApps();

        _clockTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
        _clockTimer.Tick += (_, _) => UpdateClock();
        _clockTimer.Start();
        _clockTimerRegistration = NexusTimerRegistry.Register(
            "desktop-clock",
            nameof(DesktopViewModel),
            _clockTimer.Interval,
            uiThread: true,
            "Top bar clock/date refresh");

        _statusTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(4) };
        _statusTimer.Tick += (_, _) => RefreshStatus();
        _statusTimer.Start();
        _statusTimerRegistration = NexusTimerRegistry.Register(
            "desktop-status",
            nameof(DesktopViewModel),
            _statusTimer.Interval,
            uiThread: true,
            "Desktop system status and notification refresh");

        ApplyPerformanceSettings();

        UpdateClock();
        RefreshStatus();
        RefreshAppearance();
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public event EventHandler? OpenLauncherRequested;
    public event EventHandler? OpenAppHubRequested;
    public event EventHandler? OpenNotificationCenterRequested;
    public event EventHandler? OpenSettingsRequested;
    public event EventHandler<string?>? OpenFilesRequested;
    public event EventHandler<string?>? OpenBrowserRequested;
    public event EventHandler? OpenUpdateInfoRequested;
    public event EventHandler? OpenConsoleRequested;
    public event EventHandler? OpenCalculatorRequested;
    public event EventHandler? OpenStoreRequested;
    public event EventHandler? OpenDiagnosticsRequested;

    public string ActiveDockLabel
    {
        get => _activeDockLabel;
        private set { _activeDockLabel = value; OnPropertyChanged(); }
    }

    public void ActivateDock(string label)
    {
        if (!string.IsNullOrWhiteSpace(label))
        {
            ActiveDockLabel = label;
        }
    }

    public string UserDisplayName => _profileService.CurrentProfile?.DisplayName ?? "Bruger";
    public string UserRole => FormatRole(_profileService.CurrentProfile?.Role ?? "User");
    public string UserInitials => GetInitials(UserDisplayName);
    public string UserAvatarUri => NexusAssets.ProfileAvatar;
    public string UpdateStatusSummary => _updateService.StatusSummary;
    public bool HasNotifications => NotificationCount > 0;
    public bool HasNoNotifications => NotificationCount == 0;

    public string ClockText
    {
        get => _clockText;
        private set { _clockText = value; OnPropertyChanged(); }
    }

    public string CalendarDateText
    {
        get => _calendarDateText;
        private set { _calendarDateText = value; OnPropertyChanged(); }
    }

    public string DockDateText
    {
        get => _dockDateText;
        private set { _dockDateText = value; OnPropertyChanged(); }
    }

    public string CalendarDayText
    {
        get => _calendarDayText;
        private set { _calendarDayText = value; OnPropertyChanged(); }
    }

    public string SystemStatus { get; private set; } = "OPTIMALT";
    public string NetworkLabel { get; private set; } = "—";
    public int CpuPercent { get; private set; }
    public string MemoryLabel { get; private set; } = "—";
    public string StorageLabel { get; private set; } = "—";
    public string UpdateLabel { get; private set; } = "Opdateret";
    public string IdentityLatencyLabel { get; private set; } = "—";
    public string AgentStatusLabel { get; private set; } = "Klar";
    public string ThreatScanLabel { get; private set; } = "—";
    public ImageSource? WallpaperSource { get; private set; }
    public bool IsLightTheme => NexusAssets.IsLightTheme(_userSettings.Current.Theme);
    public bool UseLightDesktopBackground =>
        IsLightTheme && string.IsNullOrWhiteSpace(_userSettings.Current.WallpaperPath?.Trim());
    public bool ShowDesktopBrand => UseLightDesktopBackground;
    public bool ShowWallpaperImage => WallpaperSource is not null;
    public string LogoTopBarUri => NexusAssets.ResolveLogoTopBar(_userSettings.Current.Theme);
    public string LogoLargeUri => NexusAssets.ResolveLogoLarge(_userSettings.Current.Theme);
    public int NotificationCount { get; private set; }
    public int HealthPercent { get; private set; } = 100;
    public bool ServicesOk { get; private set; } = true;
    public bool NetworkOk { get; private set; } = true;
    public bool SecurityOk { get; private set; } = true;
    public bool UpdatesOk { get; private set; } = true;

    public ObservableCollection<NotificationItem> Notifications { get; }
    public ObservableCollection<DockNavItem> DockItems { get; }
    public ObservableCollection<WorkspaceAppItem> WorkspaceApps { get; }
    public bool HasWorkspaceApps => WorkspaceApps.Count > 0;
    public bool PerformanceModeEnabled => _userSettings.Current.PerformanceMode;

    public ICommand OpenLauncherCommand { get; }
    public ICommand OpenSettingsCommand { get; }
    public ICommand OpenUpdateInfoCommand { get; }
    public ICommand OpenNotificationCenterCommand { get; }
    public ICommand OpenDiagnosticsCommand { get; }
    public ICommand ClearNotificationsCommand { get; }
    public ICommand DockItemCommand { get; }

    public void NotifyUpdateStatus()
    {
        UpdateLabel = ResolveUpdateLabel();
        UpdatesOk = _updateService.LastResult?.IsUpdateAvailable != true;
        OnPropertyChanged(nameof(UpdateStatusSummary));
        OnPropertyChanged(nameof(UpdateLabel));
        OnPropertyChanged(nameof(UpdatesOk));
    }

    public void RefreshAppearance()
    {
        if (UseLightDesktopBackground)
        {
            WallpaperSource = null;
        }
        else
        {
            try
            {
                var uri = _userSettings.ResolveWallpaperUri();
                WallpaperSource = new BitmapImage(new Uri(uri, UriKind.Absolute));
            }
            catch
            {
                WallpaperSource = new BitmapImage(new Uri(NexusAssets.DesktopWallpaper, UriKind.Absolute));
            }
        }

        OnPropertyChanged(nameof(WallpaperSource));
        OnPropertyChanged(nameof(IsLightTheme));
        OnPropertyChanged(nameof(UseLightDesktopBackground));
        OnPropertyChanged(nameof(ShowDesktopBrand));
        OnPropertyChanged(nameof(ShowWallpaperImage));
        OnPropertyChanged(nameof(LogoTopBarUri));
        OnPropertyChanged(nameof(LogoLargeUri));
    }

    public void Refresh()
    {
        _profileService.LoadProfile();
        _policy.Refresh();
        RefreshDockItems();
        RefreshStatus();
        RefreshAppearance();
        SyncWorkspaceApps();
        OnPropertyChanged(nameof(UserDisplayName));
        OnPropertyChanged(nameof(UserRole));
        OnPropertyChanged(nameof(UserInitials));
        OnPropertyChanged(nameof(UserAvatarUri));
        UpdateClock();
    }

    public void RefreshStatus()
    {
        if (Interlocked.CompareExchange(ref _statusRefreshBusy, 1, 0) != 0)
        {
            return;
        }

        _ = RefreshStatusAsync();
    }

    private async Task RefreshStatusAsync()
    {
        try
        {
            var snapshot = await _diagnostics.TrackOperationAsync(
                "desktop_status_capture",
                _ => Task.Run(_systemStatus.Capture),
                warnAfterMs: 200);

            if (Application.Current?.Dispatcher is { } dispatcher)
            {
                await dispatcher.InvokeAsync(() => ApplyStatusSnapshot(snapshot));
            }
            else
            {
                ApplyStatusSnapshot(snapshot);
            }
        }
        finally
        {
            Interlocked.Exchange(ref _statusRefreshBusy, 0);
        }
    }

    private void ApplyStatusSnapshot(SystemStatusSnapshot snapshot)
    {
        SystemStatus = snapshot.SystemStatus;
        NetworkLabel = snapshot.NetworkLabel;
        CpuPercent = snapshot.CpuPercent;
        MemoryLabel = snapshot.MemoryLabel;
        StorageLabel = snapshot.StorageLabel;
        IdentityLatencyLabel = snapshot.IdentityLatencyLabel;
        AgentStatusLabel = _agentClient.AgentStatusLabel;
        ThreatScanLabel = ResolveThreatScanLabel();
        UpdateLabel = ResolveUpdateLabel();
        ServicesOk = snapshot.ServicesOk;
        NetworkOk = snapshot.NetworkOk;
        SecurityOk = snapshot.SecurityOk;
        UpdatesOk = _updateService.LastResult?.IsUpdateAvailable != true;
        HealthPercent = snapshot.ServicesOk && snapshot.NetworkOk && snapshot.SecurityOk && UpdatesOk ? 100 : 87;
        RefreshNotifications(snapshot);
        NotificationCount = Notifications.Count;

        OnPropertyChanged(nameof(SystemStatus));
        OnPropertyChanged(nameof(NetworkLabel));
        OnPropertyChanged(nameof(CpuPercent));
        OnPropertyChanged(nameof(MemoryLabel));
        OnPropertyChanged(nameof(StorageLabel));
        OnPropertyChanged(nameof(UpdateLabel));
        OnPropertyChanged(nameof(IdentityLatencyLabel));
        OnPropertyChanged(nameof(AgentStatusLabel));
        OnPropertyChanged(nameof(ThreatScanLabel));
        OnPropertyChanged(nameof(NotificationCount));
        OnPropertyChanged(nameof(HasNotifications));
        OnPropertyChanged(nameof(HasNoNotifications));
        OnPropertyChanged(nameof(ServicesOk));
        OnPropertyChanged(nameof(NetworkOk));
        OnPropertyChanged(nameof(SecurityOk));
        OnPropertyChanged(nameof(UpdatesOk));
        OnPropertyChanged(nameof(HealthPercent));

        QueueIdentityLatencyRefresh();
        if (!_performance.PerformanceMode)
        {
            QueueAgentStatusRefresh();
        }
    }

    public void NotifyWorkspaceLoaded()
    {
        _diagnostics.LogWorkspace("workspace_loaded", "Nexus desktop workspace klar");
    }

    private void ApplyPerformanceSettings()
    {
        _performance.PerformanceMode = _userSettings.Current.PerformanceMode;
        _statusTimer.Interval = _performance.PerformanceMode
            ? TimeSpan.FromSeconds(7)
            : TimeSpan.FromSeconds(4);
        _statusTimerRegistration.UpdateInterval(_statusTimer.Interval);
        OnPropertyChanged(nameof(PerformanceModeEnabled));
    }

    private void SyncWorkspaceApps()
    {
        var descriptors = _workspaceWindows.ActiveWindows;
        var existing = WorkspaceApps.ToDictionary(a => a.AppId, StringComparer.OrdinalIgnoreCase);

        foreach (var descriptor in descriptors)
        {
            if (existing.TryGetValue(descriptor.AppId, out var item))
            {
                item.IsActive = descriptor.IsActive;
                item.IsExternal = descriptor.IsExternal;
                item.IsMinimized = descriptor.State == WorkspaceWindowState.Minimized;
            }
            else
            {
                var created = new WorkspaceAppItem(descriptor.AppId, descriptor.DisplayName, descriptor.IconKey, descriptor.IsExternal)
                {
                    IsActive = descriptor.IsActive,
                    IsMinimized = descriptor.State == WorkspaceWindowState.Minimized,
                };
                created.ActivateRequested += (_, appId) => _workspaceWindows.Activate(appId);
                created.MinimizeRequested += (_, appId) => _workspaceWindows.Minimize(appId);
                created.CloseRequested += (_, appId) => _workspaceWindows.Close(appId);
                WorkspaceApps.Add(created);
            }
        }

        for (var i = WorkspaceApps.Count - 1; i >= 0; i--)
        {
            if (descriptors.All(d => !string.Equals(d.AppId, WorkspaceApps[i].AppId, StringComparison.OrdinalIgnoreCase)))
            {
                WorkspaceApps.RemoveAt(i);
            }
        }

        OnPropertyChanged(nameof(HasWorkspaceApps));
    }

    private void QueueAgentStatusRefresh()
    {
        if (Interlocked.CompareExchange(ref _agentRefreshBusy, 1, 0) != 0)
        {
            return;
        }

        _ = RefreshAgentStatusAsync();
    }

    private async Task RefreshAgentStatusAsync()
    {
        try
        {
            await _agentClient.RefreshStatusAsync();
            AgentStatusLabel = _agentClient.AgentStatusLabel;
            OnPropertyChanged(nameof(AgentStatusLabel));
        }
        finally
        {
            Interlocked.Exchange(ref _agentRefreshBusy, 0);
        }
    }

    private string ResolveUpdateLabel()
    {
        if (_updateService.LastResult?.IsUpdateAvailable == true)
        {
            return "Update klar";
        }

        return _updateService.LastResult?.Status switch
        {
            Models.UpdateCheckStatus.Checking => "Tjekker…",
            Models.UpdateCheckStatus.LocalBuildAhead => "Dev ahead",
            Models.UpdateCheckStatus.ChannelUnavailable => "Offline",
            _ => "Opdateret",
        };
    }

    private string ResolveThreatScanLabel()
    {
        var scan = Notifications.FirstOrDefault(static n =>
            n.Title.Contains("Sikkerhedsscanning", StringComparison.OrdinalIgnoreCase));
        return scan is null ? "Ingen data" : $"I dag {scan.Time}";
    }

    private void RefreshNotifications(SystemStatusSnapshot snapshot)
    {
        var now = DateTime.Now.ToString("HH:mm");
        var next = new List<NotificationItem>();
        var recentErrors = _diagnostics.GetRecentEvents(5)
            .Where(e => e.Category == DiagnosticEventCategory.Error)
            .Take(2)
            .ToList();

        next.Add(new NotificationItem(
            snapshot.ServicesOk && snapshot.NetworkOk && snapshot.SecurityOk
                ? "System health OK"
                : "System health kræver opmærksomhed",
            now,
            snapshot.ServicesOk && snapshot.NetworkOk && snapshot.SecurityOk ? "success" : "warning"));

        next.Add(new NotificationItem(
            $"Identity status: {snapshot.IdentityLatencyLabel}",
            now,
            snapshot.IdentityLatencyLabel.Contains("ms", StringComparison.OrdinalIgnoreCase) ? "success" : "accent"));

        if (_updateService.LastResult?.IsUpdateAvailable == true)
        {
            next.Add(new NotificationItem("Update klar til installation", now, "accent"));
        }
        else if (_updateService.LastResult?.Status == Models.UpdateCheckStatus.LocalBuildAhead)
        {
            next.Add(new NotificationItem("Lokal dev build er nyere end CDN latest", now, "info"));
        }

        if (_performance.PerformanceMode)
        {
            next.Add(new NotificationItem("Performance Mode aktiv", now, "info"));
        }

        if (!_performance.PerformanceMode && snapshot.CpuPercent >= 85)
        {
            next.Add(new NotificationItem("CPU er høj - Performance Mode kan reducere polling", now, "warning"));
        }

        foreach (var error in recentErrors)
        {
            next.Add(new NotificationItem(error.Message, error.TimestampUtc.ToLocalTime().ToString("HH:mm"), "warning"));
        }

        var hasImportantEvent =
            !snapshot.ServicesOk
            || !snapshot.NetworkOk
            || !snapshot.SecurityOk
            || _updateService.LastResult?.IsUpdateAvailable == true
            || recentErrors.Count > 0
            || (!_performance.PerformanceMode && snapshot.CpuPercent >= 85);

        if (_notificationsCleared && !hasImportantEvent)
        {
            Notifications.Clear();
            return;
        }

        if (hasImportantEvent)
        {
            _notificationsCleared = false;
        }

        Notifications.Clear();
        foreach (var notification in next)
        {
            Notifications.Add(notification);
        }
    }

    private void ClearNotifications()
    {
        _notificationsCleared = true;
        Notifications.Clear();
        NotificationCount = 0;
        OnPropertyChanged(nameof(NotificationCount));
        OnPropertyChanged(nameof(HasNotifications));
        OnPropertyChanged(nameof(HasNoNotifications));
    }

    private void QueueIdentityLatencyRefresh()
    {
        if (Interlocked.CompareExchange(ref _latencyRefreshBusy, 1, 0) != 0)
        {
            return;
        }

        _ = RefreshIdentityLatencyAsync();
    }

    private async Task RefreshIdentityLatencyAsync()
    {
        try
        {
            await _systemStatus.RefreshIdentityLatencyAsync(_identityAuthority);
            IdentityLatencyLabel = _systemStatus.IdentityLatencyLabel;
            OnPropertyChanged(nameof(IdentityLatencyLabel));
        }
        finally
        {
            Interlocked.Exchange(ref _latencyRefreshBusy, 0);
        }
    }

    private void RefreshDockItems()
    {
        DockItems.Clear();
        foreach (var item in _allDockItems.Where(i => _policy.IsDockItemAllowed(i.Label)))
        {
            DockItems.Add(item);
        }
    }

    private void HandleDockItem(string? label)
    {
        if (string.IsNullOrWhiteSpace(label))
        {
            return;
        }

        if (!_policy.IsDockItemAllowed(label))
        {
            return;
        }

        ActiveDockLabel = label;

        switch (label)
        {
            case "Search":
            case "Home":
                OpenLauncherRequested?.Invoke(this, EventArgs.Empty);
                return;
            case "App Hub":
                OpenAppHubRequested?.Invoke(this, EventArgs.Empty);
                return;
            case "Settings":
                if (_policy.CanOpenSettings())
                {
                    OpenSettingsRequested?.Invoke(this, EventArgs.Empty);
                }

                break;
            case "Files":
                if (_policy.CanOpenFiles())
                {
                    OpenFilesRequested?.Invoke(this, null);
                }

                break;
            case "Browser":
                if (_policy.CanOpenBrowser())
                {
                    OpenBrowserRequested?.Invoke(this, null);
                }

                break;
            case "Diagnostics":
                OpenDiagnosticsRequested?.Invoke(this, EventArgs.Empty);
                break;
            default:
                OpenLauncherRequested?.Invoke(this, EventArgs.Empty);
                break;
        }
    }

    private void UpdateClock()
    {
        ClockText = DateTime.Now.ToString("HH:mm");
        DockDateText = DateTime.Now.ToString("dd-MM-yyyy");
        CalendarDateText = DateTime.Now.ToString("d. MMMM");
        CalendarDayText = DateTime.Now.ToString("dddd");
        OnPropertyChanged(nameof(CalendarDateText));
        OnPropertyChanged(nameof(CalendarDayText));
    }

    private static string FormatRole(string role) => role switch
    {
        "MasterDeveloper" => "Master Developer",
        "Administrator" => "Administrator",
        "User" => "Bruger",
        _ => role,
    };

    private static string GetInitials(string name)
    {
        var parts = name.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        return parts.Length switch
        {
            0 => "N",
            1 => parts[0][..1].ToUpperInvariant(),
            _ => $"{parts[0][0]}{parts[^1][0]}".ToUpperInvariant(),
        };
    }

    private void OnPropertyChanged([CallerMemberName] string? name = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

public sealed class NotificationItem(string title, string time, string tone = "info")
{
    public string Title { get; } = title;
    public string Time { get; } = time;
    public string Tone { get; } = tone;
}

public sealed class DockNavItem(string label, string iconKey, bool isPrimary)
{
    public string Label { get; } = label;
    public string IconKey { get; } = iconKey;
    public bool IsPrimary { get; } = isPrimary;
}
