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

namespace Billing.Views
{
    public partial class AccountPage : BillingPage
    {
        private static readonly BindableProperty BalanceProperty = BindableProperty.Create(nameof(Balance), typeof(decimal), typeof(AccountPage));
        private static readonly BindableProperty AssetProperty = BindableProperty.Create(nameof(Asset), typeof(decimal), typeof(AccountPage));
        private static readonly BindableProperty LiabilityProperty = BindableProperty.Create(nameof(Liability), typeof(decimal), typeof(AccountPage));
        private static readonly BindableProperty AccountsProperty = BindableProperty.Create(nameof(Accounts), typeof(List<AccountGrouping>), typeof(AccountPage));

        public decimal Balance => (decimal)GetValue(BalanceProperty);
        public decimal Asset => (decimal)GetValue(AssetProperty);
        public decimal Liability => (decimal)GetValue(LiabilityProperty);
        public List<AccountGrouping> Accounts => (List<AccountGrouping>)GetValue(AccountsProperty);

        public Command EditAccount { get; }
        public Command DeleteAccount { get; }

        private readonly List<AccountGrouping> accounts = new();
        private bool initialized;

        public AccountPage()
        {
            EditAccount = new Command(OnEditAccount);
            DeleteAccount = new Command(OnDeleteAccount);
            SetValue(AccountsProperty, accounts);

            InitializeComponent();
        }

        protected override void OnAppearing()
        {
            if (!initialized || accounts.Count == 0)
            {
                initialized = true;
                accounts.Clear();
                foreach (var account in App.Accounts)
                {
                    AddToAccountGroup(account);
                }
            }
            RefreshBalance(true);
            groupLayout.Refresh(accounts);
        }

        private void RefreshBalance(bool calc = false)
        {
            if (calc)
            {
                foreach (var account in App.Accounts)
                {
                    account.Balance = account.Initial + App.Bills.Where(b => b.WalletId == account.Id).Sum(b => b.Amount);
                }
            }
            SetValue(BalanceProperty, App.Accounts.Sum(a => a.Balance));
        }

        private void AddToAccountGroup(Account account)
        {
            int maxId;
            if (accounts.Count > 0)
            {
                maxId = accounts.Max(g => g.Max(a => a.Id));
            }
            else
            {
                maxId = -1;
            }
            account.Id = maxId + 1;

            var group = accounts.FirstOrDefault(g => g.Key == account.Category);
            if (group == null)
            {
                group = new AccountGrouping(account.Category, account.Balance) { account };
                accounts.Add(group);
            }
            else
            {
                group.Add(account);
                group.Balance += account.Balance;
            }
        }

        private async void OnEditAccount(object o)
        {
            if (Tap.IsBusy)
            {
                return;
            }
            using (Tap.Start())
            {
                var page = new AddAccountPage(o as Account);
                page.AccountChecked += AccountChecked;
                await Navigation.PushAsync(page);
            }
        }

        private async void OnDeleteAccount(object o)
        {
            if (Tap.IsBusy)
            {
                return;
            }
            using (Tap.Start())
            {
                if (o is Account account)
                {
                    var result = await this.ShowConfirm(Resource.ConfirmDeleteAccount);
                    if (result)
                    {
                        App.Accounts.Remove(account);
                        var group = accounts.FirstOrDefault(a => a.Key == account.Category);
                        if (group == null)
                        {
                            Helper.Error("account.delete", "unexpected deleting account, cannot find the current category");
                            return;
                        }
                        group.Remove(account);
                        if (group.Count == 0)
                        {
                            accounts.Remove(group);
                        }
                        RefreshBalance();
                        groupLayout.Refresh(accounts);

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

        private void AccountChecked(object sender, AccountEventArgs e)
        {
            var add = e.Account.Id < 0;
            if (add)
            {
                App.Accounts.Add(e.Account);
                AddToAccountGroup(e.Account);
            }
            RefreshBalance(!add);
            groupLayout.Refresh(accounts);

            Task.Run(App.WriteAccounts);
        }
    }

    public class AccountGrouping : List<Account>, IGrouping<AccountCategory, Account>
    {
        public AccountCategory Key { get; }
        public decimal Balance { get; set; }

        public AccountGrouping(AccountCategory key, decimal balance) : base()
        {
            Key = key;
            Balance = balance;
        }
    }
}