using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using NexusShell.Models;
using NexusShell.Services;

namespace NexusShell.ViewModels;

public sealed class NexusStoreViewModel : INotifyPropertyChanged
{
    private readonly NexusStoreService _store;
    private NexusStoreNavSection _selectedSection = NexusStoreNavSection.Home;
    private NexusStorePackageItem? _selectedPackage;
    private NexusStoreNavItem? _selectedNav;
    private string _searchText = string.Empty;
    private string _statusMessage = string.Empty;
    private bool _isOperationInProgress;
    private double _operationProgress;
    private bool _isOperationIndeterminate = true;
    private bool _isRefreshingCatalog;

    public NexusStoreViewModel(NexusStoreService store)
    {
        _store = store;
        NavItems = new ObservableCollection<NexusStoreNavItem>();
        CatalogItems = new ObservableCollection<NexusStorePackageItem>();
        HomeSections = new ObservableCollection<NexusStoreHomeSection>();
        HistoryItems = new ObservableCollection<NexusStoreHistoryEntry>();

        SelectNavCommand = new RelayCommand(p => SelectNav(p as NexusStoreNavItem));
        SelectPackageCommand = new RelayCommand(p => SelectPackage(p as NexusStorePackageItem));
        InstallCommand = new RelayCommand(_ => _ = RunInstallAsync(), _ => CanRunInstall);
        OpenCommand = new RelayCommand(_ => OpenSelected(), _ => SelectedPackage?.CanOpen == true);
        UpdateCommand = new RelayCommand(_ => _ = RunUpdateAsync(), _ => CanRunUpdate);
        RemoveCommand = new RelayCommand(_ => _ = RunRemoveAsync(), _ => CanRunRemove);
        ToggleFavoriteCommand = new RelayCommand(_ => ToggleFavoriteSelected(), _ => SelectedPackage is not null);
        CloseCommand = new RelayCommand(_ => CloseRequested?.Invoke(this, EventArgs.Empty));
        RefreshCommand = new RelayCommand(_ => ReloadFromRepository());

        _store.CatalogChanged += (_, _) => ReloadUiOnly();
        _store.DeploymentProgress += (_, e) => OnDeploymentProgress(e);
        ReloadUiOnly();
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public event EventHandler? CloseRequested;
    public event EventHandler<string?>? OpenFilesRequested;
    public event EventHandler<string?>? OpenBrowserRequested;
    public event EventHandler? OpenCalculatorRequested;
    public event EventHandler<string?>? OpenSettingsRequested;
    public event EventHandler? OpenConsoleRequested;
    public event EventHandler<string>? LaunchBlocked;

    public ObservableCollection<NexusStoreNavItem> NavItems { get; }
    public ObservableCollection<NexusStorePackageItem> CatalogItems { get; }
    public ObservableCollection<NexusStoreHomeSection> HomeSections { get; }
    public ObservableCollection<NexusStoreHistoryEntry> HistoryItems { get; }

    public string Title => "Ninja Store";

    public string ShellVersionLabel => _store.ShellVersionLabel;

    public string UpdateChannelLabel => _store.UpdateChannelLabel;

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

    public string StatusMessage
    {
        get => _statusMessage;
        private set { _statusMessage = value; OnPropertyChanged(); }
    }

    public bool IsOperationInProgress
    {
        get => _isOperationInProgress;
        private set
        {
            _isOperationInProgress = value;
            OnPropertyChanged();
            CommandManager.InvalidateRequerySuggested();
        }
    }

    public double OperationProgress
    {
        get => _operationProgress;
        private set { _operationProgress = value; OnPropertyChanged(); OnPropertyChanged(nameof(OperationProgressLabel)); }
    }

    public bool IsOperationIndeterminate
    {
        get => _isOperationIndeterminate;
        private set { _isOperationIndeterminate = value; OnPropertyChanged(); }
    }

    public string OperationProgressLabel =>
        IsOperationInProgress ? $"{OperationProgress:0}%" : string.Empty;

    public NexusStoreNavSection SelectedSection
    {
        get => _selectedSection;
        private set
        {
            _selectedSection = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(IsHomeSection));
            OnPropertyChanged(nameof(IsHistorySection));
            OnPropertyChanged(nameof(SectionTitle));
        }
    }

    public NexusStoreNavItem? SelectedNav
    {
        get => _selectedNav;
        private set { _selectedNav = value; OnPropertyChanged(); }
    }

    public NexusStorePackageItem? SelectedPackage
    {
        get => _selectedPackage;
        private set
        {
            _selectedPackage = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(HasSelection));
            OnPropertyChanged(nameof(DetailName));
            OnPropertyChanged(nameof(DetailVersion));
            OnPropertyChanged(nameof(DetailPublisher));
            OnPropertyChanged(nameof(DetailDescription));
            OnPropertyChanged(nameof(DetailChangelog));
            OnPropertyChanged(nameof(DetailSize));
            OnPropertyChanged(nameof(DetailStatus));
            OnPropertyChanged(nameof(DetailCategory));
            OnPropertyChanged(nameof(DetailInstalledVersion));
            OnPropertyChanged(nameof(InstallActionLabel));
            OnPropertyChanged(nameof(UpdateActionLabel));
            OnPropertyChanged(nameof(CanRunInstall));
            OnPropertyChanged(nameof(CanRunUpdate));
            OnPropertyChanged(nameof(CanRunRemove));
            OnPropertyChanged(nameof(FavoriteLabel));
            CommandManager.InvalidateRequerySuggested();
        }
    }

    public bool IsHomeSection => SelectedSection == NexusStoreNavSection.Home;
    public bool IsHistorySection => SelectedSection == NexusStoreNavSection.History;
    public bool HasSelection => SelectedPackage is not null;

    public bool CanRunInstall => SelectedPackage?.CanInstall == true && !IsOperationInProgress;
    public bool CanRunUpdate => SelectedPackage?.CanUpdate == true && !IsOperationInProgress;
    public bool CanRunRemove => SelectedPackage?.CanRemove == true && !IsOperationInProgress;

    public string SectionTitle => SelectedNav?.Label ?? "Forside";

    public string DetailName => SelectedPackage?.Manifest.Name ?? "Vælg en app";
    public string DetailVersion => SelectedPackage is null ? "—" : $"v{SelectedPackage.Manifest.Version}";
    public string DetailInstalledVersion => SelectedPackage?.InstalledVersion is { Length: > 0 } v
        ? $"Installeret: v{v}"
        : "Ikke installeret";
    public string DetailPublisher => SelectedPackage?.Manifest.Publisher ?? "—";
    public string DetailDescription => SelectedPackage?.Manifest.Description ?? "Vælg en app i kataloget for at se detaljer.";
    public string DetailChangelog => SelectedPackage?.Manifest.Changelog ?? "—";
    public string DetailSize => SelectedPackage?.Manifest.SizeLabel ?? "—";
    public string DetailStatus => SelectedPackage?.StatusLabel ?? "—";
    public string DetailCategory => SelectedPackage?.Manifest.Category ?? "—";
    public string InstallActionLabel => SelectedPackage?.InstallActionLabel ?? "Installer";
    public string UpdateActionLabel => SelectedPackage?.PackageState == NexusPackageState.UpdateAvailable
        ? "Opdater"
        : "Opdater";
    public string FavoriteLabel => SelectedPackage?.IsFavorite == true ? "Fjern favorit" : "Tilføj favorit";

    public ICommand SelectNavCommand { get; }
    public ICommand SelectPackageCommand { get; }
    public ICommand InstallCommand { get; }
    public ICommand OpenCommand { get; }
    public ICommand UpdateCommand { get; }
    public ICommand RemoveCommand { get; }
    public ICommand ToggleFavoriteCommand { get; }
    public ICommand CloseCommand { get; }
    public ICommand RefreshCommand { get; }

    public void ReloadFromRepository()
    {
        if (_isRefreshingCatalog)
        {
            return;
        }

        _isRefreshingCatalog = true;
        try
        {
            _store.Reload();
            ReloadUiOnly();
        }
        finally
        {
            _isRefreshingCatalog = false;
        }
    }

    public void ReloadUiOnly()
    {
        NavItems.Clear();
        foreach (var item in _store.GetNavItems())
        {
            NavItems.Add(item);
        }

        if (SelectedNav is null)
        {
            SelectedNav = NavItems.FirstOrDefault();
            if (SelectedNav is not null)
            {
                SelectedSection = SelectedNav.Section;
            }
        }
        else
        {
            var updated = NavItems.FirstOrDefault(n => n.Section == SelectedNav.Section);
            SelectedNav = updated ?? NavItems.FirstOrDefault();
        }

        HomeSections.Clear();
        foreach (var section in _store.GetHomeSections())
        {
            HomeSections.Add(section);
        }

        HistoryItems.Clear();
        foreach (var entry in _store.GetHistory())
        {
            HistoryItems.Add(entry);
        }

        ApplyCatalogFilter();
        if (SelectedPackage is not null)
        {
            SelectedPackage = _store.GetItem(SelectedPackage.Manifest.Id);
        }
    }

    private async Task RunInstallAsync()
    {
        if (SelectedPackage is null || !SelectedPackage.CanInstall)
        {
            return;
        }

        await RunDeploymentAsync(
            SelectedPackage.Manifest.Id,
            SelectedPackage.Manifest.Name,
            _store.InstallAsync);
    }

    private async Task RunUpdateAsync()
    {
        if (SelectedPackage is null || !SelectedPackage.CanUpdate)
        {
            return;
        }

        await RunDeploymentAsync(
            SelectedPackage.Manifest.Id,
            SelectedPackage.Manifest.Name,
            _store.UpdateAsync);
    }

    private async Task RunRemoveAsync()
    {
        if (SelectedPackage is null || !SelectedPackage.CanRemove)
        {
            return;
        }

        await RunDeploymentAsync(
            SelectedPackage.Manifest.Id,
            SelectedPackage.Manifest.Name,
            _store.RemoveAsync);
    }

    private async Task RunDeploymentAsync(
        string packageId,
        string packageName,
        Func<string, CancellationToken, Task<NexusDeploymentResult>> operation)
    {
        IsOperationInProgress = true;
        IsOperationIndeterminate = false;
        OperationProgress = 0;
        StatusMessage = $"{packageName}: starter…";

        try
        {
            var result = await operation(packageId, CancellationToken.None);
            StatusMessage = result.Success
                ? $"{packageName}: {result.Message}"
                : $"{packageName}: fejl — {result.Message}";
            RefreshSelection(packageId);
        }
        catch (Exception ex)
        {
            StatusMessage = $"{packageName}: fejl — {ex.Message}";
        }
        finally
        {
            IsOperationInProgress = false;
            IsOperationIndeterminate = true;
            OperationProgress = 0;
        }
    }

    private void OnDeploymentProgress(NexusDeploymentProgress progress)
    {
        if (SelectedPackage is null ||
            !string.Equals(SelectedPackage.Manifest.Id, progress.PackageId, StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        IsOperationInProgress = progress.Phase is not NexusDeploymentPhase.None and not NexusDeploymentPhase.Failed;
        IsOperationIndeterminate = progress.Percent <= 0;
        OperationProgress = progress.Percent;
        StatusMessage = progress.Message;
        SelectedPackage = _store.GetItem(progress.PackageId);
    }

    private void SelectNav(NexusStoreNavItem? item)
    {
        if (item is null)
        {
            return;
        }

        SelectedNav = item;
        SelectedSection = item.Section;
        ApplyCatalogFilter();
    }

    private void SelectPackage(NexusStorePackageItem? item)
    {
        if (item is null)
        {
            return;
        }

        SelectedPackage = _store.GetItem(item.Manifest.Id);
    }

    private void ApplyCatalogFilter()
    {
        CatalogItems.Clear();
        var items = _store.GetItemsForSection(SelectedSection);
        if (!string.IsNullOrWhiteSpace(SearchText))
        {
            items = items.Where(i =>
                i.Manifest.Name.Contains(SearchText, StringComparison.OrdinalIgnoreCase) ||
                i.Manifest.Category.Contains(SearchText, StringComparison.OrdinalIgnoreCase) ||
                i.Manifest.Publisher.Contains(SearchText, StringComparison.OrdinalIgnoreCase)).ToList();
        }

        foreach (var item in items)
        {
            CatalogItems.Add(item);
        }

        if (SelectedSection != NexusStoreNavSection.Home &&
            SelectedSection != NexusStoreNavSection.History &&
            CatalogItems.Count > 0 &&
            SelectedPackage is null)
        {
            SelectedPackage = CatalogItems[0];
        }
    }

    private void ToggleFavoriteSelected()
    {
        if (SelectedPackage is null)
        {
            return;
        }

        _store.ToggleFavorite(SelectedPackage.Manifest.Id);
        RefreshSelection(SelectedPackage.Manifest.Id);
    }

    private void OpenSelected()
    {
        if (SelectedPackage is null)
        {
            return;
        }

        var entry = SelectedPackage.Manifest.EntryPoint;
        if (!entry.StartsWith("nexus:", StringComparison.OrdinalIgnoreCase))
        {
            LaunchBlocked?.Invoke(this, $"{SelectedPackage.Manifest.Name} kan ikke åbnes i Store 1.0 (mock app).");
            return;
        }

        var scheme = _store.ResolveLaunchScheme(entry);
        var args = _store.ResolveLaunchArguments(entry);
        switch (scheme)
        {
            case "nexus:files":
                OpenFilesRequested?.Invoke(this, args);
                break;
            case "nexus:browser":
                OpenBrowserRequested?.Invoke(this, args);
                break;
            case "nexus:calculator":
                OpenCalculatorRequested?.Invoke(this, EventArgs.Empty);
                break;
            case "nexus:settings":
                OpenSettingsRequested?.Invoke(this, args);
                break;
            case "nexus:console":
                OpenConsoleRequested?.Invoke(this, EventArgs.Empty);
                break;
            case "nexus:media":
                LaunchBlocked?.Invoke(this, "Åbn Nexus Media fra launcher (nexus:media).");
                break;
            default:
                LaunchBlocked?.Invoke(this, "Funktion ikke tilgængelig endnu.");
                break;
        }
    }

    private void RefreshSelection(string? packageId = null)
    {
        var id = packageId ?? SelectedPackage?.Manifest.Id;
        ReloadUiOnly();
        if (id is not null)
        {
            SelectedPackage = _store.GetItem(id);
        }
    }

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