using CommunityToolkit.WinUI;
using Harmonia.Core.Imaging;
using Harmonia.Core.Playlists;
using Harmonia.WinUI.ViewModels;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.UI.Popups;

namespace Harmonia.WinUI.Views;

public sealed partial class PlaylistView : UserControl
{
    private readonly PlaylistViewModel _viewModel;

    public PlaylistView()
    {
        InitializeComponent();

        _viewModel = (PlaylistViewModel)DataContext;
        _viewModel.PropertyChanging += OnViewModelPropertyChanging;
        _viewModel.PropertyChanged += OnViewModelPropertyChanged;
        _viewModel.PlayingSongChangedAutomatically += OnPlayingSongChangedAutomatically;

        foreach (MenuFlyoutItemBase item in PlaylistListViewMenuFlyout.Items)
        {
            item.DataContextChanged += Item_DataContextChanged;
        }
    }

    private void OnViewModelPropertyChanging(object? sender, PropertyChangingEventArgs e)
    {
        if (e.PropertyName == nameof(_viewModel.PlayingSong))
        {
            ListViewItem listViewItem = (ListViewItem)PlaylistListView.ContainerFromItem(_viewModel.PlayingSong);

            if (listViewItem != null)
            {
                UpdateListViewItemStyle(listViewItem, false);
            }
        }
    }

    private void OnViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(_viewModel.PlayingSong))
        {
            ListViewItem listViewItem = (ListViewItem)PlaylistListView.ContainerFromItem(_viewModel.PlayingSong);

            UpdateListViewItemStyle(listViewItem, true);
        }
    }

    private void UpdateListViewItemStyle(FrameworkElement listViewItem, bool isPlaying)
    {
        if (listViewItem == null)
            return;

        FrameworkElement? frameworkElement;

        if (listViewItem.Name == "PlaylistSongListViewItem")
        {
            frameworkElement = listViewItem;
        }
        else
        {
            if (listViewItem.FindDescendant("PlaylistSongListViewItem") is not FrameworkElement frameworkElement2)
                return;

            frameworkElement = frameworkElement2;
        }

        if (frameworkElement == null)
            return;

        string songItemTitleBrushName = isPlaying ? "PlayingSongTitleTextBlock" : "SongTitleTextBlock";
        string songItemSubtitleBrushName = isPlaying ? "PlayingSongSubtitleTextBlock" : "SongSubtitleTextBlock";

        UpdateElementStyle(frameworkElement, "SongTitleTextBlock", songItemTitleBrushName);
        UpdateElementStyle(frameworkElement, "SongArtistsTextBlock", songItemSubtitleBrushName);
        UpdateElementStyle(frameworkElement, "SongAlbumTextBlock", songItemSubtitleBrushName);
    }

    private void UpdateElementStyle(FrameworkElement dependencyObject, string elementName, string resourceName)
    {
        if (dependencyObject.FindDescendant(elementName) is not FrameworkElement frameworkElement)
            return;

        Resources.TryGetValue(resourceName, out object? resource);

        if (resource == null)
            Application.Current.Resources.TryGetValue(resourceName, out resource);

        if (resource is not Style style)
            return;

        frameworkElement.Style = style;
    }

    private void OnPlayingSongChangedAutomatically(object? sender, EventArgs e)
    {
        BringPlayingSongIntoView();
    }

    private void BringPlayingSongIntoView()
    {
        PlaylistSong? playingSong = _viewModel.PlayingSong;

        if (playingSong == null)
            return;

        ListViewItem listViewItem = (ListViewItem)PlaylistListView.ContainerFromItem(playingSong);

        if (listViewItem != null)
        {
            listViewItem.UpdateLayout();
            listViewItem.StartBringIntoView(new BringIntoViewOptions() { VerticalAlignmentRatio = .5, AnimationDesired = true });
        }
        else
        {
            PlaylistListView.UpdateLayout();
            PlaylistListView.ScrollIntoView(playingSong);
        }
    }

    private void Item_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
    {
        if (sender is not MenuFlyoutItemBase item)
            return;

        if (args.NewValue == _viewModel)
            return;

        item.DataContext = _viewModel;
    }

    private void Image_Loaded(object sender, RoutedEventArgs e)
    {
        if (sender is not Image image)
            return;

        image.DataContextChanged += Image_DataContextChanged;

        if (image.DataContext is not PlaylistSong playlistSong)
            return;

        //Task.Run(async () => await UpdateImage(image, playlistSong));
        UpdateImage(image, playlistSong);
    }

    private void Image_Unloaded(object sender, RoutedEventArgs e)
    {
        if (sender is not Image image)
            return;

        image.DataContextChanged -= Image_DataContextChanged;
    }

    private void Image_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
    {
        if (sender is not Image image)
            return;

        if (args.NewValue is not PlaylistSong playlistSong)
            return;

        //Task.Run(async () => await UpdateImage(image, playlistSong));
        UpdateImage(image, playlistSong);
    }

    private void UpdateImage(Image image, PlaylistSong playlistSong)
    {
        int hashCode = image.GetHashCode();
        //BitmapImage? bitmapImage = await _viewModel.GetBitmapImageAsync(hashCode, playlistSong);
        //SongPictureInfo? songPictureInfo = await _viewModel.GetSongPictureInfoAsync(hashCode, playlistSong);

        DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
        {
            BitmapImage? bitmapImage = await _viewModel.GetBitmapImageAsync(hashCode, playlistSong);
            image.Source = bitmapImage;
        });
    }

    private void PlaylistSongListViewItem_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
    {
        if (args.NewValue is not PlaylistSong playlistSong)
            return;

        bool isPlaying = playlistSong == _viewModel.PlayingSong;
        
        UpdateListViewItemStyle(sender, isPlaying);
    }

    private async void PlaylistListViewItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
    {
        if (sender is not FrameworkElement element)
            return;

        if (element == null || element.DataContext is not PlaylistSong playlistSong)
            return;

        await _viewModel.PlaySongAsync(playlistSong);
    }

    private void PlaylistListViewItem_RightTapped(object sender, RightTappedRoutedEventArgs e)
    {
        if (sender is not FrameworkElement element)
            return;

        if (element == null || element.DataContext is not PlaylistSong playlistSong)
            return;

        if (PlaylistListView.SelectedItems.Contains(playlistSong))
            return;

        int index = PlaylistListView.Items.IndexOf(playlistSong);

        PlaylistListView.DeselectAll();
        PlaylistListView.SelectRange(new ItemIndexRange(index, 1));
    }

    private void PlaylistListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (sender is not ListView listView)
            return;

        PlaylistSong[] selectedPlaylistSongs = [.. listView.SelectedItems.Cast<PlaylistSong>()];

        _viewModel.SelectedPlaylistSongs = [.. selectedPlaylistSongs];
    }

    private void MenuFlyout_Opening(object sender, object e)
    {
        if (sender is not MenuFlyout menuFlyout)
            return;

        foreach (MenuFlyoutItemBase item in menuFlyout.Items)
        {
            if (item.DataContext != _viewModel)
            {
                item.DataContext = _viewModel;
            }
        }
    }

    private void PlaylistListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
    {
        _viewModel.IsUserUpdating = true;
    }

    private void PlaylistListView_DragItemsCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
    {
        _viewModel.IsUserUpdating = false;
    }
}