using Billing.Languages;
using Billing.Models;
using Billing.Themes;
using Billing.UI;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace Billing.Views
{
    public partial class CategoryPage : BillingPage
    {
        private static readonly BindableProperty CategoriesProperty = BindableProperty.Create(nameof(Categories), typeof(IList), typeof(CategoryPage));
        private static readonly BindableProperty IsTopCategoryProperty = BindableProperty.Create(nameof(IsTopCategory), typeof(bool), typeof(CategoryPage));

        public IList Categories
        {
            get => (IList)GetValue(CategoriesProperty);
            set => SetValue(CategoriesProperty, value);
        }
        public bool IsTopCategory => (bool)GetValue(IsTopCategoryProperty);

        public Command LongPressed { get; }
        public Command Tapped { get; }

        private readonly int parentId;
        private readonly Category parent;

        public CategoryPage(int pid = -1)
        {
            parentId = pid;
            parent = App.Categories.FirstOrDefault(c => c.Id == pid);
            Title = parent?.Name ?? Resource.CategoryManage;

            if (parent != null)
            {
                SetValue(IsTopCategoryProperty, false);
                Categories = App.Categories.Where(c => c.ParentId == pid).Select(c => WrapCategory(c)).ToList();
                ToolbarItems.Add(new ToolbarItem
                {
                    Order = ToolbarItemOrder.Primary,
                    IconImageSource = "plus.png",
                    Command = new Command(OnAddCategory)
                });
            }
            else
            {
                SetValue(IsTopCategoryProperty, true);
                Categories = new List<CategoryGrouping>
                {
                    new(Resource.Spending, App.Categories.Where(c => c.Type == CategoryType.Spending && c.ParentId == null).Select(c => WrapCategory(c))),
                    new(Resource.Income, App.Categories.Where(c => c.Type == CategoryType.Income && c.ParentId == null).Select(c => WrapCategory(c)))
                };
            }

            LongPressed = new Command(OnLongPressed);
            Tapped = new Command(OnTapped);

            InitializeComponent();
        }

        private UICategory WrapCategory(Category category)
        {
            return new UICategory(category)
            {
                Icon = category.Icon,
                Name = category.Name,
                IsTopCategory = IsTopCategory,
                TintColor = category.TintColor == Color.Transparent || category.TintColor == default ?
                    BaseTheme.CurrentPrimaryColor :
                    category.TintColor
            };
        }

        private async void OnAddCategory()
        {
            if (Tap.IsBusy)
            {
                return;
            }
            using (Tap.Start())
            {
                var page = new AddCategoryPage(parent: parent);
                page.CategoryChecked += OnCategoryChecked;
                await Navigation.PushAsync(page);
            }
        }

        private async void OnLongPressed(object o)
        {
            if (Tap.IsBusy)
            {
                return;
            }
            using (Tap.Start())
            {
                if (o is UICategory c)
                {
                    if (parentId < 0)
                    {
                        var page = new AddCategoryPage(c.Category.Id);
                        page.CategoryChecked += OnCategoryChecked;
                        await Navigation.PushAsync(page);
                    }
                    else
                    {
                        var result = await this.ShowConfirm(string.Format(Resource.ConfirmDeleteCategory, c.Category.Name));
                        if (result)
                        {
                            Categories.Remove(c);
                            groupLayout.Refresh(Categories);
                            App.Categories.Remove(c.Category);
                            _ = Task.Run(App.WriteCategories);
                        }
                    }
                }
            }
        }

        private async void OnTapped(object o)
        {
            if (Tap.IsBusy)
            {
                return;
            }
            using (Tap.Start())
            {
                if (o is UICategory c)
                {
                    if (parentId < 0)
                    {
                        var page = new CategoryPage(c.Category.Id);
                        await Navigation.PushAsync(page);
                    }
                    else
                    {
                        var page = new AddCategoryPage(c.Category.Id);
                        page.CategoryChecked += OnCategoryChecked;
                        await Navigation.PushAsync(page);
                    }
                }
            }
        }

        private void OnCategoryChecked(object sender, Category category)
        {
            if (category.Id < 0)
            {
                // add
                int maxId;
                if (App.Categories.Count > 0)
                {
                    maxId = App.Categories.Max(b => b.Id);
                }
                else
                {
                    maxId = -1;
                }
                category.Id = maxId + 1;
                App.Categories.Add(category);
                Categories.Add(WrapCategory(category));
            }
            else
            {
                foreach (var o in Categories)
                {
                    if (o is CategoryGrouping grouping)
                    {
                        var c = grouping.FirstOrDefault(c => c.Category == category);
                        if (c != null)
                        {
                            UpdateCategory(c);
                        }
                    }
                    else if (o is UICategory c && c.Category == category)
                    {
                        UpdateCategory(c);
                        break;
                    }
                }
            }
            groupLayout.Refresh(Categories);

            Task.Run(App.WriteCategories);
        }

        private void UpdateCategory(UICategory c)
        {
            c.Name = c.Category.Name;
            c.Icon = c.Category.Icon;
            c.TintColor = c.Category.TintColor == Color.Transparent || c.Category.TintColor == default ?
                BaseTheme.CurrentPrimaryColor :
                c.Category.TintColor;
        }
    }

    public class UICategory : BindableObject
    {
        public static readonly BindableProperty IsCheckedProperty = BindableProperty.Create(nameof(IsChecked), typeof(bool), typeof(UICategory));
        public static readonly BindableProperty IconProperty = BindableProperty.Create(nameof(Icon), typeof(string), typeof(UICategory));
        public static readonly BindableProperty NameProperty = BindableProperty.Create(nameof(Name), typeof(string), typeof(UICategory));
        public static readonly BindableProperty TintColorProperty = BindableProperty.Create(nameof(TintColor), typeof(Color), typeof(UICategory));
        public static readonly BindableProperty IsTopCategoryProperty = BindableProperty.Create(nameof(IsTopCategory), typeof(bool), typeof(UICategory));

        public bool IsChecked
        {
            get => (bool)GetValue(IsCheckedProperty);
            set => SetValue(IsCheckedProperty, value);
        }
        public string Icon
        {
            get => (string)GetValue(IconProperty);
            set => SetValue(IconProperty, value);
        }
        public string Name
        {
            get => (string)GetValue(NameProperty);
            set => SetValue(NameProperty, value);
        }
        public Color TintColor
        {
            get => (Color)GetValue(TintColorProperty);
            set => SetValue(TintColorProperty, value);
        }
        public bool IsTopCategory
        {
            get => (bool)GetValue(IsTopCategoryProperty);
            set => SetValue(IsTopCategoryProperty, value);
        }

        public Category Category { get; }

        public UICategory(Category category)
        {
            Category = category;
        }
    }

    public class CategoryGrouping : List<UICategory>, IGrouping<string, UICategory>
    {
        public string Key { get; }

        public CategoryGrouping(string key, IEnumerable<UICategory> categories) : base(categories)
        {
            Key = key;
        }
    }
}