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

namespace XLAB2
{
    internal sealed class TprmkEditWindowViewModel : ObservableObject
    {
        private readonly IReadOnlyList<TprmkDirectoryItem> _existingItems;
        private string _costText;
        private string _extraCostText;
        private int _groupId;
        private int _organizationId;
        private int _placeId;
        private int _qualificationId;
        private string _rushMarkupText;
        private string _timeNormCode;
        private string _timeNormHoursText;
        private string _normDocHoursText;
        private string _validationMessage;
        private string _verificationCode;
        private int _verificationTypeId;
        private string _verifierCountText;

        public TprmkEditWindowViewModel(TprmkDirectoryItem seed, bool isNew, IReadOnlyList<TprmkDirectoryItem> existingItems, TypeSizeDirectoryService service)
        {
            var source = seed ?? new TprmkDirectoryItem();
            _existingItems = existingItems ?? Array.Empty<TprmkDirectoryItem>();
            Id = source.Id;
            TypeSizeId = source.TypeSizeId;
            IsNew = isNew;
            VerificationTypes = service.LoadSpvdmkReferences();
            Organizations = service.LoadFrpdReferences();
            Qualifications = WithEmpty(service.LoadSpkvReferences());
            Groups = WithEmpty(service.LoadGrsiReferences());
            Places = WithEmpty(service.LoadSpmpobReferences());
            VerificationTypeId = source.VerificationTypeId;
            OrganizationId = source.OrganizationId;
            QualificationId = source.QualificationId ?? 0;
            GroupId = source.GroupId ?? 0;
            PlaceId = source.PlaceId ?? 0;
            CostText = ToString(source.Cost);
            ExtraCostText = ToString(source.ExtraCost);
            RushMarkupText = ToString(source.RushMarkup);
            TimeNormHoursText = ToString(source.TimeNormHours);
            NormDocHoursText = ToString(source.NormDocHours);
            TimeNormCode = source.TimeNormCode ?? string.Empty;
            VerificationCode = source.VerificationCode ?? string.Empty;
            VerifierCountText = source.VerifierCount.ToString();
            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 IReadOnlyList<DirectoryLookupItem> VerificationTypes { get; private set; }
        public IReadOnlyList<DirectoryLookupItem> Organizations { get; private set; }
        public IReadOnlyList<DirectoryLookupItem> Qualifications { get; private set; }
        public IReadOnlyList<DirectoryLookupItem> Groups { get; private set; }
        public IReadOnlyList<DirectoryLookupItem> Places { get; private set; }
        public int Id { get; private set; }
        public bool IsNew { get; private set; }
        public int TypeSizeId { get; private set; }
        public int VerificationTypeId { get { return _verificationTypeId; } set { SetProperty(ref _verificationTypeId, value); } }
        public int OrganizationId { get { return _organizationId; } set { SetProperty(ref _organizationId, value); } }
        public int QualificationId { get { return _qualificationId; } set { SetProperty(ref _qualificationId, value); } }
        public int GroupId { get { return _groupId; } set { SetProperty(ref _groupId, value); } }
        public int PlaceId { get { return _placeId; } set { SetProperty(ref _placeId, value); } }
        public string CostText { get { return _costText; } set { SetProperty(ref _costText, value); } }
        public string ExtraCostText { get { return _extraCostText; } set { SetProperty(ref _extraCostText, value); } }
        public string RushMarkupText { get { return _rushMarkupText; } set { SetProperty(ref _rushMarkupText, value); } }
        public string TimeNormHoursText { get { return _timeNormHoursText; } set { SetProperty(ref _timeNormHoursText, value); } }
        public string NormDocHoursText { get { return _normDocHoursText; } set { SetProperty(ref _normDocHoursText, value); } }
        public string TimeNormCode { get { return _timeNormCode; } set { SetProperty(ref _timeNormCode, value); } }
        public string VerificationCode { get { return _verificationCode; } set { SetProperty(ref _verificationCode, value); } }
        public string VerifierCountText { get { return _verifierCountText; } set { SetProperty(ref _verifierCountText, value); } }
        public string Title { get { return IsNew ? "Новый регламент МК" : "Редактирование регламента МК"; } }
        public string ValidationMessage { get { return _validationMessage; } private set { SetProperty(ref _validationMessage, value); } }

        public TprmkDirectoryItem ToResult()
        {
            return new TprmkDirectoryItem
            {
                Id = Id,
                TypeSizeId = TypeSizeId,
                VerificationTypeId = VerificationTypeId,
                OrganizationId = OrganizationId,
                QualificationId = QualificationId > 0 ? (int?)QualificationId : null,
                GroupId = GroupId > 0 ? (int?)GroupId : null,
                PlaceId = PlaceId > 0 ? (int?)PlaceId : null,
                Cost = ParseNullableDecimal(CostText),
                ExtraCost = ParseNullableDecimal(ExtraCostText),
                RushMarkup = ParseNullableDecimal(RushMarkupText),
                TimeNormHours = ParseNullableDecimal(TimeNormHoursText),
                NormDocHours = ParseNullableDecimal(NormDocHoursText),
                TimeNormCode = Normalize(TimeNormCode),
                VerificationCode = Normalize(VerificationCode),
                VerifierCount = int.Parse(VerifierCountText.Trim())
            };
        }

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

        private void Confirm(object parameter)
        {
            if (VerificationTypeId <= 0) { ValidationMessage = "Укажите вид МК."; return; }
            if (OrganizationId <= 0) { ValidationMessage = "Укажите организацию/подразделение."; return; }
            int verifierCount;
            if (!int.TryParse((VerifierCountText ?? string.Empty).Trim(), out verifierCount)) { ValidationMessage = "Количество поверителей должно быть целым числом."; return; }
            decimal? value;
            if (!TryParseNullableDecimal(CostText, out value)) { ValidationMessage = "Стоимость должна быть числом."; return; }
            if (!TryParseNullableDecimal(ExtraCostText, out value)) { ValidationMessage = "Дополнительная стоимость должна быть числом."; return; }
            if (!TryParseNullableDecimal(RushMarkupText, out value)) { ValidationMessage = "Наценка за срочность должна быть числом."; return; }
            if (!TryParseNullableDecimal(TimeNormHoursText, out value)) { ValidationMessage = "Норма времени должна быть числом."; return; }
            if (!TryParseNullableDecimal(NormDocHoursText, out value)) { ValidationMessage = "Норма времени по НД должна быть числом."; return; }
            var timeNormCode = Normalize(TimeNormCode);
            if (!string.IsNullOrWhiteSpace(timeNormCode) && timeNormCode.Length > TprmkDirectoryRules.TimeNormCodeMaxLength) { ValidationMessage = string.Format("Код нормы не должен превышать {0} символов.", TprmkDirectoryRules.TimeNormCodeMaxLength); return; }
            var verificationCode = Normalize(VerificationCode);
            if (!string.IsNullOrWhiteSpace(verificationCode) && verificationCode.Length > TprmkDirectoryRules.VerificationCodeMaxLength) { ValidationMessage = string.Format("Код поверки не должен превышать {0} символов.", TprmkDirectoryRules.VerificationCodeMaxLength); return; }
            var duplicate = _existingItems.FirstOrDefault(delegate(TprmkDirectoryItem item)
            {
                return item != null && item.Id != Id && item.VerificationTypeId == VerificationTypeId && item.OrganizationId == OrganizationId && item.GroupId == (GroupId > 0 ? (int?)GroupId : null) && item.PlaceId == (PlaceId > 0 ? (int?)PlaceId : null);
            });
            if (duplicate != null) { ValidationMessage = "Такой регламент МК уже существует для выбранного типоразмера."; return; }
            ValidationMessage = string.Empty;
            RaiseCloseRequested(true);
        }

        private static IReadOnlyList<DirectoryLookupItem> WithEmpty(IReadOnlyList<DirectoryLookupItem> source)
        {
            return new[] { new DirectoryLookupItem { Id = 0, Name = string.Empty } }.Concat(source ?? Array.Empty<DirectoryLookupItem>()).ToList();
        }

        private static string Normalize(string value) { return string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim(); }
        private static string ToString(decimal? value) { return value.HasValue ? value.Value.ToString("0.##") : string.Empty; }
        private static decimal? ParseNullableDecimal(string value) { decimal? parsed; return TryParseNullableDecimal(value, out parsed) ? parsed : (decimal?)null; }
        private static bool TryParseNullableDecimal(string value, out decimal? result)
        {
            if (string.IsNullOrWhiteSpace(value)) { result = null; return true; }
            decimal parsed;
            if (decimal.TryParse(value.Trim(), out parsed)) { result = parsed; return true; }
            result = null; return false;
        }

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