using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;

namespace XLAB
{
    internal sealed class FrpdEditWindowViewModel : ObservableObject
    {
        private readonly IReadOnlyList<FrpdDirectoryItem> _existingItems;
        private string _guid;
        private string _localCode;
        private string _name;
        private int _parentId;
        private string _validationMessage;

        public FrpdEditWindowViewModel(FrpdDirectoryItem seed, bool isNew, IReadOnlyList<FrpdDirectoryItem> existingItems, FrpdDirectoryService service)
        {
            var source = seed ?? new FrpdDirectoryItem();
            _existingItems = existingItems ?? Array.Empty<FrpdDirectoryItem>();
            Id = source.Id;
            IsNew = isNew;

            ParentItems = new[] { new DirectoryLookupItem { Id = 0, Name = string.Empty } }
                .Concat((service.LoadFrpdReferences() ?? Array.Empty<DirectoryLookupItem>()).Where(delegate(DirectoryLookupItem item) { return item.Id != Id; }))
                .ToList();

            ParentId = source.ParentId.HasValue ? source.ParentId.Value : 0;
            Name = source.Name ?? string.Empty;
            LocalCode = source.LocalCode ?? string.Empty;
            Guid = source.Guid ?? string.Empty;
            CreatedOn = source.CreatedOn;
            LiquidatedOn = source.LiquidatedOn;

            ConfirmCommand = new RelayCommand(Confirm);
            CancelCommand = new RelayCommand(Cancel);
        }

        public event EventHandler<bool?> CloseRequested;

        public ICommand CancelCommand { get; private set; }
        public ICommand ConfirmCommand { get; private set; }
        public DateTime? CreatedOn { get; set; }

        public string Guid
        {
            get { return _guid; }
            set { SetProperty(ref _guid, value); }
        }

        public Visibility GuidFieldVisibility
        {
            get { return IsNew ? Visibility.Collapsed : Visibility.Visible; }
        }

        public int Id { get; private set; }
        public bool IsNew { get; private set; }
        public DateTime? LiquidatedOn { get; set; }

        public string LocalCode
        {
            get { return _localCode; }
            set { SetProperty(ref _localCode, value); }
        }

        public string Name
        {
            get { return _name; }
            set { SetProperty(ref _name, value); }
        }

        public int ParentId
        {
            get { return _parentId; }
            set { SetProperty(ref _parentId, value); }
        }

        public IReadOnlyList<DirectoryLookupItem> ParentItems { get; private set; }

        public string Title
        {
            get { return IsNew ? "Новая организация/подразделение" : "Редактирование организации/подразделения"; }
        }

        public string ValidationMessage
        {
            get { return _validationMessage; }
            private set { SetProperty(ref _validationMessage, value); }
        }

        public FrpdDirectoryItem ToResult()
        {
            return new FrpdDirectoryItem
            {
                CreatedOn = CreatedOn,
                Guid = string.IsNullOrWhiteSpace(Guid) ? null : Guid.Trim(),
                Id = Id,
                LiquidatedOn = LiquidatedOn,
                LocalCode = string.IsNullOrWhiteSpace(LocalCode) ? null : LocalCode.Trim(),
                Name = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim(),
                ParentId = ParentId > 0 ? (int?)ParentId : null
            };
        }

        private void Cancel(object parameter)
        {
            RaiseCloseRequested(false);
        }

        private void Confirm(object parameter)
        {
            var normalizedName = string.IsNullOrWhiteSpace(Name) ? string.Empty : Name.Trim();
            var normalizedLocalCode = string.IsNullOrWhiteSpace(LocalCode) ? null : LocalCode.Trim();
            var normalizedGuid = string.IsNullOrWhiteSpace(Guid) ? null : Guid.Trim();

            if (normalizedName.Length == 0)
            {
                ValidationMessage = "Укажите организацию/подразделение.";
                return;
            }

            if (normalizedName.Length > FrpdDirectoryRules.NameMaxLength)
            {
                ValidationMessage = string.Format("Организация/подразделение не должна превышать {0} символов.", FrpdDirectoryRules.NameMaxLength);
                return;
            }

            if (normalizedLocalCode != null && normalizedLocalCode.Length > FrpdDirectoryRules.LocalCodeMaxLength)
            {
                ValidationMessage = string.Format("Локальный код не должен превышать {0} символов.", FrpdDirectoryRules.LocalCodeMaxLength);
                return;
            }

            if (normalizedGuid != null && normalizedGuid.Length > FrpdDirectoryRules.GuidMaxLength)
            {
                ValidationMessage = string.Format("GUID подразделения не должен превышать {0} символов.", FrpdDirectoryRules.GuidMaxLength);
                return;
            }

            if (ParentId == Id && Id > 0)
            {
                ValidationMessage = "Нельзя выбрать эту же запись как родительскую.";
                return;
            }

            var duplicateGuid = _existingItems.FirstOrDefault(delegate(FrpdDirectoryItem item)
            {
                return item != null
                       && item.Id != Id
                       && !string.IsNullOrWhiteSpace(item.Guid)
                       && normalizedGuid != null
                       && string.Equals(item.Guid.Trim(), normalizedGuid, StringComparison.OrdinalIgnoreCase);
            });
            if (duplicateGuid != null)
            {
                ValidationMessage = string.Format("GUID подразделения \"{0}\" уже существует в справочнике.", normalizedGuid);
                return;
            }

            ValidationMessage = string.Empty;
            RaiseCloseRequested(true);
        }

        private void RaiseCloseRequested(bool? dialogResult)
        {
            var handler = CloseRequested;
            if (handler != null)
            {
                handler(this, dialogResult);
            }
        }
    }
}
