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

namespace XLAB
{
    internal sealed class PlanningEditWindowViewModel : ObservableObject
    {
        private readonly IReadOnlyList<PlanningInstrumentOption> _instrumentItems;
        private readonly bool _isNew;
        private readonly int? _seedTemplateId;
        private readonly PlanningService _service;
        private int _selectedInstrumentId;
        private int? _selectedTemplateId;
        private IReadOnlyList<PlanningTemplateOption> _templateItems;
        private DateTime? _plannedOn;
        private string _validationMessage;

        public PlanningEditWindowViewModel(PlanningEditSeed seed, bool isNew, IReadOnlyList<PlanningInstrumentOption> instruments, PlanningService service)
        {
            if (seed == null)
            {
                throw new ArgumentNullException("seed");
            }

            _instrumentItems = instruments ?? Array.Empty<PlanningInstrumentOption>();
            _service = service ?? throw new ArgumentNullException("service");
            _isNew = isNew;
            _seedTemplateId = seed.TemplateId;

            PlanId = seed.PlanId;
            TargetYear = seed.TargetYear;
            InstrumentItems = _instrumentItems
                .OrderBy(delegate(PlanningInstrumentOption item) { return item.DisplayName; }, StringComparer.CurrentCultureIgnoreCase)
                .ToList();
            TemplateItems = Array.Empty<PlanningTemplateOption>();
            PlannedOn = seed.PlannedOn;

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

            if (seed.InstrumentId > 0)
            {
                SelectedInstrumentId = seed.InstrumentId;
            }
            else if (InstrumentItems.Count > 0)
            {
                SelectedInstrumentId = InstrumentItems[0].Id;
            }
        }

        public event EventHandler<bool?> CloseRequested;

        public ICommand CancelCommand { get; private set; }

        public ICommand ConfirmCommand { get; private set; }

        public IReadOnlyList<PlanningInstrumentOption> InstrumentItems { get; private set; }

        public int? PlanId { get; private set; }

        public DateTime? PlannedOn
        {
            get { return _plannedOn; }
            set { SetProperty(ref _plannedOn, value); }
        }

        public int SelectedInstrumentId
        {
            get { return _selectedInstrumentId; }
            set
            {
                if (SetProperty(ref _selectedInstrumentId, value))
                {
                    LoadTemplates();
                    OnPropertyChanged("SelectedInstrumentDescription");
                    OnPropertyChanged("TemplateWarningMessage");
                }
            }
        }

        public PlanningInstrumentOption SelectedInstrument
        {
            get { return InstrumentItems.FirstOrDefault(delegate(PlanningInstrumentOption item) { return item.Id == SelectedInstrumentId; }); }
        }

        public string SelectedInstrumentDescription
        {
            get
            {
                return SelectedInstrument == null ? string.Empty : SelectedInstrument.DisplayName;
            }
        }

        public int? SelectedTemplateId
        {
            get { return _selectedTemplateId; }
            set { SetProperty(ref _selectedTemplateId, value); }
        }

        public IReadOnlyList<PlanningTemplateOption> TemplateItems
        {
            get { return _templateItems; }
            private set
            {
                _templateItems = value ?? Array.Empty<PlanningTemplateOption>();
                OnPropertyChanged("TemplateItems");
                OnPropertyChanged("SelectedTemplateDescription");
            }
        }

        public string SelectedTemplateDescription
        {
            get
            {
                var selected = TemplateItems.FirstOrDefault(delegate(PlanningTemplateOption item) { return item.Id == SelectedTemplateId; });
                return selected == null ? string.Empty : selected.DisplayName;
            }
        }

        public string TemplateWarningMessage
        {
            get
            {
                if (SelectedInstrument == null || TemplateItems.Count > 0)
                {
                    return string.Empty;
                }

                return "Для выбранного прибора в TPRMCP не найден период. Расчет можно показать, но запись в EKZMCP сохранить нельзя.";
            }
        }

        public int TargetYear { get; private set; }

        public string Title
        {
            get
            {
                return _isNew || !PlanId.HasValue
                    ? string.Format("Планирование на {0} год", TargetYear)
                    : string.Format("Редактирование плана {0}", TargetYear);
            }
        }

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

        public PlanningEditResult ToResult()
        {
            if (!PlannedOn.HasValue || !SelectedTemplateId.HasValue)
            {
                throw new InvalidOperationException("Результат плановой записи недоступен без даты и периода.");
            }

            return new PlanningEditResult
            {
                PlanId = PlanId,
                InstrumentId = SelectedInstrumentId,
                TemplateId = SelectedTemplateId.Value,
                PlannedOn = PlannedOn.Value.Date
            };
        }

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

        private void Confirm(object parameter)
        {
            if (SelectedInstrument == null)
            {
                ValidationMessage = "Выберите прибор.";
                return;
            }

            if (TemplateItems.Count == 0)
            {
                ValidationMessage = "Для выбранного прибора отсутствует период TPRMCP. Сохранить запись в EKZMCP нельзя.";
                return;
            }

            if (!SelectedTemplateId.HasValue || TemplateItems.All(delegate(PlanningTemplateOption item) { return item.Id != SelectedTemplateId.Value; }))
            {
                ValidationMessage = "Выберите период из TPRMCP.";
                return;
            }

            if (!PlannedOn.HasValue)
            {
                ValidationMessage = "Укажите плановую дату.";
                return;
            }

            if (PlannedOn.Value.Year != TargetYear)
            {
                ValidationMessage = string.Format("Плановая дата должна относиться к {0} году.", TargetYear);
                return;
            }

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

        private void LoadTemplates()
        {
            var instrument = SelectedInstrument;
            if (instrument == null)
            {
                TemplateItems = Array.Empty<PlanningTemplateOption>();
                SelectedTemplateId = null;
                return;
            }

            var templates = _service.LoadTemplateOptions(instrument.TypeSizeId)
                .OrderBy(delegate(PlanningTemplateOption item) { return item.PeriodMonths; })
                .ThenBy(delegate(PlanningTemplateOption item) { return item.DisplayName; }, StringComparer.CurrentCultureIgnoreCase)
                .ToList();

            TemplateItems = templates;

            if (_seedTemplateId.HasValue && templates.Any(delegate(PlanningTemplateOption item) { return item.Id == _seedTemplateId.Value; }))
            {
                SelectedTemplateId = _seedTemplateId.Value;
                return;
            }

            if (SelectedTemplateId.HasValue && templates.Any(delegate(PlanningTemplateOption item) { return item.Id == SelectedTemplateId.Value; }))
            {
                return;
            }

            SelectedTemplateId = templates.Count > 0 ? (int?)templates[0].Id : null;
        }

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