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

namespace XLAB2
{
    internal sealed class PrsnEditWindowViewModel : ObservableObject
    {
        private readonly IReadOnlyList<PrsnDirectoryItem> _existingItems;
        private string _email;
        private string _externalId;
        private string _firstName;
        private string _fullName;
        private string _guid;
        private string _lastName;
        private string _notes;
        private string _patronymic;
        private string _phone;
        private string _validationMessage;

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

            FullName = source.FullName ?? string.Empty;
            LastName = source.LastName ?? string.Empty;
            FirstName = source.FirstName ?? string.Empty;
            Patronymic = source.Patronymic ?? string.Empty;
            Phone = source.Phone ?? string.Empty;
            Email = source.Email ?? string.Empty;
            Notes = source.Notes ?? string.Empty;
            Guid = source.Guid ?? string.Empty;
            ExternalId = source.ExternalId ?? string.Empty;

            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 string Email
        {
            get { return _email; }
            set { SetProperty(ref _email, value); }
        }

        public string ExternalId
        {
            get { return _externalId; }
            set { SetProperty(ref _externalId, value); }
        }

        public string FirstName
        {
            get { return _firstName; }
            set { SetProperty(ref _firstName, value); }
        }

        public string FullName
        {
            get { return _fullName; }
            set { SetProperty(ref _fullName, value); }
        }

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

        public int Id { get; private set; }
        public bool IsNew { get; private set; }

        public string LastName
        {
            get { return _lastName; }
            set { SetProperty(ref _lastName, value); }
        }

        public string Notes
        {
            get { return _notes; }
            set { SetProperty(ref _notes, value); }
        }

        public string Patronymic
        {
            get { return _patronymic; }
            set { SetProperty(ref _patronymic, value); }
        }

        public string Phone
        {
            get { return _phone; }
            set { SetProperty(ref _phone, value); }
        }

        public string Title
        {
            get { return IsNew ? "Новая персона" : "Редактирование персоны"; }
        }

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

        public PrsnDirectoryItem ToResult()
        {
            return new PrsnDirectoryItem
            {
                Email = NormalizeNullable(Email),
                ExternalId = NormalizeNullable(ExternalId),
                FirstName = NormalizeNullable(FirstName),
                FullName = NormalizeRequired(FullName),
                Guid = NormalizeNullable(Guid),
                Id = Id,
                LastName = NormalizeNullable(LastName),
                Notes = NormalizeNullable(Notes),
                Patronymic = NormalizeNullable(Patronymic),
                Phone = NormalizeNullable(Phone)
            };
        }

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

        private void Confirm(object parameter)
        {
            var normalizedFullName = NormalizeRequired(FullName);
            var normalizedLastName = NormalizeNullable(LastName);
            var normalizedFirstName = NormalizeNullable(FirstName);
            var normalizedPatronymic = NormalizeNullable(Patronymic);
            var normalizedPhone = NormalizeNullable(Phone);
            var normalizedEmail = NormalizeNullable(Email);
            var normalizedNotes = NormalizeNullable(Notes);
            var normalizedGuid = NormalizeNullable(Guid);
            var normalizedExternalId = NormalizeNullable(ExternalId);

            if (normalizedFullName.Length == 0)
            {
                ValidationMessage = "Укажите ФИО.";
                return;
            }

            if (normalizedFullName.Length > PrsnDirectoryRules.FullNameMaxLength)
            {
                ValidationMessage = string.Format("ФИО не должно превышать {0} символов.", PrsnDirectoryRules.FullNameMaxLength);
                return;
            }

            if (!ValidateMaxLength(normalizedLastName, PrsnDirectoryRules.LastNameMaxLength, "Фамилия")
                || !ValidateMaxLength(normalizedFirstName, PrsnDirectoryRules.FirstNameMaxLength, "Имя")
                || !ValidateMaxLength(normalizedPatronymic, PrsnDirectoryRules.PatronymicMaxLength, "Отчество")
                || !ValidateMaxLength(normalizedPhone, PrsnDirectoryRules.PhoneMaxLength, "Телефон")
                || !ValidateMaxLength(normalizedEmail, PrsnDirectoryRules.EmailMaxLength, "E-mail")
                || !ValidateMaxLength(normalizedGuid, PrsnDirectoryRules.GuidMaxLength, "GUID персоны")
                || !ValidateMaxLength(normalizedExternalId, PrsnDirectoryRules.ExternalIdMaxLength, "Дополнительный идентификатор персоны")
                || !ValidateMaxLength(normalizedNotes, PrsnDirectoryRules.NotesMaxLength, "Дополнительные сведения о персоне"))
            {
                return;
            }

            var duplicate = _existingItems.FirstOrDefault(delegate(PrsnDirectoryItem item)
            {
                return item != null
                       && item.Id != Id
                       && string.Equals(NormalizeRequired(item.FullName), normalizedFullName, StringComparison.OrdinalIgnoreCase)
                       && string.Equals(NormalizeNullable(item.ExternalId) ?? string.Empty, normalizedExternalId ?? string.Empty, StringComparison.OrdinalIgnoreCase);
            });
            if (duplicate != null)
            {
                ValidationMessage = normalizedExternalId == null
                    ? string.Format("Персона \"{0}\" без дополнительного идентификатора уже существует в справочнике.", normalizedFullName)
                    : string.Format("Персона \"{0}\" с дополнительным идентификатором \"{1}\" уже существует в справочнике.", normalizedFullName, normalizedExternalId);
                return;
            }

            var duplicateGuid = _existingItems.FirstOrDefault(delegate(PrsnDirectoryItem 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 string NormalizeNullable(string value)
        {
            return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
        }

        private string NormalizeRequired(string value)
        {
            return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim();
        }

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

        private bool ValidateMaxLength(string value, int maxLength, string fieldName)
        {
            if (value != null && value.Length > maxLength)
            {
                ValidationMessage = string.Format("{0} не должно превышать {1} символов.", fieldName, maxLength);
                return false;
            }

            return true;
        }
    }
}
