using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using NexusShell.Models;
using NexusShell.Services;

namespace NexusShell.ViewModels;

public sealed class NexusPhotosViewModel : INotifyPropertyChanged, IDisposable
{
    private readonly NexusPhotosLibraryService _library;
    private readonly NexusThumbnailService _thumbnails;
    private readonly NexusImageViewerService _viewer;
    private readonly NexusSlideshowService _slideshow;
    private readonly NexusMetadataService _metadata;
    private readonly NexusPhotosFavoritesService _favorites;

    private NexusPhotosSection _currentSection = NexusPhotosSection.Library;
    private string? _selectedFolderPath;
    private string _searchQuery = string.Empty;
    private string _statusMessage = string.Empty;
    private int _gridZoomLevel = 1;
    private NexusPhotoGridItem? _selectedGridItem;
    private NexusPhotoMetadata _metadataState;
    private IReadOnlyList<NexusPhotoItem> _allPhotos = [];
    private int _thumbnailGeneration;

    public NexusPhotosViewModel(
        NexusPhotosLibraryService library,
        NexusThumbnailService thumbnails,
        NexusImageViewerService viewer,
        NexusSlideshowService slideshow,
        NexusMetadataService metadata,
        NexusPhotosFavoritesService favorites)
    {
        _library = library;
        _thumbnails = thumbnails;
        _viewer = viewer;
        _slideshow = slideshow;
        _metadata = metadata;
        _favorites = favorites;
        _metadataState = _metadata.BuildMetadata(null);

        GridItems = new ObservableCollection<NexusPhotoGridItem>();
        NavItems = new ObservableCollection<NexusPhotoNavItem>();
        HomeSections = new ObservableCollection<NexusPhotoHomeSection>();
        FolderItems = new ObservableCollection<NexusPhotoFolderSummary>();

        CloseCommand = new RelayCommand(_ => CloseRequested?.Invoke(this, EventArgs.Empty));
        RefreshCommand = new RelayCommand(_ => Reload(forceRefresh: true));
        NavigateSectionCommand = new RelayCommand(p => NavigateSection(ParseSection(p)));
        OpenPhotoCommand = new RelayCommand(p => OpenPhoto(p as NexusPhotoGridItem));
        SelectPhotoCommand = new RelayCommand(p => SelectPhoto(p as NexusPhotoGridItem));
        ToggleFavoriteCommand = new RelayCommand(p => ToggleFavorite(p as NexusPhotoGridItem ?? SelectedGridItem));
        ClearSearchCommand = new RelayCommand(_ => SearchQuery = string.Empty, _ => !string.IsNullOrWhiteSpace(SearchQuery));
        ZoomGridOutCommand = new RelayCommand(_ => GridZoomLevel = Math.Max(0, GridZoomLevel - 1), _ => GridZoomLevel > 0);
        ZoomGridInCommand = new RelayCommand(_ => GridZoomLevel = Math.Min(2, GridZoomLevel + 1), _ => GridZoomLevel < 2);
        OpenFolderCommand = new RelayCommand(p => OpenFolder(p as NexusPhotoFolderSummary));
        ViewerCloseCommand = new RelayCommand(_ => _viewer.Close());
        ViewerPreviousCommand = new RelayCommand(_ => _viewer.GoPrevious(), _ => _viewer.CanGoPrevious);
        ViewerNextCommand = new RelayCommand(_ => _viewer.GoNext(), _ => _viewer.CanGoNext);
        ViewerZoomCommand = new RelayCommand(_ => _viewer.CycleZoomMode());
        ViewerInfoCommand = new RelayCommand(_ => _viewer.ToggleInfoPanel());
        StartSlideshowCommand = new RelayCommand(_ => StartSlideshow(), _ => GridItems.Count > 0);
        SlideshowCloseCommand = new RelayCommand(_ => _slideshow.Stop());

        _viewer.StateChanged += (_, _) => OnViewerStateChanged();
        _slideshow.StateChanged += (_, _) => OnSlideshowStateChanged();
        _favorites.FavoritesChanged += (_, _) => Reload(forceRefresh: false);

        BuildNavItems();
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public event EventHandler? CloseRequested;

    public ObservableCollection<NexusPhotoGridItem> GridItems { get; }
    public ObservableCollection<NexusPhotoNavItem> NavItems { get; }
    public ObservableCollection<NexusPhotoHomeSection> HomeSections { get; }
    public ObservableCollection<NexusPhotoFolderSummary> FolderItems { get; }

    public NexusPhotosSection CurrentSection
    {
        get => _currentSection;
        private set
        {
            _currentSection = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(IsLibraryHome));
            OnPropertyChanged(nameof(IsFolderBrowser));
            OnPropertyChanged(nameof(IsGridVisible));
        }
    }

    public bool IsLibraryHome => CurrentSection == NexusPhotosSection.Library && string.IsNullOrWhiteSpace(_selectedFolderPath);
    public bool IsFolderBrowser => CurrentSection == NexusPhotosSection.Folders && string.IsNullOrWhiteSpace(_selectedFolderPath);
    public bool IsGridVisible => !IsLibraryHome && !IsFolderBrowser;

    public string SearchQuery
    {
        get => _searchQuery;
        set
        {
            if (_searchQuery == value)
            {
                return;
            }

            _searchQuery = value;
            OnPropertyChanged();
            ApplyCurrentView();
        }
    }

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

    public int GridZoomLevel
    {
        get => _gridZoomLevel;
        set
        {
            if (_gridZoomLevel == value)
            {
                return;
            }

            _gridZoomLevel = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(ThumbnailSize));
            OnPropertyChanged(nameof(ZoomLevelLabel));
            _ = LoadThumbnailsAsync();
        }
    }

    public int ThumbnailSize => _thumbnails.SizeForZoomLevel(GridZoomLevel);
    public string ZoomLevelLabel => GridZoomLevel switch { 0 => "Lille", 2 => "Stor", _ => "Medium" };

    public NexusPhotoGridItem? SelectedGridItem
    {
        get => _selectedGridItem;
        set
        {
            if (_selectedGridItem == value)
            {
                return;
            }

            if (_selectedGridItem is not null)
            {
                _selectedGridItem.IsSelected = false;
            }

            _selectedGridItem = value;
            if (_selectedGridItem is not null)
            {
                _selectedGridItem.IsSelected = true;
            }

            MetadataState = _metadata.BuildMetadata(_selectedGridItem?.Photo);
            OnPropertyChanged();
        }
    }

    public NexusPhotoMetadata MetadataState
    {
        get => _metadataState;
        private set { _metadataState = value; OnPropertyChanged(); }
    }

    public bool IsViewerOpen => _viewer.IsOpen;
    public bool IsSlideshowActive => _slideshow.IsActive;
    public NexusPhotoItem? ViewerPhoto => _viewer.CurrentPhoto;
    public NexusPhotoItem? SlideshowPhoto => _slideshow.CurrentPhoto;
    public ImageSource? ViewerImageSource { get; private set; }
    public ImageSource? SlideshowImageSource { get; private set; }
    public NexusImageZoomMode ViewerZoomMode => _viewer.ZoomMode;
    public string ViewerPositionLabel => _viewer.PositionLabel;
    public string ViewerZoomLabel => _viewer.ZoomLabel;
    public bool ViewerShowInfo => _viewer.ShowInfoPanel;
    public NexusPhotoMetadata ViewerMetadata => _metadata.BuildMetadata(_viewer.CurrentPhoto);
    public string SlideshowPositionLabel => _slideshow.PositionLabel;

    public ICommand CloseCommand { get; }
    public ICommand RefreshCommand { get; }
    public ICommand NavigateSectionCommand { get; }
    public ICommand OpenPhotoCommand { get; }
    public ICommand SelectPhotoCommand { get; }
    public ICommand ToggleFavoriteCommand { get; }
    public ICommand ClearSearchCommand { get; }
    public ICommand ZoomGridOutCommand { get; }
    public ICommand ZoomGridInCommand { get; }
    public ICommand OpenFolderCommand { get; }
    public ICommand ViewerCloseCommand { get; }
    public ICommand ViewerPreviousCommand { get; }
    public ICommand ViewerNextCommand { get; }
    public ICommand ViewerZoomCommand { get; }
    public ICommand ViewerInfoCommand { get; }
    public ICommand StartSlideshowCommand { get; }
    public ICommand SlideshowCloseCommand { get; }

    public void OpenSection(string? hint)
    {
        _selectedFolderPath = null;
        CurrentSection = hint?.ToLowerInvariant() switch
        {
            "favorites" or "favoritter" => NexusPhotosSection.Favorites,
            "recent" or "seneste" => NexusPhotosSection.Recent,
            "folders" or "mapper" => NexusPhotosSection.Folders,
            "pictures" or "billeder" => NexusPhotosSection.Pictures,
            "screenshots" or "skærmbilleder" => NexusPhotosSection.Screenshots,
            "downloads" => NexusPhotosSection.Downloads,
            "camera" or "kamera" => NexusPhotosSection.Camera,
            _ => NexusPhotosSection.Library,
        };
        Reload(forceRefresh: false);
    }

    public void OpenFilePath(string filePath)
    {
        if (!_library.IsSupportedExtension(filePath))
        {
            StatusMessage = _library.GetUnsupportedFormatMessage(filePath);
            return;
        }

        var info = new FileInfo(filePath);
        var photo = new NexusPhotoItem
        {
            FullPath = info.FullName,
            FileName = info.Name,
            FolderPath = info.DirectoryName ?? string.Empty,
            FolderLabel = info.DirectoryName is { Length: > 0 } dir ? Path.GetFileName(dir) : "Fil",
            FileSizeBytes = info.Length,
            CreatedUtc = info.CreationTimeUtc,
            ModifiedUtc = info.LastWriteTimeUtc,
            Format = info.Extension.TrimStart('.').ToUpperInvariant(),
            Extension = info.Extension,
        };

        _viewer.Open([photo], 0);
    }

    public void HandleViewerKey(System.Windows.Input.Key key)
    {
        if (_slideshow.IsActive)
        {
            if (key == System.Windows.Input.Key.Escape)
            {
                _slideshow.Stop();
            }
            else if (key == System.Windows.Input.Key.Left)
            {
                _slideshow.GoPrevious();
            }
            else if (key == System.Windows.Input.Key.Right)
            {
                _slideshow.GoNext();
            }

            return;
        }

        if (!_viewer.IsOpen)
        {
            return;
        }

        switch (key)
        {
            case System.Windows.Input.Key.Escape:
                _viewer.Close();
                break;
            case System.Windows.Input.Key.Left:
                _viewer.GoPrevious();
                break;
            case System.Windows.Input.Key.Right:
                _viewer.GoNext();
                break;
        }
    }

    public void HandleViewerMouseWheel(int delta)
    {
        if (_viewer.IsOpen)
        {
            _viewer.HandleMouseWheel(delta);
        }
    }

    public void Dispose()
    {
        _slideshow.Dispose();
    }

    private void NavigateSection(NexusPhotosSection section)
    {
        _selectedFolderPath = null;
        CurrentSection = section;
        SearchQuery = string.Empty;
        ApplyCurrentView();
    }

    private void OpenFolder(NexusPhotoFolderSummary? folder)
    {
        if (folder is null)
        {
            return;
        }

        _selectedFolderPath = folder.FolderPath;
        CurrentSection = NexusPhotosSection.Folders;
        OnPropertyChanged(nameof(IsFolderBrowser));
        OnPropertyChanged(nameof(IsGridVisible));
        ApplyCurrentView();
    }

    private void OpenPhoto(NexusPhotoGridItem? item)
    {
        if (item is null)
        {
            return;
        }

        if (!_library.IsSupportedExtension(item.Photo.FullPath))
        {
            StatusMessage = _library.GetUnsupportedFormatMessage(item.Photo.FullPath);
            return;
        }

        var items = GridItems.Select(static g => g.Photo).ToList();
        var index = items.FindIndex(p => string.Equals(p.FullPath, item.Photo.FullPath, StringComparison.OrdinalIgnoreCase));
        _viewer.Open(items, Math.Max(0, index));
    }

    private void SelectPhoto(NexusPhotoGridItem? item) => SelectedGridItem = item;

    private void ToggleFavorite(NexusPhotoGridItem? item)
    {
        if (item is null)
        {
            return;
        }

        _favorites.ToggleFavorite(item.Photo);
        MetadataState = _metadata.BuildMetadata(item.Photo);
        if (CurrentSection == NexusPhotosSection.Favorites)
        {
            ApplyCurrentView();
        }
    }

    private void StartSlideshow()
    {
        var items = GridItems.Select(static g => g.Photo).ToList();
        if (items.Count == 0)
        {
            return;
        }

        var startIndex = SelectedGridItem is not null
            ? items.FindIndex(p => string.Equals(p.FullPath, SelectedGridItem.Photo.FullPath, StringComparison.OrdinalIgnoreCase))
            : 0;
        _slideshow.Start(items, Math.Max(0, startIndex));
    }

    private void Reload(bool forceRefresh)
    {
        StatusMessage = "Scanner bibliotek…";
        _ = ReloadAsync(forceRefresh);
    }

    private async Task ReloadAsync(bool forceRefresh)
    {
        var dispatcher = System.Windows.Application.Current?.Dispatcher
            ?? Dispatcher.CurrentDispatcher;

        try
        {
            var photos = await Task.Run(() => _library.ScanLibrary(forceRefresh));
            await dispatcher.InvokeAsync(() =>
            {
                _allPhotos = photos;
                _favorites.ApplyFavorites(_allPhotos);
                ApplyCurrentView();
            });
        }
        catch (Exception ex)
        {
            await dispatcher.InvokeAsync(() =>
            {
                StatusMessage = $"Kunne ikke scanne bibliotek: {ex.Message}";
            });
        }
    }

    private void ApplyCurrentView()
    {
        HomeSections.Clear();
        FolderItems.Clear();
        GridItems.Clear();
        SelectedGridItem = null;

        if (IsLibraryHome)
        {
            BuildHomeDashboard();
            StatusMessage = $"{_allPhotos.Count} billeder i biblioteket";
            return;
        }

        if (IsFolderBrowser)
        {
            foreach (var folder in _library.GetFolderSummaries(_allPhotos))
            {
                FolderItems.Add(folder);
            }

            StatusMessage = FolderItems.Count switch
            {
                0 => "Ingen mapper med billeder.",
                1 => "1 mappe",
                _ => $"{FolderItems.Count} mapper",
            };
            return;
        }

        IReadOnlyList<NexusPhotoItem> source;
        if (!string.IsNullOrWhiteSpace(_selectedFolderPath))
        {
            source = _library.GetPhotosInFolder(_selectedFolderPath, _allPhotos);
        }
        else
        {
            source = _library.GetPhotosForSection(CurrentSection, _allPhotos);
        }

        source = _library.SearchLocal(SearchQuery, source);
        foreach (var photo in source)
        {
            GridItems.Add(new NexusPhotoGridItem(photo));
        }

        StatusMessage = string.IsNullOrWhiteSpace(SearchQuery)
            ? GridItems.Count switch
            {
                0 => "Ingen billeder i denne visning.",
                1 => "1 billede",
                _ => $"{GridItems.Count} billeder",
            }
            : GridItems.Count switch
            {
                0 => $"Ingen resultater for \"{SearchQuery.Trim()}\"",
                1 => "1 resultat",
                _ => $"{GridItems.Count} resultater",
            };

        _ = LoadThumbnailsAsync();
    }

    private void BuildHomeDashboard()
    {
        AddHomeSection("Seneste billeder", NexusLucideIcons.Clock,
            _library.GetRecentPhotos(8, _allPhotos));
        AddHomeSection("Favoritter", NexusLucideIcons.Star,
            _allPhotos.Where(static p => p.IsFavorite).Take(8).ToList());
        AddHomeSection("Største mapper", NexusLucideIcons.FolderTree,
            _library.GetLargestFolders(4, _allPhotos)
                .SelectMany(f => _library.GetPhotosInFolder(f.FolderPath, _allPhotos).Take(1))
                .ToList(),
            titleOnly: false);
        AddHomeSection("Nyligt importerede", NexusLucideIcons.Download,
            _library.GetRecentlyImported(8, _allPhotos));
    }

    private void AddHomeSection(string title, string iconPath, IReadOnlyList<NexusPhotoItem> photos, bool titleOnly = true)
    {
        if (photos.Count == 0)
        {
            return;
        }

        var items = photos.Select(static p => new NexusPhotoGridItem(p)).ToList();
        HomeSections.Add(new NexusPhotoHomeSection
        {
            Title = title,
            IconPath = iconPath,
            Items = items,
        });
        if (titleOnly)
        {
            _ = LoadThumbnailsForItemsAsync(items);
        }
        else
        {
            _ = LoadThumbnailsForItemsAsync(items);
        }
    }

    private async Task LoadThumbnailsAsync()
    {
        var generation = ++_thumbnailGeneration;
        var size = ThumbnailSize;
        var items = GridItems.ToList();
        await LoadThumbnailsForItemsAsync(items, generation, size);
    }

    private async Task LoadThumbnailsForItemsAsync(
        IReadOnlyList<NexusPhotoGridItem> items,
        int? generation = null,
        int? sizeOverride = null)
    {
        var gen = generation ?? ++_thumbnailGeneration;
        var size = sizeOverride ?? ThumbnailSize;
        var dispatcher = System.Windows.Application.Current?.Dispatcher
            ?? Dispatcher.CurrentDispatcher;

        foreach (var item in items)
        {
            if (generation.HasValue && gen != _thumbnailGeneration)
            {
                break;
            }

            var thumb = await dispatcher.InvokeAsync(() =>
                _thumbnails.GetThumbnail(item.Photo.FullPath, size));

            if (generation.HasValue && gen != _thumbnailGeneration)
            {
                break;
            }

            item.Thumbnail = thumb;
            await Task.Yield();
        }
    }

    private void BuildNavItems()
    {
        NavItems.Clear();
        AddNav(NexusPhotosSection.Library, "Bibliotek", NexusLucideIcons.Home);
        AddNav(NexusPhotosSection.Favorites, "Favoritter", NexusLucideIcons.Star);
        AddNav(NexusPhotosSection.Recent, "Seneste", NexusLucideIcons.Clock);
        AddNav(NexusPhotosSection.Folders, "Mapper", NexusLucideIcons.FolderTree);
        AddNav(NexusPhotosSection.Pictures, "Billeder", NexusLucideIcons.Image);
        AddNav(NexusPhotosSection.Screenshots, "Skærmbilleder", NexusLucideIcons.Monitor);
        AddNav(NexusPhotosSection.Downloads, "Downloads", NexusLucideIcons.Download);
        AddNav(NexusPhotosSection.Camera, "Kamera", NexusLucideIcons.Camera);
    }

    private void AddNav(NexusPhotosSection section, string label, string iconPath) =>
        NavItems.Add(new NexusPhotoNavItem { Section = section, Label = label, IconPath = iconPath });

    private static NexusPhotosSection ParseSection(object? parameter) => parameter switch
    {
        NexusPhotosSection section => section,
        NexusPhotoNavItem nav => nav.Section,
        string text when Enum.TryParse<NexusPhotosSection>(text, true, out var parsed) => parsed,
        _ => NexusPhotosSection.Library,
    };

    private void OnViewerStateChanged()
    {
        ViewerImageSource = LoadFullImage(_viewer.CurrentPhoto?.FullPath);
        OnPropertyChanged(nameof(IsViewerOpen));
        OnPropertyChanged(nameof(ViewerPhoto));
        OnPropertyChanged(nameof(ViewerImageSource));
        OnPropertyChanged(nameof(ViewerZoomMode));
        OnPropertyChanged(nameof(ViewerPositionLabel));
        OnPropertyChanged(nameof(ViewerZoomLabel));
        OnPropertyChanged(nameof(ViewerShowInfo));
        OnPropertyChanged(nameof(ViewerMetadata));
        System.Windows.Input.CommandManager.InvalidateRequerySuggested();
    }

    private void OnSlideshowStateChanged()
    {
        SlideshowImageSource = LoadFullImage(_slideshow.CurrentPhoto?.FullPath);
        OnPropertyChanged(nameof(IsSlideshowActive));
        OnPropertyChanged(nameof(SlideshowPhoto));
        OnPropertyChanged(nameof(SlideshowImageSource));
        OnPropertyChanged(nameof(SlideshowPositionLabel));
    }

    private static ImageSource? LoadFullImage(string? path)
    {
        if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
        {
            return null;
        }

        try
        {
            var image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.UriSource = new Uri(path, UriKind.Absolute);
            image.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
            image.EndInit();
            image.Freeze();
            return image;
        }
        catch
        {
            return null;
        }
    }

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