using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Prism.Commands;
using Valg.EVA.Skanning.ContainerExtensions;
using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Resources;
using Valg.EVA.Skanning.Resources.Language;
using Valg.EVA.Skanning.ServicesModel.ServiceContracts.CountResult.Write;
using Valg.EVA.Skanning.UserInterfaceElements.LightBox;
using Valg.EVA.Skanning.UserInterfaceElements.Spinner;
using Valg.EVA.Skanning.UserInterfaceElements.MessageBox;
using Valg.EVA.Skanning.ServicesModel.Internal;
using ReactiveUI;
using Valg.EVA.Skanning.Contracts;
using Valg.EVA.Skanning.JobManagement.Startup.Data;
using Valg.EVA.Skanning.JobManagement.Startup.UI.Duplicates;
using Valg.EVA.Skanning.UserInterfaceElements.Commands;

namespace Valg.EVA.Skanning.JobManagement.Startup.UI.Counting
{
    public class CountingViewModel : ReactiveViewModel, ICountingViewModel
    {
        private readonly IDataModel dataModel;
        private readonly IJobManagementApplication jobManagementApplication;
        private readonly ITypedFactory typedFactory;
        private readonly IDiagnosticsConfiguration diagnosticsConfiguration;
        private CountingModel model;

        public CountingViewModel(IDataModel dataModel, 
            IJobManagementApplication jobManagementApplication, 
            ITypedFactory typedFactory, 
            IDiagnosticsConfiguration diagnosticsConfiguration)
        {
            this.dataModel = dataModel;
            this.jobManagementApplication = jobManagementApplication;
            this.typedFactory = typedFactory;
            this.diagnosticsConfiguration = diagnosticsConfiguration;

            CreateResultCommand = GuardedCommand.CreateForAsync(CreateResult);
            DeleteCountingCommand = GuardedCommand.Create(DeleteCounting);
            ResolveDuplicatesCommand = GuardedCommand.Create(ShowResolveDuplicatesView);
        }

        private void ShowResolveDuplicatesView()
        {
            var boxCountings = dataModel.GetBoxCountings(Model.Id);

            var viewModel = typedFactory.Create<ResolveDuplicatesPopupViewModel>();
            viewModel.Initialize(boxCountings, model.Id);

            var view = typedFactory.Create<ResolveDuplicatesPopupView>();
            view.Initialize(viewModel);

            var eventArgs = new LightBoxEventArgs(view);

            PublishEvent<ShowLightBoxEvent, LightBoxEventArgs>(eventArgs);

            if (viewModel.ActionToTake != ResolveDuplicateAction.NoActionSelected)
            {
                var resolveResult = jobManagementApplication.ResolveDuplicates(Model.Id, Model.CountingEmlAreaContextId,
                    viewModel.GetBoxCountingsToDelete(),
                    viewModel.GetBoxCountingsToKeep());

                dataModel.LoadData();

                if (!resolveResult.DuplicatesResolvedSuccessfully)
                {
                    var errorDialog = typedFactory.Create<IMessageBox>();
                    var allBoxNumbersFailing =
                        resolveResult.BarcodesForBoxCountingFailedStatusUpdate.Select(b => b.GetBoxNumberFromBarcodeLong())
                            .Union(resolveResult.BarcodesForBoxCountingsFailedDelete.Select(b => b.GetBoxNumberFromBarcodeLong()))
                            .OrderBy(b => b)
                            .ToArray();

                    var messageText = allBoxNumbersFailing.Length > 0 ? JobManagementLanguageString.DetailsBoxesUnableToCompleteDuplicateHandling + string.Join(", ", allBoxNumbersFailing) : null;

                    errorDialog.Initialize(JobManagementLanguageString.ErrorOccuredDuringCompletingDuplicateHandling, messageText,
                        JobManagementLanguageString.PossibleSolutionsWhenDuplicateHandlingFails,
                        MessageBoxType.Warning,
                        resolveResult.PossibleException);
                    PublishEvent<ShowLightBoxEvent, LightBoxEventArgs>(new LightBoxEventArgs(errorDialog as Window));
                }
            }
        }

        private void DeleteCounting()
        {
            ShowDeleteCountingPopupView();
        }

        private void LoadBoxCountings()
        {
            var boxCountings = dataModel.GetBoxCountings(Model.Id).CreateDerivedCollection(CreateBoxCountingViewModel);

            boxCountings.BubbleCollection(
                this,
                t => t.IsCompleteCountingButtonEnabled,
                t => t.HasCountings,
                t => t.IsResolveDuplicatesButtonEnabled,
                t => t.State,
                t => t.Status);

            BoxCountings = boxCountings;
        }

        private BoxCountingViewModel CreateBoxCountingViewModel(BoxCountingDetailsModel boxCountingDetailsModel)
        {
            var viewModel = typedFactory.Create<BoxCountingViewModel>();
            viewModel.Initialize(boxCountingDetailsModel);
            viewModel.Bubble(
                s => s.BoxCountingState,
                this,
                t => t.IsCompleteCountingButtonEnabled,
                t => t.State);
            viewModel.Bubble(
                s => s.Model.BallotsToVerify,
                this,
                t => t.IsCompleteCountingButtonEnabled);

            viewModel.Bubble(
                s => s.HasDuplicates,
                this,
                t => t.IsResolveDuplicatesButtonEnabled);

            return viewModel;
        }

        private async Task CreateResult()
        {
            var runner = typedFactory.Create<ProcessRunner<GenerateCountingResultResult>>();

            GenerateCountingResultResult serviceResult;
            try
            {
                serviceResult = await runner.Run(() => jobManagementApplication.CreateResult(Model), string.Empty, JobManagementLanguageString.PleaseWaitWhileCountingIsCompleting, isSpinnerBlocking: false);
            }
            finally
            {
                jobManagementApplication.LoadCountingAndTransfersInDatamodel();
            }

            ShowErrorMessageIfError(serviceResult);
        }

        private void ShowErrorMessageIfError(GenerateCountingResultResult serviceResult)
        {
            if (serviceResult.HasFailed)
            {
                var resultDialog = typedFactory.Create<IMessageBox>();

                resultDialog.Initialize(JobManagementLanguageString.SomethingWentWrongWhileCompletingCounting, null, JobManagementLanguageString.PossibleSolutionsWhenCompleteCountingFailed,
                    MessageBoxType.Warning, serviceResult.Exception != null ? new[] { serviceResult.Exception } : null);

                var eventArgs = new LightBoxEventArgs(resultDialog as Window);
                PublishEvent<ShowLightBoxEvent, LightBoxEventArgs>(eventArgs);
            }
        }

        public string Name { get { return Model.Name; } }
        public CountingState State { get { return Model.CountingState; } }
        public string Status
        {
            get
            {
                if (!BoxCountings.Any())
                    return JobManagementLanguageString.BoxCounting_WaitingForBarcode;

                return JobManagementLanguageString.ResourceManager.GetEnumString(State);
            }
        }
        public bool IsCompleteCountingButtonEnabled
        {
            get
            {
                if (BoxCountings.Any())
                {
                    var areAllLockedOrEmpty = BoxCountings.All(bc => bc.BoxCountingState == BoxCountingState.Locked || bc.BoxCountingState == BoxCountingState.Empty);
                    var areAllEmpty = BoxCountings.All(bc => bc.BoxCountingState == BoxCountingState.Empty);

                    if (areAllEmpty)
                    {
                        return false;
                    }
                    if (areAllLockedOrEmpty)
                    {
                        return true;
                    }
                    else
                    {
                        var allAreReadyForVerify = BoxCountings.All(bc => bc.BoxCountingState == BoxCountingState.ReadyForVerify || bc.BoxCountingState == BoxCountingState.Empty);
                        if (Model.IsFinalCounting)
                        {
                            return allAreReadyForVerify && BoxCountings.All(bc => bc.Model.BallotsToVerify == 0);
                        }
                        else
                        {
                            return allAreReadyForVerify;
                        }
                    }
                }

                return false;
            }
        }

        public bool IsResolveDuplicatesButtonEnabled
        {
            get { return BoxCountings.Any(p => p.HasDuplicates); }
        }
        public CountingModel Model => model;

        public bool IsGenerateBarcodeButtonEnabled => diagnosticsConfiguration.IsGenerateBarcodeUtilityEnabled;

        public void SetModel(CountingModel countingModel)
        {
            model = countingModel;

            ConfigureBubbling();
            LoadBoxCountings();

            IsOpen = !countingModel.IsCountingComplete;
        }
        private void ConfigureBubbling()
        {
            Model.Bubble(
                s => s.CountingState,
                this,
                t => t.State);
            this.Bubble(
                s => s.State,
                t => t.Status,
                t => t.IsDeleteCountingButtonEnabled);
        }

        public bool IsDeleteCountingButtonEnabled => (State == CountingState.Active || State == CountingState.Locked);

        public ICommand CreateResultCommand { get; set; }
        public ICommand DeleteCountingCommand { get; set; }
        public ICommand ResolveDuplicatesCommand { get; set; }

        public ICommand GenerateBarcodesCommand => new DelegateCommand(() =>
        {
            var viewModel =  typedFactory.Create<GenerateBarcodesViewModel>();
            viewModel.Initialize(model);
            var eventArgs = new LightBoxEventArgs(typeof(GenerateBarcodesView), viewModel);
            PublishEvent<ShowLightBoxEvent, LightBoxEventArgs>(eventArgs);
        });

        public bool HasCountings => BoxCountings.Any();

        public IReactiveDerivedList<BoxCountingViewModel> BoxCountings { get; set; }
        public bool IsOpen { get; set; }
        public string ToVerifyOrInDoubtColumnTitle
        {
            get
            {
                if (model.IsFinalCounting)
                {
                    return JobManagementLanguageString.BoxCounting__ColumnTitle_ToVerify;
                }

                return JobManagementLanguageString.BoxCounting__ColumnTitle_InDoubt;
            }
        }

        private void ShowDeleteCountingPopupView()
        {
            var viewModel = new DeleteCountingPopupViewModel(Name, Model, jobManagementApplication);
            var eventArgs = new LightBoxEventArgs(typeof(DeleteCountingPopupView), viewModel);
            PublishEvent<ShowLightBoxEvent, LightBoxEventArgs>(eventArgs);
        }
    }
}

