using Billing.Models;
using Billing.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms;
using Resource = Billing.Languages.Resource;

namespace Billing.Views
{
    public partial class BillPage : BillingPage
    {
        private static readonly BindableProperty SelectedDateProperty = Helper.Create<DateTime, BillPage>(nameof(SelectedDate), propertyChanged: OnSelectedDateChanged);
        private static readonly BindableProperty BillsProperty = Helper.Create<List<UIBill>, BillPage>(nameof(Bills));
        private static readonly BindableProperty NoBillsProperty = Helper.Create<bool, BillPage>(nameof(NoBills), defaultValue: true);
        private static readonly BindableProperty IncomeProperty = Helper.Create<decimal, BillPage>(nameof(Income));
        private static readonly BindableProperty SpendingProperty = Helper.Create<decimal, BillPage>(nameof(Spending));
        private static readonly BindableProperty BalanceProperty = Helper.Create<decimal, BillPage>(nameof(Balance));

        private static void OnSelectedDateChanged(BillPage page, DateTime old, DateTime @new)
        {
            page.titleDatePicker.Unfocus();
        }

        public DateTime SelectedDate
        {
            get => (DateTime)GetValue(SelectedDateProperty);
            set => SetValue(SelectedDateProperty, value);
        }
        public List<UIBill> Bills
        {
            get => (List<UIBill>)GetValue(BillsProperty);
            set => SetValue(BillsProperty, value);
        }
        public bool NoBills => (bool)GetValue(NoBillsProperty);
        public decimal Income => (decimal)GetValue(IncomeProperty);
        public decimal Spending => (decimal)GetValue(SpendingProperty);
        public decimal Balance => (decimal)GetValue(BalanceProperty);

        public Command TitleDateTap { get; }
        public Command TitleLongPressed { get; }
        public Command EditBilling { get; }
        public Command DeleteBilling { get; }

        private bool initialized;

        public BillPage()
        {
            TitleDateTap = new Command(OnTitleDateTapped);
            TitleLongPressed = new Command(OnTitleDateLongPressed);
            EditBilling = new Command(OnEditBilling);
            DeleteBilling = new Command(OnDeleteBilling);

            InitializeComponent();
        }

        public override void OnLoaded()
        {
            if (!initialized)
            {
                initialized = true;
                billingDate.SetDateTime(DateTime.Today);
            }
        }

        private void OnDateSelected(object sender, DateEventArgs e)
        {
            SelectedDate = e.Date;

            Task.Run(() =>
            {
                var bills = App.Bills.Where(b => Helper.IsSameDay(b.CreateTime, e.Date));
                Bills = new List<UIBill>(bills.OrderBy(b => b.CreateTime).Select(b => WrapBill(b)));
                RefreshBalance(Bills);
                MainThread.BeginInvokeOnMainThread(async () => await scrollView.ScrollToAsync(0, 0, true));
            });
        }

        private void RefreshBalance(List<UIBill> bills)
        {
            SetValue(NoBillsProperty, bills.Count == 0);
            var income = bills.Where(b => b.Amount > 0).Sum(b => b.Amount);
            var spending = -bills.Where(b => b.Amount < 0).Sum(b => b.Amount);
            SetValue(IncomeProperty, income);
            SetValue(SpendingProperty, spending);
            SetValue(BalanceProperty, income - spending);
        }

        private UIBill WrapBill(Bill b)
        {
            return new UIBill(b)
            {
                Icon = App.Categories.FirstOrDefault(c => c.Id == b.CategoryId)?.Icon ?? BaseModel.ICON_DEFAULT,
                Name = b.Name,
                DateCreation = b.CreateTime,
                Amount = b.Amount,
                Wallet = App.Accounts.FirstOrDefault(a => a.Id == b.WalletId)?.Name
            };
        }

        private void UpdateBill(UIBill bill)
        {
            bill.Icon = App.Categories.FirstOrDefault(c => c.Id == bill.Bill.CategoryId)?.Icon ?? BaseModel.ICON_DEFAULT;
            bill.Name = bill.Bill.Name;
            bill.DateCreation = bill.Bill.CreateTime;
            bill.Amount = bill.Bill.Amount;
            bill.Wallet = App.Accounts.FirstOrDefault(a => a.Id == bill.Bill.WalletId)?.Name;
        }

        private void OnTitleDateTapped()
        {
            titleDatePicker.Focus();
        }

        private void TitlePicker_DateSelected(object sender, DateChangedEventArgs e)
        {
            if (e.NewDate.Year > 1900)
            {
                billingDate.SetDateTime(e.NewDate);
            }
        }

        private void OnTitleDateLongPressed()
        {
            billingDate.SetDateTime(DateTime.Today);
        }

        private async void OnEditBilling(object o)
        {
            if (Tap.IsBusy)
            {
                return;
            }
            using (Tap.Start())
            {
                AddBillPage page;
                if (o is UIBill bill)
                {
                    page = new AddBillPage(bill.Bill);
                }
                else
                {
                    if (App.Accounts.Count == 0)
                    {
                        await this.ShowMessage(Resource.NeedAccount);
                        await Shell.Current.GoToAsync("//Accounts");
                        return;
                    }
                    page = new AddBillPage(SelectedDate);
                }
                page.BillChecked += OnBillChecked;
                await Navigation.PushAsync(page);
            }
        }

        private async void OnDeleteBilling(object o)
        {
            if (Tap.IsBusy)
            {
                return;
            }
            using (Tap.Start())
            {
                if (o is UIBill bill)
                {
                    var result = await this.ShowConfirm(Resource.ConfirmDeleteBill);
                    if (result)
                    {
                        var bills = Bills;
                        bills.Remove(bill);
                        App.Bills.Remove(bill.Bill);
                        billsLayout.Refresh(bills);
                        RefreshBalance(bills);

                        _ = Task.Run(App.WriteBills);
                    }
                }
            }
        }

        private void OnBillChecked(object sender, Bill e)
        {
            if (e.Id < 0)
            {
                int maxId;
                if (App.Bills.Count > 0)
                {
                    maxId = App.Bills.Max(b => b.Id);
                }
                else
                {
                    maxId = -1;
                }
                e.Id = maxId + 1;
                App.Bills.Add(e);
                var bills = Bills;
                bills.Add(WrapBill(e));
                Bills = bills.OrderBy(b => b.DateCreation).ToList();
            }
            else
            {
                var bill = Bills.FirstOrDefault(b => b.Bill == e);
                if (bill != null)
                {
                    UpdateBill(bill);
                }
            }
            RefreshBalance(Bills);

            Task.Run(App.WriteBills);
        }
    }

    public class UIBill : BindableObject
    {
        public static readonly BindableProperty IconProperty = BindableProperty.Create(nameof(Icon), typeof(string), typeof(UIBill));
        public static readonly BindableProperty NameProperty = BindableProperty.Create(nameof(Name), typeof(string), typeof(UIBill));
        public static readonly BindableProperty DateCreationProperty = BindableProperty.Create(nameof(DateCreation), typeof(DateTime), typeof(UIBill));
        public static readonly BindableProperty AmountProperty = BindableProperty.Create(nameof(Amount), typeof(decimal), typeof(UIBill));
        public static readonly BindableProperty WalletProperty = BindableProperty.Create(nameof(Wallet), typeof(string), typeof(UIBill));

        public string Icon
        {
            get => (string)GetValue(IconProperty);
            set => SetValue(IconProperty, value);
        }
        public string Name
        {
            get => (string)GetValue(NameProperty);
            set => SetValue(NameProperty, value);
        }
        public DateTime DateCreation
        {
            get => (DateTime)GetValue(DateCreationProperty);
            set => SetValue(DateCreationProperty, value);
        }
        public decimal Amount
        {
            get => (decimal)GetValue(AmountProperty);
            set => SetValue(AmountProperty, value);
        }
        public string Wallet
        {
            get => (string)GetValue(WalletProperty);
            set => SetValue(WalletProperty, value);
        }

        public Bill Bill { get; }

        public UIBill(Bill bill)
        {
            Bill = bill;
        }
    }
}