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 RankingPage : IllustRankingDataCollectionPage
    {
        private static readonly string[] segmentDates = { "daily", "weekly", "monthly", "male" };

        public static readonly BindableProperty SegmentDateProperty = BindableProperty.Create(
            nameof(SegmentDate), typeof(int), typeof(RankingPage), propertyChanged: OnSegmentDatePropertyChanged);
        public static readonly BindableProperty SegmentTypeVisibleProperty = BindableProperty.Create(
            nameof(SegmentTypeVisible), typeof(bool), typeof(RankingPage));
        public static readonly BindableProperty SegmentTypeProperty = BindableProperty.Create(
            nameof(SegmentType), typeof(int), typeof(RankingPage), propertyChanged: OnSegmentTypePropertyChanged);
        public static readonly BindableProperty MaximumDateProperty = BindableProperty.Create(
            nameof(MaximumDate), typeof(DateTime), typeof(RankingPage), DateTime.Today);
        public static readonly BindableProperty SelectedDateProperty = BindableProperty.Create(
            nameof(SelectedDate), typeof(DateTime), typeof(RankingPage), DateTime.Today);

        private static void OnSegmentDatePropertyChanged(BindableObject obj, object old, object @new)
        {
            var page = (RankingPage)obj;
            var index = (int)@new;
            if (index == 2)
            {
                // monthly
                var typeIndex = page.SegmentType;
                if (typeIndex == 1)
                {
                    // r-18
                    page.SegmentType = 0;
                }
            }
        }
        private static void OnSegmentTypePropertyChanged(BindableObject obj, object old, object @new)
        {
            var page = (RankingPage)obj;
            var index = (int)@new;
            if (index == 1)
            {
                // r-18
                var dateIndex = page.SegmentDate;
                if (dateIndex == 2)
                {
                    // monthly
                    page.SegmentDate = 0;
                }
            }
        }

        public int SegmentDate
        {
            get => (int)GetValue(SegmentDateProperty);
            set => SetValue(SegmentDateProperty, value);
        }
        public bool SegmentTypeVisible
        {
            get => (bool)GetValue(SegmentTypeVisibleProperty);
            set => SetValue(SegmentTypeVisibleProperty, value);
        }
        public int SegmentType
        {
            get => (int)GetValue(SegmentTypeProperty);
            set => SetValue(SegmentTypeProperty, value);
        }
        public DateTime MaximumDate
        {
            get => (DateTime)GetValue(MaximumDateProperty);
            set => SetValue(MaximumDateProperty, value);
        }
        public DateTime SelectedDate
        {
            get => (DateTime)GetValue(SelectedDateProperty);
            set => SetValue(SelectedDateProperty, value);
        }
        public Command<string> ToolbarCommand { get; private set; }

        private bool previousEnabled;
        private bool dateEnabled;
        private bool nextEnabled;

        private bool isFilterVisible;
        private string lastQueryKey;
        private string queryDate;
        private string previousDate;
        private string nextDate;
        private int currentPage;
        private int nextPage;

        private string QueryKey => segmentDates[SegmentDate] + (SegmentType == 1 ? "_r18" : string.Empty);

        public RankingPage()
        {
            Resources.Add("cardView", GetCardViewTemplate());
            ToolbarCommand = new Command<string>(OnDateTrigger, OnCanDateTrigger);
            InitializeComponent();
            gridFilter.TranslationY = -100;
            panelFilter.TranslationY = -100;

            // preferences
            SegmentDate = Preferences.Get(Configs.QueryModeKey, 0);
            SegmentType = Preferences.Get(Configs.QueryTypeKey, 0);
            lastQueryKey = QueryKey;
            queryDate = Preferences.Get(Configs.QueryDateKey, null);
            currentPage = 1;
            datePicker.MinimumDate = new DateTime(2007, 9, 13);
            MaximumDate = DateTime.Today.AddDays(-1);
        }

        protected override ActivityIndicator LoadingIndicator => activityLoading;

        protected override void OnAppearing()
        {
            var r18 = Configs.IsOnR18;
            if (!r18)
            {
                SegmentType = 0;
                var query = QueryKey;
                if (lastQueryKey != query)
                {
                    ReleaseCollection();
                    lastQueryKey = query;
                }
            }
            SegmentTypeVisible = r18;

            base.OnAppearing();
        }

        protected override void OnDisappearing()
        {
            base.OnDisappearing();

            // saving state
            Preferences.Set(Configs.QueryModeKey, SegmentDate);
            Preferences.Set(Configs.QueryTypeKey, SegmentType);
            if (queryDate != null)
            {
                Preferences.Set(Configs.QueryDateKey, queryDate);
            }
        }

        protected override IEnumerable<IllustItem> DoGetIllustList(IllustRankingData data)
        {
            if (lastQueryKey != null && lastQueryKey.StartsWith(segmentDates[3]))
            {
                return data.contents.Where(i => i.illust_type == "0").Select(i => i.ConvertToItem());
            }
            return data.contents.Select(i => i.ConvertToItem());
        }

        protected override IllustRankingData DoLoadIllustData(bool force)
        {
            var data = Stores.LoadIllustRankingData(lastQueryKey, queryDate, currentPage, out lastError, force);
            if (data != null)
            {
                if (int.TryParse(data.next, out int next))
                {
                    nextPage = next;
                }
                var date = data.date;
                DateTime now;
                if (date.Length == 8 && int.TryParse(date, out _))
                {
                    queryDate = date;
                    now = new DateTime(
                        int.Parse(date.Substring(0, 4)),
                        int.Parse(date.Substring(4, 2)),
                        int.Parse(date.Substring(6, 2)));
                    SelectedDate = now;
                    //date = now.ToShortDateString();
                    date = now.ToString("yyyy-MM-dd");
                }
                else
                {
                    now = default;
                }
                date = ResourceHelper.GetResource(data.mode, date);
                MainThread.BeginInvokeOnMainThread(() => Title = date);

                var prev_date = data.prev_date;
                if (int.TryParse(prev_date, out _))
                {
                    previousDate = prev_date;
                    previousEnabled = true;
                }
                else
                {
                    previousDate = null;
                    previousEnabled = false;
                }
                var next_date = data.next_date;
                if (int.TryParse(next_date, out _))
                {
                    nextDate = next_date;
                    nextEnabled = true;
                }
                else
                {
                    nextDate = null;
                    nextEnabled = false;
                    if (now != default && force)
                    {
                        MaximumDate = now;
                    }
                }
            }
            dateEnabled = true;
            MainThread.BeginInvokeOnMainThread(ToolbarCommand.ChangeCanExecute);
            return data;
        }

        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, -100, easing: Easing.CubicIn),
                    gridFilter.FadeTo(0, easing: Easing.CubicIn),
                    panelFilter.TranslateTo(0, -100, easing: Easing.CubicIn),
                    panelFilter.FadeTo(0, easing: Easing.CubicIn)
                    );
            }
        }

        private void OnDateTrigger(string action)
        {
            if (IsLoading)
            {
                return;
            }
            if (action == "select")
            {
                datePicker.Focus();
            }
            else
            {
                var date = action == "prev" ? previousDate : nextDate;
                if (date == null)
                {
                    return;
                }
                if (isFilterVisible)
                {
                    ToggleFilterPanel(false);
                }
                queryDate = date;
                PrepareLoad();
            }
        }

        private bool OnCanDateTrigger(string action)
        {
            switch (action)
            {
                case "prev":
                    return previousEnabled;
                case "next":
                    return nextEnabled;
                default:
                    return dateEnabled;
            }
        }

        private void DatePicker_DateSelected(object sender, DateChangedEventArgs e)
        {
            if (e.OldDate == DateTime.Today || IsLoading)
            {
                // first load or loading
                return;
            }
            queryDate = e.NewDate.ToString("yyyyMMdd");
            PrepareLoad();
        }

        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 (nextPage == currentPage + 1)
            {
                currentPage = nextPage;
#if DEBUG
                App.DebugPrint($"loading page {nextPage}");
#endif
                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);
        }

        private void Filter_Clicked(object sender, EventArgs e)
        {
            var query = QueryKey;
            ToggleFilterPanel(false);

            if (IsLoading)
            {
                return;
            }
            //if (lastQueryKey != query)
            {
                // query changed.
                lastQueryKey = query;
#if DEBUG
                App.DebugPrint($"query changed: {query}");
#endif
                PrepareLoad();
            }
        }

        private async void PrepareLoad()
        {
            await ScrollToTopAsync(scrollView);
            // release
            currentPage = 1;
            previousEnabled = false;
            dateEnabled = false;
            nextEnabled = false;
            ToolbarCommand.ChangeCanExecute();
            StartLoad(true);
        }

        private void ReleaseCollection()
        {
            currentPage = 1;
            InvalidateCollection();
        }
    }
}
