using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;

namespace XLAB
{
    internal sealed class PrsnDirectoryWindowViewModel : ObservableObject
    {
        private readonly IPrsnDirectoryDialogService _dialogService;
        private readonly PrsnDirectoryService _service;
        private bool _isBusy;
        private List<PrsnDirectoryItem> _prsnCache;
        private string _searchText;
        private PrdspvDirectoryItem _selectedPrdspv;
        private PrfrDirectoryItem _selectedPrfr;
        private PrfrvdDirectoryItem _selectedPrfrvd;
        private PrsnDirectoryItem _selectedPrsn;
        private string _statusText;

        public PrsnDirectoryWindowViewModel(PrsnDirectoryService service, IPrsnDirectoryDialogService dialogService)
        {
            _service = service;
            _dialogService = dialogService;
            _prsnCache = new List<PrsnDirectoryItem>();

            PrsnItems = new ObservableCollection<PrsnDirectoryItem>();
            PrfrItems = new ObservableCollection<PrfrDirectoryItem>();
            PrfrvdItems = new ObservableCollection<PrfrvdDirectoryItem>();
            PrdspvItems = new ObservableCollection<PrdspvDirectoryItem>();

            AddPrsnCommand = new RelayCommand(delegate { AddPrsnAsync(); }, delegate { return !IsBusy; });
            EditPrsnCommand = new RelayCommand(delegate { EditPrsnAsync(); }, delegate { return !IsBusy && SelectedPrsn != null; });
            DeletePrsnCommand = new RelayCommand(delegate { DeletePrsnAsync(); }, delegate { return !IsBusy && SelectedPrsn != null; });
            AddPrfrCommand = new RelayCommand(delegate { AddPrfrAsync(); }, delegate { return !IsBusy && SelectedPrsn != null; });
            EditPrfrCommand = new RelayCommand(delegate { EditPrfrAsync(); }, delegate { return !IsBusy && SelectedPrfr != null; });
            DeletePrfrCommand = new RelayCommand(delegate { DeletePrfrAsync(); }, delegate { return !IsBusy && SelectedPrfr != null; });
            AddPrfrvdCommand = new RelayCommand(delegate { AddPrfrvdAsync(); }, delegate { return !IsBusy && SelectedPrfr != null; });
            EditPrfrvdCommand = new RelayCommand(delegate { EditPrfrvdAsync(); }, delegate { return !IsBusy && SelectedPrfrvd != null; });
            DeletePrfrvdCommand = new RelayCommand(delegate { DeletePrfrvdAsync(); }, delegate { return !IsBusy && SelectedPrfrvd != null; });
            AddPrdspvCommand = new RelayCommand(delegate { AddPrdspvAsync(); }, delegate { return !IsBusy && SelectedPrfr != null; });
            EditPrdspvCommand = new RelayCommand(delegate { EditPrdspvAsync(); }, delegate { return !IsBusy && SelectedPrdspv != null; });
            DeletePrdspvCommand = new RelayCommand(delegate { DeletePrdspvAsync(); }, delegate { return !IsBusy && SelectedPrdspv != null; });
            RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });

            UpdateStatus();
        }

        public ICommand AddPrdspvCommand { get; private set; }
        public ICommand AddPrfrCommand { get; private set; }
        public ICommand AddPrfrvdCommand { get; private set; }
        public ICommand AddPrsnCommand { get; private set; }
        public ICommand DeletePrdspvCommand { get; private set; }
        public ICommand DeletePrfrCommand { get; private set; }
        public ICommand DeletePrfrvdCommand { get; private set; }
        public ICommand DeletePrsnCommand { get; private set; }
        public ICommand EditPrdspvCommand { get; private set; }
        public ICommand EditPrfrCommand { get; private set; }
        public ICommand EditPrfrvdCommand { get; private set; }
        public ICommand EditPrsnCommand { get; private set; }
        public ObservableCollection<PrdspvDirectoryItem> PrdspvItems { get; private set; }
        public ObservableCollection<PrfrDirectoryItem> PrfrItems { get; private set; }
        public ObservableCollection<PrfrvdDirectoryItem> PrfrvdItems { get; private set; }
        public ObservableCollection<PrsnDirectoryItem> PrsnItems { get; private set; }
        public ICommand RefreshCommand { get; private set; }

        public bool IsBusy
        {
            get { return _isBusy; }
            private set
            {
                if (SetProperty(ref _isBusy, value))
                {
                    RaiseCommandStates();
                }
            }
        }

        public string SearchText
        {
            get { return _searchText; }
            set
            {
                if (SetProperty(ref _searchText, value))
                {
                    ApplySearchFilter();
                    UpdateStatus();
                }
            }
        }

        public PrdspvDirectoryItem SelectedPrdspv
        {
            get { return _selectedPrdspv; }
            set
            {
                if (SetProperty(ref _selectedPrdspv, value))
                {
                    RaiseCommandStates();
                    UpdateStatus();
                }
            }
        }

        public PrfrDirectoryItem SelectedPrfr
        {
            get { return _selectedPrfr; }
            set
            {
                if (SetProperty(ref _selectedPrfr, value))
                {
                    RaiseCommandStates();
                    LoadLeafTablesForSelection();
                    UpdateStatus();
                }
            }
        }

        public PrfrvdDirectoryItem SelectedPrfrvd
        {
            get { return _selectedPrfrvd; }
            set
            {
                if (SetProperty(ref _selectedPrfrvd, value))
                {
                    RaiseCommandStates();
                    UpdateStatus();
                }
            }
        }

        public PrsnDirectoryItem SelectedPrsn
        {
            get { return _selectedPrsn; }
            set
            {
                if (SetProperty(ref _selectedPrsn, value))
                {
                    RaiseCommandStates();
                    LoadPrfrForSelection();
                    UpdateStatus();
                }
            }
        }

        public string StatusText
        {
            get { return _statusText; }
            private set { SetProperty(ref _statusText, value); }
        }

        public async Task InitializeAsync()
        {
            await ExecuteBusyOperationAsync(async delegate { await RefreshPrsnCoreAsync(null, null, null, null); });
        }

        private void AddPrdspvAsync()
        {
            if (SelectedPrfr == null)
            {
                return;
            }

            var result = _dialogService.ShowPrdspvEditDialog(new PrdspvDirectoryItem { EmploymentId = SelectedPrfr.EmploymentId }, true, PrdspvItems.ToList(), _service);
            if (result == null)
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                var createdId = await Task.Run(delegate { return _service.AddPrdspvItem(result); });
                await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, result.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, createdId);
                _dialogService.ShowInfo("Запись PRDSPV добавлена.");
            });
        }

        private void AddPrfrAsync()
        {
            if (SelectedPrsn == null)
            {
                return;
            }

            var result = _dialogService.ShowPrfrEditDialog(new PrfrDirectoryItem { PersonId = SelectedPrsn.Id }, true, PrfrItems.ToList(), _service);
            if (result == null)
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                var createdId = await Task.Run(delegate { return _service.AddPrfrItem(result); });
                await RefreshPrsnCoreAsync(result.PersonId, createdId, null, null);
                _dialogService.ShowInfo("Запись PRFR добавлена.");
            });
        }

        private void AddPrfrvdAsync()
        {
            if (SelectedPrfr == null)
            {
                return;
            }

            var result = _dialogService.ShowPrfrvdEditDialog(new PrfrvdDirectoryItem { EmploymentId = SelectedPrfr.EmploymentId }, true, PrfrvdItems.ToList(), _service);
            if (result == null)
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                var createdId = await Task.Run(delegate { return _service.AddPrfrvdItem(result); });
                await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, result.EmploymentId, createdId, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
                _dialogService.ShowInfo("Запись PRFRVD добавлена.");
            });
        }

        private void AddPrsnAsync()
        {
            var result = _dialogService.ShowPrsnEditDialog(new PrsnDirectoryItem(), true, _prsnCache.ToList());
            if (result == null)
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                var createdId = await Task.Run(delegate { return _service.AddPrsnItem(result); });
                await RefreshPrsnCoreAsync(createdId, null, null, null);
                _dialogService.ShowInfo("Запись PRSN добавлена.");
            });
        }

        private void ApplyPrsnFilter(int? preferredId)
        {
            var filteredItems = _prsnCache.Where(delegate(PrsnDirectoryItem item) { return MatchesSearch(item); }).ToList();
            PrsnItems.Clear();
            foreach (var item in filteredItems)
            {
                PrsnItems.Add(item);
            }

            SelectedPrsn = preferredId.HasValue
                ? PrsnItems.FirstOrDefault(delegate(PrsnDirectoryItem item) { return item.Id == preferredId.Value; })
                : PrsnItems.FirstOrDefault();
        }

        private void ApplySearchFilter()
        {
            if (!IsBusy)
            {
                ApplyPrsnFilter(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id);
            }
        }

        private static PrdspvDirectoryItem ClonePrdspv(PrdspvDirectoryItem source)
        {
            return new PrdspvDirectoryItem
            {
                EmploymentId = source.EmploymentId,
                Id = source.Id,
                Notes = source.Notes,
                ReceivedOn = source.ReceivedOn,
                StampCode = source.StampCode,
                StampTypeId = source.StampTypeId,
                StampTypeName = source.StampTypeName
            };
        }

        private static PrfrDirectoryItem ClonePrfr(PrfrDirectoryItem source)
        {
            return new PrfrDirectoryItem
            {
                DismissalDate = source.DismissalDate,
                EmploymentId = source.EmploymentId,
                Guid = source.Guid,
                HireDate = source.HireDate,
                IsPinAuth = source.IsPinAuth,
                LastVacationEndDate = source.LastVacationEndDate,
                LastVacationStartDate = source.LastVacationStartDate,
                NextVacationEndDate = source.NextVacationEndDate,
                NextVacationStartDate = source.NextVacationStartDate,
                OrganizationId = source.OrganizationId,
                OrganizationName = source.OrganizationName,
                PersonId = source.PersonId,
                PersonnelNumber = source.PersonnelNumber,
                PinChangedAt = source.PinChangedAt,
                PinHash = source.PinHash,
                PositionId = source.PositionId,
                PositionName = source.PositionName,
                PositionStartDate = source.PositionStartDate,
                QualificationPlanDate = source.QualificationPlanDate
            };
        }

        private static PrfrvdDirectoryItem ClonePrfrvd(PrfrvdDirectoryItem source)
        {
            return new PrfrvdDirectoryItem
            {
                ActivityId = source.ActivityId,
                ActivityName = source.ActivityName,
                EmploymentId = source.EmploymentId,
                Id = source.Id
            };
        }

        private static PrsnDirectoryItem ClonePrsn(PrsnDirectoryItem source)
        {
            return new PrsnDirectoryItem
            {
                Email = source.Email,
                ExternalId = source.ExternalId,
                FirstName = source.FirstName,
                FullName = source.FullName,
                Guid = source.Guid,
                Id = source.Id,
                LastName = source.LastName,
                Notes = source.Notes,
                Patronymic = source.Patronymic,
                Phone = source.Phone
            };
        }

        private void DeletePrdspvAsync()
        {
            if (SelectedPrdspv == null)
            {
                return;
            }

            var selected = SelectedPrdspv;
            if (!_dialogService.Confirm(string.Format("Удалить клеймо \"{0}\"?", selected.StampCode)))
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                var result = await Task.Run(delegate { return _service.DeletePrdspvItem(selected.Id); });
                if (!result.IsDeleted)
                {
                    _dialogService.ShowWarning(result.WarningMessage);
                    return;
                }

                await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, selected.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, null);
                _dialogService.ShowInfo("Запись PRDSPV удалена.");
            });
        }

        private void DeletePrfrAsync()
        {
            if (SelectedPrfr == null)
            {
                return;
            }

            var selected = SelectedPrfr;
            if (!_dialogService.Confirm(string.Format("Удалить запись персонала в организации \"{0}\"?", selected.OrganizationName)))
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                var result = await Task.Run(delegate { return _service.DeletePrfrItem(selected.EmploymentId); });
                if (!result.IsDeleted)
                {
                    _dialogService.ShowWarning(result.WarningMessage);
                    return;
                }

                await RefreshPrsnCoreAsync(selected.PersonId, null, null, null);
                _dialogService.ShowInfo("Запись PRFR удалена.");
            });
        }

        private void DeletePrfrvdAsync()
        {
            if (SelectedPrfrvd == null)
            {
                return;
            }

            var selected = SelectedPrfrvd;
            if (!_dialogService.Confirm(string.Format("Удалить вид деятельности \"{0}\"?", selected.ActivityName)))
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                var result = await Task.Run(delegate { return _service.DeletePrfrvdItem(selected.Id); });
                if (!result.IsDeleted)
                {
                    _dialogService.ShowWarning(result.WarningMessage);
                    return;
                }

                await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, selected.EmploymentId, null, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
                _dialogService.ShowInfo("Запись PRFRVD удалена.");
            });
        }

        private void DeletePrsnAsync()
        {
            if (SelectedPrsn == null)
            {
                return;
            }

            var selected = SelectedPrsn;
            if (!_dialogService.Confirm(string.Format("Удалить персону \"{0}\"?", selected.FullName)))
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                var result = await Task.Run(delegate { return _service.DeletePrsnItem(selected.Id); });
                if (!result.IsDeleted)
                {
                    _dialogService.ShowWarning(result.WarningMessage);
                    return;
                }

                await RefreshPrsnCoreAsync(null, null, null, null);
                _dialogService.ShowInfo("Запись PRSN удалена.");
            });
        }

        private async Task ExecuteBusyOperationAsync(Func<Task> operation)
        {
            try
            {
                IsBusy = true;
                await operation();
            }
            catch (Exception ex)
            {
                _dialogService.ShowError(ex.Message);
            }
            finally
            {
                IsBusy = false;
            }
        }

        private async Task ExecuteMutationOperationAsync(Func<Task> operation)
        {
            try
            {
                IsBusy = true;
                await operation();
            }
            catch (InvalidOperationException ex)
            {
                _dialogService.ShowWarning(ex.Message);
            }
            catch (Exception ex)
            {
                _dialogService.ShowError(ex.Message);
            }
            finally
            {
                IsBusy = false;
            }
        }

        private void EditPrdspvAsync()
        {
            if (SelectedPrdspv == null)
            {
                return;
            }

            var result = _dialogService.ShowPrdspvEditDialog(ClonePrdspv(SelectedPrdspv), false, PrdspvItems.ToList(), _service);
            if (result == null)
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                await Task.Run(delegate { _service.UpdatePrdspvItem(result); });
                await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, result.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, result.Id);
                _dialogService.ShowInfo("Запись PRDSPV обновлена.");
            });
        }

        private void EditPrfrAsync()
        {
            if (SelectedPrfr == null)
            {
                return;
            }

            var result = _dialogService.ShowPrfrEditDialog(ClonePrfr(SelectedPrfr), false, PrfrItems.ToList(), _service);
            if (result == null)
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                await Task.Run(delegate { _service.UpdatePrfrItem(result); });
                await RefreshPrsnCoreAsync(result.PersonId, result.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
                _dialogService.ShowInfo("Запись PRFR обновлена.");
            });
        }

        private void EditPrfrvdAsync()
        {
            if (SelectedPrfrvd == null)
            {
                return;
            }

            var result = _dialogService.ShowPrfrvdEditDialog(ClonePrfrvd(SelectedPrfrvd), false, PrfrvdItems.ToList(), _service);
            if (result == null)
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                await Task.Run(delegate { _service.UpdatePrfrvdItem(result); });
                await RefreshPrsnCoreAsync(SelectedPrsn == null ? (int?)null : SelectedPrsn.Id, result.EmploymentId, result.Id, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
                _dialogService.ShowInfo("Запись PRFRVD обновлена.");
            });
        }

        private void EditPrsnAsync()
        {
            if (SelectedPrsn == null)
            {
                return;
            }

            var result = _dialogService.ShowPrsnEditDialog(ClonePrsn(SelectedPrsn), false, _prsnCache.ToList());
            if (result == null)
            {
                return;
            }

            RunMutationOperation(async delegate
            {
                await Task.Run(delegate { _service.UpdatePrsnItem(result); });
                await RefreshPrsnCoreAsync(result.Id, SelectedPrfr == null ? (int?)null : SelectedPrfr.EmploymentId, SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id, SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
                _dialogService.ShowInfo("Запись PRSN обновлена.");
            });
        }

        private string[] GetSearchTokens()
        {
            return (SearchText ?? string.Empty)
                .Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(delegate(string token) { return token.Trim().ToUpperInvariant(); })
                .Where(delegate(string token) { return token.Length > 0; })
                .ToArray();
        }

        private void LoadLeafTablesForSelection()
        {
            if (!IsBusy)
            {
                RunBusyOperation(async delegate
                {
                    if (SelectedPrfr == null)
                    {
                        PrfrvdItems.Clear();
                        PrdspvItems.Clear();
                        SelectedPrfrvd = null;
                        SelectedPrdspv = null;
                        UpdateStatus();
                        return;
                    }

                    await RefreshLeafTablesCoreAsync(SelectedPrfr.EmploymentId, null, null);
                });
            }
        }

        private void LoadPrfrForSelection()
        {
            if (!IsBusy)
            {
                RunBusyOperation(async delegate
                {
                    if (SelectedPrsn == null)
                    {
                        PrfrItems.Clear();
                        PrfrvdItems.Clear();
                        PrdspvItems.Clear();
                        SelectedPrfr = null;
                        SelectedPrfrvd = null;
                        SelectedPrdspv = null;
                        UpdateStatus();
                        return;
                    }

                    await RefreshPrfrCoreAsync(SelectedPrsn.Id, null, null, null);
                });
            }
        }

        private bool MatchesSearch(PrsnDirectoryItem item)
        {
            if (item == null)
            {
                return false;
            }

            var tokens = GetSearchTokens();
            if (tokens.Length == 0)
            {
                return true;
            }

            var haystack = string.Join(
                " ",
                new[]
                {
                    item.Id.ToString(),
                    item.FullName,
                    item.LastName,
                    item.FirstName,
                    item.Patronymic,
                    item.Phone,
                    item.Email,
                    item.ExternalId,
                    item.Guid,
                    item.OrganizationNames,
                    item.ActivityNames,
                    item.StampNames,
                    item.Notes
                }
                .Where(delegate(string value) { return !string.IsNullOrWhiteSpace(value); }))
                .ToUpperInvariant();
            return tokens.All(delegate(string token) { return haystack.IndexOf(token, StringComparison.Ordinal) >= 0; });
        }

        private async Task RefreshLeafTablesCoreAsync(int employmentId, int? prfrvdIdToSelect, int? prdspvIdToSelect)
        {
            var prfrvdTask = Task.Run(delegate { return _service.LoadPrfrvdItems(employmentId); });
            var prdspvTask = Task.Run(delegate { return _service.LoadPrdspvItems(employmentId); });
            await Task.WhenAll(prfrvdTask, prdspvTask);

            PrfrvdItems.Clear();
            foreach (var item in prfrvdTask.Result)
            {
                PrfrvdItems.Add(item);
            }

            PrdspvItems.Clear();
            foreach (var item in prdspvTask.Result)
            {
                PrdspvItems.Add(item);
            }

            SelectedPrfrvd = prfrvdIdToSelect.HasValue
                ? PrfrvdItems.FirstOrDefault(delegate(PrfrvdDirectoryItem item) { return item.Id == prfrvdIdToSelect.Value; })
                : PrfrvdItems.FirstOrDefault();
            SelectedPrdspv = prdspvIdToSelect.HasValue
                ? PrdspvItems.FirstOrDefault(delegate(PrdspvDirectoryItem item) { return item.Id == prdspvIdToSelect.Value; })
                : PrdspvItems.FirstOrDefault();
            UpdateStatus();
        }

        private async Task RefreshPrfrCoreAsync(int personId, int? prfrIdToSelect, int? prfrvdIdToSelect, int? prdspvIdToSelect)
        {
            var items = await Task.Run(delegate { return _service.LoadPrfrItems(personId); });
            PrfrItems.Clear();
            foreach (var item in items)
            {
                PrfrItems.Add(item);
            }

            SelectedPrfr = prfrIdToSelect.HasValue
                ? PrfrItems.FirstOrDefault(delegate(PrfrDirectoryItem item) { return item.EmploymentId == prfrIdToSelect.Value; })
                : PrfrItems.FirstOrDefault();
            if (SelectedPrfr == null)
            {
                PrfrvdItems.Clear();
                PrdspvItems.Clear();
                SelectedPrfrvd = null;
                SelectedPrdspv = null;
                UpdateStatus();
                return;
            }

            await RefreshLeafTablesCoreAsync(SelectedPrfr.EmploymentId, prfrvdIdToSelect, prdspvIdToSelect);
            UpdateStatus();
        }

        private async Task RefreshPrsnCoreAsync(int? prsnIdToSelect, int? prfrIdToSelect, int? prfrvdIdToSelect, int? prdspvIdToSelect)
        {
            var currentPrsnId = SelectedPrsn == null ? (int?)null : SelectedPrsn.Id;
            _prsnCache = (await Task.Run(delegate { return _service.LoadPrsnItems(); })).ToList();

            ApplyPrsnFilter(prsnIdToSelect.HasValue ? prsnIdToSelect : currentPrsnId);
            if (SelectedPrsn == null)
            {
                PrfrItems.Clear();
                PrfrvdItems.Clear();
                PrdspvItems.Clear();
                SelectedPrfr = null;
                SelectedPrfrvd = null;
                SelectedPrdspv = null;
                UpdateStatus();
                return;
            }

            await RefreshPrfrCoreAsync(SelectedPrsn.Id, prfrIdToSelect, prfrvdIdToSelect, prdspvIdToSelect);
            UpdateStatus();
        }

        private void RaiseCommandStates()
        {
            ((RelayCommand)AddPrsnCommand).RaiseCanExecuteChanged();
            ((RelayCommand)EditPrsnCommand).RaiseCanExecuteChanged();
            ((RelayCommand)DeletePrsnCommand).RaiseCanExecuteChanged();
            ((RelayCommand)AddPrfrCommand).RaiseCanExecuteChanged();
            ((RelayCommand)EditPrfrCommand).RaiseCanExecuteChanged();
            ((RelayCommand)DeletePrfrCommand).RaiseCanExecuteChanged();
            ((RelayCommand)AddPrfrvdCommand).RaiseCanExecuteChanged();
            ((RelayCommand)EditPrfrvdCommand).RaiseCanExecuteChanged();
            ((RelayCommand)DeletePrfrvdCommand).RaiseCanExecuteChanged();
            ((RelayCommand)AddPrdspvCommand).RaiseCanExecuteChanged();
            ((RelayCommand)EditPrdspvCommand).RaiseCanExecuteChanged();
            ((RelayCommand)DeletePrdspvCommand).RaiseCanExecuteChanged();
            ((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
        }

        private void RefreshAsync()
        {
            RunBusyOperation(async delegate
            {
                await RefreshPrsnCoreAsync(
                    SelectedPrsn == null ? (int?)null : SelectedPrsn.Id,
                    SelectedPrfr == null ? (int?)null : SelectedPrfr.EmploymentId,
                    SelectedPrfrvd == null ? (int?)null : SelectedPrfrvd.Id,
                    SelectedPrdspv == null ? (int?)null : SelectedPrdspv.Id);
            });
        }

        private async void RunBusyOperation(Func<Task> operation)
        {
            try
            {
                await ExecuteBusyOperationAsync(operation);
            }
            catch (Exception ex)
            {
                IsBusy = false;
                _dialogService.ShowError(ex.Message);
            }
        }

        private async void RunMutationOperation(Func<Task> operation)
        {
            try
            {
                await ExecuteMutationOperationAsync(operation);
            }
            catch (Exception ex)
            {
                IsBusy = false;
                _dialogService.ShowError(ex.Message);
            }
        }

        private void UpdateStatus()
        {
            var searchText = string.IsNullOrWhiteSpace(SearchText) ? null : SearchText.Trim();
            StatusText = string.Format(
                "{0}PRSN: {1}/{2}. PRFR: {3}. PRFRVD: {4}. PRDSPV: {5}.",
                string.IsNullOrWhiteSpace(searchText) ? string.Empty : string.Format("Поиск: \"{0}\". ", searchText),
                PrsnItems.Count,
                _prsnCache.Count,
                PrfrItems.Count,
                PrfrvdItems.Count,
                PrdspvItems.Count);
        }
    }
}
