using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Nexus.Contracts.Apps;
using NexusShell.Services;
using NexusShell.Services.Diagnostics;
using NexusShell.Services.Policy;

namespace NexusShell.ViewModels;

public sealed class LauncherViewModel : INotifyPropertyChanged
{
    private readonly AppCatalogService _catalog;
    private readonly ProfileService _profile;
    private readonly AppLaunchService _launcher;
    private readonly NexusPolicyService _policy;
    private readonly AppLaunchTracker? _launchTracker;
    private string _searchText = string.Empty;
    private bool _showAllApps;

    public LauncherViewModel(
        AppCatalogService catalog,
        ProfileService profile,
        AppLaunchService launcher,
        NexusPolicyService policy,
        AppLaunchTracker? launchTracker = null)
    {
        _catalog = catalog;
        _profile = profile;
        _launcher = launcher;
        _policy = policy;
        _launchTracker = launchTracker;
        _policy.PolicyChanged += (_, _) => RefreshLists();
        LaunchCommand = new RelayCommand(Launch);
        CloseCommand = new RelayCommand(_ => CloseRequested?.Invoke(this, EventArgs.Empty));
        OpenSettingsCommand = new RelayCommand(_ => RequestSettings());
        LogoutCommand = new RelayCommand(_ => LogoutRequested?.Invoke(this, EventArgs.Empty));
        LockCommand = new RelayCommand(_ => LockRequested?.Invoke(this, EventArgs.Empty));
        RestartCommand = new RelayCommand(_ => RestartRequested?.Invoke(this, EventArgs.Empty));
        ShutdownCommand = new RelayCommand(_ => ShutdownRequested?.Invoke(this, EventArgs.Empty));
        ShowAllAppsCommand = new RelayCommand(_ => ShowAllApps = !ShowAllApps);
        OpenFilesCommand = new RelayCommand(_ => OpenFiles());
        PinnedApps = new ObservableCollection<LauncherAppItem>();
        RecentDocuments = new ObservableCollection<LauncherRecentDocument>();
        _allRecentDocuments =
        [
            new("System Status Rapport", "Rapporter", "14:21", "📄"),
            new("Projektplan Q2", "Dokumenter", "11:03", "📘"),
            new("Budget 2024", "Regneark", "i går", "📊"),
            new("Ninja Nexus Præsentation", "Præsentation", "i går", "📙"),
            new("Mødenoter Q1", "Noter", "i går", "📝"),
        ];
        RefreshLists();
    }

    private readonly List<LauncherRecentDocument> _allRecentDocuments;

    public event PropertyChangedEventHandler? PropertyChanged;
    public event EventHandler? CloseRequested;
    public event EventHandler? OpenSettingsRequested;
    public event EventHandler? OpenAppHubRequested;
    public event EventHandler<string?>? OpenFilesRequested;
    public event EventHandler<string?>? OpenBrowserRequested;
    public event EventHandler? OpenCalculatorRequested;
    public event EventHandler<string?>? OpenPhotosRequested;
    public event EventHandler<string?>? OpenMediaRequested;
    public event EventHandler? OpenStoreRequested;
    public event EventHandler? OpenConsoleRequested;
    public event EventHandler? OpenUpdateInfoRequested;
    public event EventHandler? OpenDiagnosticsRequested;
    public event EventHandler<string?>? OpenDeviceManagerRequested;
    public event EventHandler<AppEntry>? OpenPlaceholderRequested;
    public event EventHandler? LogoutRequested;
    public event EventHandler? LockRequested;
    public event EventHandler? RestartRequested;
    public event EventHandler? ShutdownRequested;
    public event EventHandler<string>? LaunchSucceeded;
    public event EventHandler<string>? LaunchBlocked;

    public ObservableCollection<LauncherAppItem> PinnedApps { get; }
    public ObservableCollection<LauncherRecentDocument> RecentDocuments { get; }

    public string SearchText
    {
        get => _searchText;
        set
        {
            _searchText = value;
            OnPropertyChanged();
            RefreshLists();
        }
    }

    public bool ShowAllApps
    {
        get => _showAllApps;
        set
        {
            _showAllApps = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(PinnedSectionTitle));
            RefreshLists();
        }
    }

    public string PinnedSectionTitle =>
        !string.IsNullOrWhiteSpace(SearchText)
            ? "Søgeresultater"
            : ShowAllApps ? "Alle apps" : "Fastgjorte apps";
    public string UserDisplayName => _profile.CurrentProfile?.DisplayName ?? "Bruger";
    public string UserRole => FormatRole(_profile.CurrentProfile?.Role ?? "User");
    public string UserInitials => GetInitials(UserDisplayName);

    public ICommand LaunchCommand { get; }
    public ICommand CloseCommand { get; }
    public ICommand OpenSettingsCommand { get; }
    public ICommand LogoutCommand { get; }
    public ICommand LockCommand { get; }
    public ICommand RestartCommand { get; }
    public ICommand ShutdownCommand { get; }
    public ICommand ShowAllAppsCommand { get; }
    public ICommand OpenFilesCommand { get; }

    public void RefreshLists()
    {
        var query = SearchText;
        var apps = string.IsNullOrWhiteSpace(query)
            ? _catalog.Apps
            : _catalog.Search(query).ToList();

        PinnedApps.Clear();
        if (!string.IsNullOrWhiteSpace(query))
        {
            foreach (var app in apps.Take(15).Select(CreateLauncherItem))
            {
                PinnedApps.Add(app);
            }
        }
        else
        {
            var pinnedSource = ShowAllApps ? apps : _catalog.GetPinned();
            foreach (var app in pinnedSource.Take(ShowAllApps ? 30 : 10).Select(CreateLauncherItem))
            {
                PinnedApps.Add(app);
            }
        }

        RecentDocuments.Clear();
        foreach (var doc in _allRecentDocuments.Take(4))
        {
            RecentDocuments.Add(doc);
        }

        OnPropertyChanged(nameof(UserDisplayName));
        OnPropertyChanged(nameof(UserRole));
        OnPropertyChanged(nameof(UserInitials));
        OnPropertyChanged(nameof(PinnedSectionTitle));
        OnPropertyChanged(nameof(CanOpenFiles));
    }

    public bool CanOpenFiles => _policy.CanOpenFiles();

    private void Launch(object? parameter)
    {
        var item = parameter as LauncherAppItem;
        var app = item?.App ?? parameter as AppEntry;
        if (app is null)
        {
            return;
        }

        if (item is not null && !item.CanOpen)
        {
            LaunchBlocked?.Invoke(this, $"{item.Name}: {item.StatusLabel}");
            return;
        }

        if (!_policy.CanLaunchApp(app))
        {
            LaunchBlocked?.Invoke(this, $"{app.Name}: Deaktiveret af policy");
            return;
        }

        if (app.ExecutablePath.StartsWith("nexus:", StringComparison.OrdinalIgnoreCase))
        {
            HandleNexusAction(app);
            return;
        }

        var (result, message) = _launcher.LaunchApp(app);
        if (result == LaunchResult.Success)
        {
            LaunchSucceeded?.Invoke(this, app.Name);
            CloseRequested?.Invoke(this, EventArgs.Empty);
            return;
        }

        if (result == LaunchResult.Blocked)
        {
            LaunchBlocked?.Invoke(this, message ?? "Denne funktion er låst af Ninja Nexus.");
        }
    }

    private void HandleNexusAction(AppEntry app)
    {
        switch (app.ExecutablePath.ToLowerInvariant())
        {
            case "nexus:settings":
                App.Services.SettingsViewModel.OpenSection(app.Arguments);
                RequestSettings();
                break;
            case "nexus:hub":
                RequestAppHub();
                break;
            case "nexus:desktop":
                CloseRequested?.Invoke(this, EventArgs.Empty);
                break;
            case "nexus:updates":
                RequestUpdateInfo();
                break;
            case "nexus:diagnostics":
                RequestDiagnostics();
                break;
            case "nexus:store":
                RequestStore();
                break;
            case "nexus:files":
                RequestFiles(app.Arguments);
                break;
            case "nexus:browser":
                RequestBrowser(app.Arguments);
                break;
            case "nexus:calculator":
                RequestCalculator();
                break;
            case "nexus:console":
                RequestConsole();
                break;
            case "nexus:photos":
                RequestPhotos(app.Arguments);
                break;
            case "nexus:media":
                RequestMedia(app.Arguments);
                break;
            case "nexus:devices":
            case "nexus:device-manager":
                RequestDeviceManager(app.Arguments);
                break;
            case "nexus:placeholder":
                RequestPlaceholder(app);
                break;
            default:
                RequestPlaceholder(new AppEntry
                {
                    Id = app.Id,
                    Name = app.Name,
                    Category = app.Category,
                    Description = string.IsNullOrWhiteSpace(app.Description)
                        ? "Denne Nexus-route er registreret, men mangler en hostet app-overflade."
                        : app.Description,
                    Status = "Under udvikling",
                    IconPath = app.IconPath,
                    ExecutablePath = app.ExecutablePath,
                });
                break;
        }
    }

    private LauncherAppItem CreateLauncherItem(AppEntry app)
    {
        var isPlaceholder = app.ExecutablePath.Equals("nexus:placeholder", StringComparison.OrdinalIgnoreCase);
        var policyAllowed = _policy.CanLaunchApp(app);
        var canOpen = policyAllowed && !isPlaceholder;
        var status = isPlaceholder
            ? app.Status
            : policyAllowed
                ? (app.Status.Equals("Deaktiveret af policy", StringComparison.OrdinalIgnoreCase) ? "Klar" : app.Status)
                : "Deaktiveret af policy";

        return new LauncherAppItem(app, canOpen, status);
    }

    private void RequestSettings()
    {
        OpenSettingsRequested?.Invoke(this, EventArgs.Empty);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestAppHub()
    {
        SearchText = string.Empty;
        ShowAllApps = true;
        CloseRequested?.Invoke(this, EventArgs.Empty);
        OpenAppHubRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestPlaceholder(AppEntry app)
    {
        OpenPlaceholderRequested?.Invoke(this, app);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void OpenFiles()
    {
        if (!_policy.CanOpenFiles())
        {
            return;
        }

        RequestFiles("documents");
    }

    private void RequestFiles(string? pathHint)
    {
        OpenFilesRequested?.Invoke(this, pathHint);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestBrowser(string? urlHint)
    {
        OpenBrowserRequested?.Invoke(this, urlHint);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestCalculator()
    {
        OpenCalculatorRequested?.Invoke(this, EventArgs.Empty);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestConsole()
    {
        OpenConsoleRequested?.Invoke(this, EventArgs.Empty);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestPhotos(string? sectionHint)
    {
        OpenPhotosRequested?.Invoke(this, sectionHint);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestMedia(string? sectionHint)
    {
        OpenMediaRequested?.Invoke(this, sectionHint);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestStore()
    {
        OpenStoreRequested?.Invoke(this, EventArgs.Empty);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestUpdateInfo()
    {
        OpenUpdateInfoRequested?.Invoke(this, EventArgs.Empty);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestDiagnostics()
    {
        OpenDiagnosticsRequested?.Invoke(this, EventArgs.Empty);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    private void RequestDeviceManager(string? sectionHint)
    {
        OpenDeviceManagerRequested?.Invoke(this, sectionHint);
        CloseRequested?.Invoke(this, EventArgs.Empty);
    }

    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 LauncherRecentDocument(string name, string category, string time, string glyph)
{
    public string Name { get; } = name;
    public string Category { get; } = category;
    public string Time { get; } = time;
    public string Glyph { get; } = glyph;
}

public sealed class LauncherAppItem(AppEntry app, bool canOpen, string statusLabel)
{
    public AppEntry App { get; } = app;
    public bool CanOpen { get; } = canOpen;
    public string StatusLabel { get; } = statusLabel;
    public string Id => App.Id;
    public string Name => App.Name;
    public string Category => App.Category;
    public string Description => App.Description;
    public string IconPath => string.IsNullOrWhiteSpace(App.IconPath) ? "⬡" : App.IconPath;
}

public static class AppEntryVisual
{
    public static string GetGlyph(AppEntry app) =>
        string.IsNullOrWhiteSpace(app.IconPath) ? "⬡" : app.IconPath;
}
