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

namespace XLAB2
{
    internal sealed class EkzEditWindowViewModel : ObservableObject
    {
        private readonly IReadOnlyList<EkzDirectoryItem> _existingItems;
        private string _inventoryNumber;
        private string _notes;
        private int _ownerOrganizationId;
        private string _serialNumber;
        private int _typeSizeId;
        private string _validationMessage;

        public EkzEditWindowViewModel(EkzDirectoryItem seed, bool isNew, IReadOnlyList<EkzDirectoryItem> existingItems, EkzDirectoryService service)
        {
            var source = seed ?? new EkzDirectoryItem();
            _existingItems = existingItems ?? Array.Empty<EkzDirectoryItem>();

            Id = source.Id;
            IsNew = isNew;
            TypeSizeItems = service.LoadTypeSizeReferences();
            OwnerItems = service.LoadFrpdReferences();
            TypeSizeId = source.TypeSizeId;
            OwnerOrganizationId = source.OwnerOrganizationId;
            SerialNumber = source.SerialNumber ?? string.Empty;
            InventoryNumber = source.InventoryNumber ?? string.Empty;
            Notes = source.Notes ?? 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 int Id { get; private set; }

        public bool IsNew { get; private set; }

        public string InventoryNumber
        {
            get { return _inventoryNumber; }
            set { SetProperty(ref _inventoryNumber, value); }
        }

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

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

        public int OwnerOrganizationId
        {
            get { return _ownerOrganizationId; }
            set { SetProperty(ref _ownerOrganizationId, value); }
        }

        public string SerialNumber
        {
            get { return _serialNumber; }
            set { SetProperty(ref _serialNumber, value); }
        }

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

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

        public int TypeSizeId
        {
            get { return _typeSizeId; }
            set { SetProperty(ref _typeSizeId, value); }
        }

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

        public EkzDirectoryItem ToResult()
        {
            return new EkzDirectoryItem
            {
                Id = Id,
                TypeSizeId = TypeSizeId,
                OwnerOrganizationId = OwnerOrganizationId,
                SerialNumber = Normalize(SerialNumber),
                InventoryNumber = Normalize(InventoryNumber),
                Notes = Normalize(Notes)
            };
        }

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

        private void Confirm(object parameter)
        {
            var serialNumber = Normalize(SerialNumber);
            var inventoryNumber = Normalize(InventoryNumber);
            var notes = Normalize(Notes);

            if (TypeSizeId <= 0)
            {
                ValidationMessage = "Укажите типоразмер СИ.";
                return;
            }

            if (OwnerOrganizationId <= 0)
            {
                ValidationMessage = "Укажите организацию-владельца.";
                return;
            }

            if (string.IsNullOrWhiteSpace(serialNumber))
            {
                ValidationMessage = "Укажите заводской номер.";
                return;
            }

            if (serialNumber.Length > EkzDirectoryRules.SerialNumberMaxLength)
            {
                ValidationMessage = string.Format("Заводской номер не должен превышать {0} символов.", EkzDirectoryRules.SerialNumberMaxLength);
                return;
            }

            if (!string.IsNullOrWhiteSpace(inventoryNumber) && inventoryNumber.Length > EkzDirectoryRules.InventoryNumberMaxLength)
            {
                ValidationMessage = string.Format("Инвентарный номер не должен превышать {0} символов.", EkzDirectoryRules.InventoryNumberMaxLength);
                return;
            }

            if (!string.IsNullOrWhiteSpace(notes) && notes.Length > EkzDirectoryRules.NotesMaxLength)
            {
                ValidationMessage = string.Format("Примечание не должно превышать {0} символов.", EkzDirectoryRules.NotesMaxLength);
                return;
            }

            var duplicate = _existingItems.FirstOrDefault(delegate(EkzDirectoryItem item)
            {
                return item != null
                       && item.Id != Id
                       && item.TypeSizeId == TypeSizeId
                       && item.OwnerOrganizationId == OwnerOrganizationId
                       && string.Equals(item.SerialNumber ?? string.Empty, serialNumber ?? string.Empty, StringComparison.OrdinalIgnoreCase);
            });

            if (duplicate != null)
            {
                ValidationMessage = "Экземпляр с таким типоразмером, владельцем и заводским номером уже существует.";
                return;
            }

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

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

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