using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Dollet.Core.Abstractions;
using Dollet.Core.Constants;
using Dollet.Core.Entities;
using Dollet.Helpers;
using Dollet.Pages;
using Dollet.Pages.Accounts;
using Dollet.ViewModels.Dtos;

namespace Dollet.ViewModels.Accounts
{
    public partial class AccountsPageViewModel(IUnitOfWork unitOfWork) : ObservableObject
    {
        private readonly IUnitOfWork _unitOfWork = unitOfWork;

        private decimal accountsBalance = 0.00m;
        private bool hiddenAccountsExpanded = false;
        private string selectedCurrency = "PLN", hiddenAccountsIcon = MaterialDesignIcons.Arrow_left;
        private Currency _defaultCurrency;
        private Dictionary<string, decimal> _currencyValues;

        public decimal AccountsBalance 
        {
            get => accountsBalance; 
            set => SetProperty(ref accountsBalance, value); 
        }
        public string SelectedCurrency 
        {
            get => selectedCurrency; 
            set => SetProperty(ref selectedCurrency, value); 
        }
        public bool HiddenAccountsExpanded
        {
            get => hiddenAccountsExpanded;
            set
            {
                if (value)
                    HiddenAccountsIcon = MaterialDesignIcons.Arrow_drop_down;
                else
                    HiddenAccountsIcon = MaterialDesignIcons.Arrow_left;

                SetProperty(ref hiddenAccountsExpanded, value);
            }
        }

        public string HiddenAccountsIcon
        {
            get { return hiddenAccountsIcon; }
            set { SetProperty(ref hiddenAccountsIcon, value); }
        }

        public ObservableRangeCollection<AccountDto> Accounts { get; } = [];
        public ObservableRangeCollection<AccountDto> HiddenAccounts { get; } = [];
        public ObservableRangeCollection<string> Currencies { get; } = [];

        [RelayCommand]
        async Task Appearing()
        {
            Accounts.Clear();

            _defaultCurrency = await _unitOfWork.CurrencyRepository.GetDefaultAsync();
            _currencyValues = await _unitOfWork.CurrencyRepository.GetCurrencyValuesAsync(_defaultCurrency.Code);

            var accounts = await _unitOfWork.AccountRepository.GetAllAsync();

            foreach (var item in accounts.GroupBy(r => r.IsHidden))
            {
                if (item.Key)
                {
                    HiddenAccounts.ReplaceRange(MapToDtos(item));
                    continue;
                }
                Accounts.ReplaceRange(MapToDtos(item));
            }

            var currencies = _currencyValues.Select(f => f.Key);
            Currencies.ReplaceRange(currencies);

            CurrencyChanged();
        }

        private static IEnumerable<AccountDto> MapToDtos(IEnumerable<Account> accounts)
        {
            return accounts
                .Select(x => new AccountDto
                {
                    Id = x.Id,
                    Amount = x.Amount,
                    Name = x.Name,
                    Description = x.Description,
                    Icon = x.Icon,
                    Color = x.Color,
                    Currency = x.Currency,
                    IsHidden = x.IsHidden,
                    IsDefault = x.IsDefault
                })
                .ToList();
        }

        [RelayCommand]
        void CurrencyChanged()
        {
            AccountsBalance = 0;

            foreach (var account in Accounts.Where(x => !x.IsHidden))
            {
                _currencyValues.TryGetValue(account.Currency, out decimal value);

                if (value == 0)
                    continue;

                var balance = account.Amount / value;
                AccountsBalance += balance;
            }
            SelectedCurrency = _defaultCurrency.Code;
        }

        [RelayCommand]
        void ToogleHiddenAccounts() => HiddenAccountsExpanded = !HiddenAccountsExpanded;

        [RelayCommand]
        async Task NavigateToAddAccountPage()
        {
            await Shell.Current.GoToAsync(nameof(AddAccountPage));
        }

        [RelayCommand]
        async Task NavigateToEditAccountPage(AccountDto account)
        {
            await Shell.Current.GoToAsync(nameof(EditAccountPage), true, new Dictionary<string, object>
            {
                {"Account", account }
            });
        }
    }
}
