using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;

namespace XLAB2
{
    internal sealed class VerificationReportsWindowViewModel : ObservableObject
    {
        private readonly IDialogService _dialogService;
        private readonly VerificationReportsService _service;
        private DateTime? _dateFrom;
        private DateTime? _dateTo;
        private bool _isBusy;
        private int _selectedCustomerId;
        private int _selectedMeasurementAreaId;
        private VerificationReportSummary _summary;
        private string _statusText;

        public VerificationReportsWindowViewModel(VerificationReportsService service, IDialogService dialogService)
        {
            _service = service;
            _dialogService = dialogService;

            CustomerItems = new ObservableCollection<DirectoryLookupItem>();
            MeasurementAreaItems = new ObservableCollection<DirectoryLookupItem>();
            CustomerRows = new ObservableCollection<VerificationReportCustomerRow>();
            MeasurementAreaRows = new ObservableCollection<VerificationReportMeasurementAreaRow>();
            Summary = new VerificationReportSummary();

            CustomerItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все заказчики" });
            MeasurementAreaItems.Add(new DirectoryLookupItem { Id = 0, Name = "Все области измерений" });

            RefreshCommand = new RelayCommand(delegate { RefreshAsync(); }, delegate { return !IsBusy; });

            UpdateStatus();
        }

        public ObservableCollection<DirectoryLookupItem> CustomerItems { get; private set; }

        public ObservableCollection<VerificationReportCustomerRow> CustomerRows { get; private set; }

        public DateTime? DateFrom
        {
            get { return _dateFrom; }
            set
            {
                if (SetProperty(ref _dateFrom, value))
                {
                    OnPropertyChanged("PeriodText");
                }
            }
        }

        public DateTime? DateTo
        {
            get { return _dateTo; }
            set
            {
                if (SetProperty(ref _dateTo, value))
                {
                    OnPropertyChanged("PeriodText");
                }
            }
        }

        public bool IsBusy
        {
            get { return _isBusy; }
            private set
            {
                if (SetProperty(ref _isBusy, value))
                {
                    ((RelayCommand)RefreshCommand).RaiseCanExecuteChanged();
                }
            }
        }

        public ObservableCollection<DirectoryLookupItem> MeasurementAreaItems { get; private set; }

        public ObservableCollection<VerificationReportMeasurementAreaRow> MeasurementAreaRows { get; private set; }

        public string PeriodText
        {
            get
            {
                if (!DateFrom.HasValue && !DateTo.HasValue)
                {
                    return "Период: все время.";
                }

                if (DateFrom.HasValue && DateTo.HasValue)
                {
                    return string.Format("Период: с {0:d} по {1:d}.", DateFrom.Value, DateTo.Value);
                }

                if (DateFrom.HasValue)
                {
                    return string.Format("Период: с {0:d}.", DateFrom.Value);
                }

                return string.Format("Период: по {0:d}.", DateTo.Value);
            }
        }

        public ICommand RefreshCommand { get; private set; }

        public int SelectedCustomerId
        {
            get { return _selectedCustomerId; }
            set { SetProperty(ref _selectedCustomerId, value); }
        }

        public int SelectedMeasurementAreaId
        {
            get { return _selectedMeasurementAreaId; }
            set { SetProperty(ref _selectedMeasurementAreaId, value); }
        }

        public string StatusText
        {
            get { return _statusText; }
            private set { SetProperty(ref _statusText, value); }
        }

        public VerificationReportSummary Summary
        {
            get { return _summary; }
            private set { SetProperty(ref _summary, value); }
        }

        public async Task InitializeAsync()
        {
            await ExecuteBusyOperationAsync(async delegate
            {
                await LoadFiltersAsync();
                await RefreshCoreAsync();
            });
        }

        private VerificationReportFilter BuildFilter()
        {
            if (DateFrom.HasValue && DateTo.HasValue && DateFrom.Value.Date > DateTo.Value.Date)
            {
                throw new InvalidOperationException("Дата \"с\" не может быть позже даты \"по\".");
            }

            return new VerificationReportFilter
            {
                CustomerId = SelectedCustomerId > 0 ? SelectedCustomerId : (int?)null,
                MeasurementAreaId = SelectedMeasurementAreaId > 0 ? SelectedMeasurementAreaId : (int?)null,
                DateFrom = DateFrom,
                DateTo = DateTo
            };
        }

        private async Task ExecuteBusyOperationAsync(Func<Task> operation)
        {
            try
            {
                IsBusy = true;
                await operation();
            }
            catch (InvalidOperationException ex)
            {
                _dialogService.ShowWarning(ex.Message);
            }
            catch (Exception ex)
            {
                _dialogService.ShowError(ex.Message);
            }
            finally
            {
                IsBusy = false;
            }
        }

        private async Task LoadFiltersAsync()
        {
            var customersTask = Task.Run(delegate { return _service.LoadCustomerItems(); });
            var measurementAreasTask = Task.Run(delegate { return _service.LoadMeasurementAreaItems(); });

            await Task.WhenAll(customersTask, measurementAreasTask);

            ApplyLookupItems(CustomerItems, customersTask.Result, "Все заказчики");
            ApplyLookupItems(MeasurementAreaItems, measurementAreasTask.Result, "Все области измерений");

            if (!CustomerItems.Any(delegate(DirectoryLookupItem item) { return item.Id == SelectedCustomerId; }))
            {
                SelectedCustomerId = 0;
            }

            if (!MeasurementAreaItems.Any(delegate(DirectoryLookupItem item) { return item.Id == SelectedMeasurementAreaId; }))
            {
                SelectedMeasurementAreaId = 0;
            }
        }

        private static void ApplyLookupItems(ObservableCollection<DirectoryLookupItem> target, System.Collections.Generic.IReadOnlyList<DirectoryLookupItem> source, string allItemText)
        {
            target.Clear();
            target.Add(new DirectoryLookupItem { Id = 0, Name = allItemText });

            foreach (var item in source)
            {
                target.Add(item);
            }
        }

        private async Task RefreshCoreAsync()
        {
            var filter = BuildFilter();
            var report = await Task.Run(delegate { return _service.LoadReport(filter); });

            Summary = report.Summary ?? new VerificationReportSummary();

            CustomerRows.Clear();
            foreach (var row in report.CustomerRows ?? Array.Empty<VerificationReportCustomerRow>())
            {
                CustomerRows.Add(row);
            }

            MeasurementAreaRows.Clear();
            foreach (var row in report.MeasurementAreaRows ?? Array.Empty<VerificationReportMeasurementAreaRow>())
            {
                MeasurementAreaRows.Add(row);
            }

            UpdateStatus();
        }

        private async void RefreshAsync()
        {
            await ExecuteBusyOperationAsync(RefreshCoreAsync);
        }

        private void UpdateStatus()
        {
            StatusText = string.Format(
                "Заказчиков в своде: {0}. Видов измерений в своде: {1}. Поверено: {2}. Годен: {3}. Забракован: {4}.",
                CustomerRows.Count,
                MeasurementAreaRows.Count,
                Summary == null ? 0 : Summary.TotalCount,
                Summary == null ? 0 : Summary.GoodCount,
                Summary == null ? 0 : Summary.RejectedCount);
        }
    }
}
