﻿using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using ApiKickstart.Commands;
using ApiKickstart.CommandResults;
using ApiKickstart.Domain;
using MediatR;
using ApiKickstart.DataModel.DbContexts;
using static ApiKickstart.Domain.DomainEnumerations;
using Polly;
using AutoMapper;
using Polly.Registry;

namespace ApiKickstart.QueryHandlers
{
    public class CreateWidgetCommandHandler : IRequestHandler<CreateWidgetCommand, CreateWidgetCommandResult>
    {
        private readonly ILogger<CreateWidgetCommandHandler> _logger;
        private readonly WidgetDbContext _widgetDb;
        private readonly IMapper _mapper;
        private readonly IReadOnlyPolicyRegistry<string> _policyRegistry;

        public CreateWidgetCommandHandler(ILogger<CreateWidgetCommandHandler> logger, WidgetDbContext db, IMapper mapper, IReadOnlyPolicyRegistry<string> registry)
        {
            _logger = logger;
            _widgetDb = db;
            _mapper = mapper;
            _policyRegistry = registry;
        }

        public async Task<CreateWidgetCommandResult> Handle(CreateWidgetCommand command, CancellationToken cancellationToken)
        {
            var commandResult = new CreateWidgetCommandResult();
            try
            {
                var newWidget = _mapper.Map<DataModel.Entities.Widget>(command);
                var policy = _policyRegistry.Get<IAsyncPolicy<int>>("DbCommand");
                var policyResult = await policy
                    .ExecuteAndCaptureAsync(async () =>
                  {
                      using (_widgetDb)
                      {
                          _widgetDb.Widgets.Add(newWidget);
                          return await _widgetDb.SaveChangesAsync();
                      }
                  });
                if (policyResult.Outcome == OutcomeType.Failure) return ServiceUnavailableCommandResult();
                
                commandResult.Widget = new Widget()//todo - automapper
                {
                    Id = newWidget.Id,
                    WidgetName = command.WidgetName
                };
                commandResult.ResultStatus = CQRS.CommandResultStatus.SuccessfullyProcessed;
                _logger.LogTrace("{result} has completed. {widget} returned.", nameof(CreateWidgetCommandResult), commandResult.Widget);
            }
            catch (Exception e)
            {
                _logger.LogError(new EventId(), e, "Oops! Ran into an error!");
                commandResult.ResultStatus = CQRS.CommandResultStatus.CriticalError;
            }
            finally
            {
                //log?

            }
            return commandResult;
        }

        private CreateWidgetCommandResult ServiceUnavailableCommandResult()
        {
            _logger.LogError("Policy outcome was failure."); // warning?
             return new CreateWidgetCommandResult() { ResultStatus = CQRS.CommandResultStatus.ServiceUnavailable };
        }
    }
}
