using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Pixiview.Resources;
using Pixiview.UI;
using Pixiview.Utils;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Pixiview.Illust
{
    public partial class FavoritesPage : FavoriteIllustCollectionPage
    {
        private const int STEP = 20;

        public static readonly BindableProperty SegmentTypeProperty = BindableProperty.Create(
            nameof(SegmentType), typeof(int), typeof(FavoritesPage), propertyChanged: OnSegmentTypePropertyChanged);

        private static void OnSegmentTypePropertyChanged(BindableObject obj, object old, object @new)
        {
            var page = (FavoritesPage)obj;
            MainThread.BeginInvokeOnMainThread(page.ChangeFilter);
        }

        public int SegmentType
        {
            get => (int)GetValue(SegmentTypeProperty);
            set => SetValue(SegmentTypeProperty, value);
        }

        private bool isFilterVisible;
        private int startIndex;
        private int nextIndex;
        private bool flag = false;

        public FavoritesPage()
        {
            Resources.Add("cardView", GetCardViewTemplate());
            InitializeComponent();
            gridFilter.TranslationY = -60;
            panelFilter.TranslationY = -60;

            startIndex = -1;
            nextIndex = 0;
        }

        protected override bool IsFavoriteVisible => false;
        protected override ActivityIndicator LoadingIndicator => activityLoading;

        protected override void OnAppearing()
        {
            if (lastUpdated != LastUpdated)
            {
                startIndex = -1;
                StartLoad();
            }
            else
            {
                var favorites = Stores.Favorites;
                if (favorites.Changed)
                {
                    lastUpdated = default;
                    startIndex = -1;
                    StartLoad();
                }
            }
        }

        protected override IEnumerable<IllustItem> DoGetIllustList(IllustItem[] data)
        {
            return data;
        }

        protected override IllustItem[] DoLoadIllustData(bool force)
        {
            IEnumerable<IllustItem> favs;
            if (startIndex < 0)
            {
                var favorites = Stores.GetFavoriteObject(flag);
                flag = false;
                if (favorites == null)
                {
                    return null;
                }
                favs = favorites.Illusts.Reload();
                startIndex = 0;
            }
            else
            {
                favs = Stores.Favorites;
            }
            switch (SegmentType)
            {
                case 1: // animation
                    favs = favs.Where(f => f.IllustType == IllustType.Anime);
                    break;
                case 2: // online
                    favs = favs.Where(f => f.BookmarkId != null);
                    break;
            }
            var illusts = favs.Skip(startIndex).Take(STEP).ToArray();
            nextIndex = startIndex + STEP;
            if (illusts.Length == 0 || nextIndex >= Stores.Favorites.Count)
            {
                // reach the bottom
                startIndex = nextIndex;
            }
            return illusts;
        }

        private async void ToggleFilterPanel(bool flag)
        {
            ViewExtensions.CancelAnimations(gridFilter);
            ViewExtensions.CancelAnimations(panelFilter);
            if (flag)
            {
                isFilterVisible = true;
                if (scrollDirection == ScrollDirection.Down)
                {
                    // stop the scrolling
                    await scrollView.ScrollToAsync(scrollView.ScrollX, scrollView.ScrollY, false);
                }
                await Task.WhenAll(
                    labelCaret.RotateTo(180, easing: Easing.CubicOut),
                    gridFilter.TranslateTo(0, 0, easing: Easing.CubicOut),
                    gridFilter.FadeTo(1, easing: Easing.CubicOut),
                    panelFilter.TranslateTo(0, 0, easing: Easing.CubicOut),
                    panelFilter.FadeTo(1, easing: Easing.CubicOut)
                    );
            }
            else
            {
                isFilterVisible = false;
                await Task.WhenAll(
                    labelCaret.RotateTo(0, easing: Easing.CubicIn),
                    gridFilter.TranslateTo(0, -60, easing: Easing.CubicIn),
                    gridFilter.FadeTo(0, easing: Easing.CubicIn),
                    panelFilter.TranslateTo(0, -60, easing: Easing.CubicIn),
                    panelFilter.FadeTo(0, easing: Easing.CubicIn)
                    );
            }
        }

        private async void ChangeFilter()
        {
            ToggleFilterPanel(false);
            await ScrollToTopAsync(scrollView);
            lastUpdated = default;
            startIndex = 0;
            StartLoad();
        }

        private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
        {
            ToggleFilterPanel(!isFilterVisible);
        }

        private void FlowLayout_MaxHeightChanged(object sender, HeightEventArgs e)
        {
            SetOffset(e.ContentHeight - scrollView.Bounds.Height - SCROLL_OFFSET);
        }

        protected override bool CheckRefresh()
        {
            if (nextIndex > startIndex)
            {
                startIndex = nextIndex;
                return true;
            }
            return false;
        }

        private void ScrollView_Scrolled(object sender, ScrolledEventArgs e)
        {
            var y = e.ScrollY;
            if (IsScrollingDown(y))
            {
                // down
                if (isFilterVisible)
                {
                    ToggleFilterPanel(false);
                }
            }
            OnScrolled(y);
        }

        public void Reload(bool force = false)
        {
            if (force)
            {
                flag = true;
            }
            lastUpdated = default;
            startIndex = -1;
            StartLoad(force);
        }

        private async void Refresh_Clicked(object sender, EventArgs e)
        {
            if (IsLoading)
            {
                return;
            }
            await ScrollToTopAsync(scrollView);
            IsLoading = true;

            var offset = 16 - IndicatorMarginTop;
            activityLoading.Margin = new Thickness(0, loadingOffset - offset, 0, offset);
            activityLoading.Animate("margin", top =>
            {
                activityLoading.Margin = new Thickness(0, top, 0, offset);
            },
            loadingOffset - offset, 16 - offset, easing: Easing.CubicOut, finished: (v, r) =>
            {
                Task.Run(() =>
                {
                    var list = Stores.LoadOnlineFavorites();
                    if (list != null && list.Length > 0)
                    {
                        MainThread.BeginInvokeOnMainThread(() => ConfirmNext(list));
                    }
                    else
                    {
                        flag = false;
                        lastUpdated = default;
                        startIndex = -1;
                        MainThread.BeginInvokeOnMainThread(() => StartLoad(true));
                    }
                });
            });
        }

        private void CloseLoading(Action next = null)
        {
            var offset = 16 - IndicatorMarginTop;
            activityLoading.Animate("margin", top =>
            {
                activityLoading.Margin = new Thickness(0, top, 0, offset);
            },
            16 - offset, loadingOffset - offset, easing: Easing.CubicIn, finished: (v, r) =>
            {
                IsLoading = false;

                next?.Invoke();
            });
        }

        private async void ConfirmNext(IllustItem[] list)
        {
            var cancel = ResourceHelper.Cancel;
            var combine = ResourceHelper.FavoritesCombine;
            var replace = ResourceHelper.FavoritesReplace;
            var result = await DisplayActionSheet(
                ResourceHelper.FavoritesOperation,
                cancel,
                combine,
                replace);

            if (result == cancel)
            {
                return;
            }
            if (result == replace)
            {
                Stores.GetFavoriteObject().Illusts = new FavoriteList(list);
            }
            else if (result == combine)
            {
                // combine
                var nows = Stores.GetFavoriteObject().Illusts;
                // sync bookmarks from remote
                for (var i = nows.Count - 1; i >= 0; i--)
                {
                    var b = nows[i];
                    var bookmarkId = b.BookmarkId;
                    var bookmark = list.FirstOrDefault(f => f.Id == b.Id);
                    if (bookmark == null)
                    {
                        if (bookmarkId != null)
                        {
                            // not exists in remote any more
#if LOG
                            App.DebugPrint($"remove bookmark ({bookmarkId}) - {b.Id}: {b.Title}");
#endif
                            nows.RemoveAt(i);
                        }
                    }
                    else if (bookmarkId != bookmark.BookmarkId)
                    {
                        // update bookmark id
#if LOG
                        App.DebugPrint($"change bookmark ({bookmarkId}) to ({bookmark.BookmarkId}) - {b.Id}: {b.Title}");
#endif
                        b.BookmarkId = bookmark.BookmarkId;
                    }
                }
                // add bookmarks that exists in remote only
                list = list.Where(f => !nows.Any(i => i.Id == f.Id)).ToArray();

                if (list.Length > 0)
                {
                    for (var i = 0; i < list.Length; i++)
                    {
                        var item = list[i];
#if LOG
                        App.DebugPrint($"add bookmark ({item.BookmarkId}) - {item.Id}: {item.Title}");
#endif
                        item.Image = StyleDefinition.DownloadBackground;
                    }
                    nows.InsertRange(0, list);
                }
            }
            else
            {
                return;
            }

            ParallelTask.Start("favorite.loadimages", 0, list.Length, Configs.MaxPageThreads, i =>
            {
                var illustItem = list[i];
                var data = Stores.LoadIllustPreloadData(illustItem.Id);
                if (data != null && data.illust.TryGetValue(illustItem.Id, out var illust))
                {
                    illust.CopyToItem(illustItem);
                    if (data.user.TryGetValue(illust.userId, out var user))
                    {
                        illustItem.ProfileUrl = user.image;
                    }
                    return true;
                }
                App.DebugError("load.favorite", $"cannot fetch preload data, {illustItem.Id}, disposed.");
                return false;
            }, () =>
            {
                Stores.SaveFavoritesIllusts();

                MainThread.BeginInvokeOnMainThread(() =>
                {
                    CloseLoading(() =>
                    {
                        flag = false;
                        lastUpdated = default;
                        startIndex = -1;
                        StartLoad();
                    });
                });
            });
        }

        private async void ShareFavorites_Clicked(object sender, EventArgs e)
        {
            var file = Stores.FavoritesPath;
            await Share.RequestAsync(new ShareFileRequest
            {
                Title = ResourceHelper.Favorites,
                File = new ShareFile(file)
            });
        }
    }
}
