using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using NetworkResurrector.Agent.PublishedLanguage.Dto;
using NetworkResurrector.Agent.Wrapper.Services;
using NetworkResurrector.Api.Application.Extensions;
using NetworkResurrector.Api.Application.Services.Abstractions;
using NetworkResurrector.Api.Domain.Constants;
using NetworkResurrector.Api.Domain.Repositories;
using NetworkResurrector.Api.PublishedLanguage.Commands;
using NetworkResurrector.Api.PublishedLanguage.Events;
using NetworkResurrector.Server.Wrapper.Services;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace NetworkResurrector.Api.Application.CommandHandlers
{
    public class ShutdownMachineHandler : IRequestHandler<ShutdownMachine, MachineShutdown>
    {
        private readonly ILogger<ShutdownMachineHandler> _logger;
        private readonly IResurrectorService _resurrectorService;
        private readonly INetworkRepository _repository;
        private readonly IResurrectorAgentService _resurrectorAgentService;
        private readonly IConfiguration _configuration;
        private readonly INotificationService _notificationService;

        public ShutdownMachineHandler(ILogger<ShutdownMachineHandler> logger, IResurrectorService resurrectorService, INetworkRepository repository, IResurrectorAgentService resurrectorAgentService, IConfiguration configuration, INotificationService notificationService)
        {
            _logger=logger;
            _resurrectorService=resurrectorService;
            _repository=repository;
            _resurrectorAgentService=resurrectorAgentService;
            _configuration=configuration;
            _notificationService=notificationService;
        }

        public async Task<MachineShutdown> Handle(ShutdownMachine command, CancellationToken cancellationToken)
        {
            _logger.LogDebug($"Start shutdown machine {command.MachineId}");
            var machine = await _repository.GetMachine(command.MachineId);
            var powerConfiguration = await _repository.GetPowerActionConfiguration(command.MachineId, PowerActions.SHUTDOWN);

            //log activity

            var ipAddressOrMachineName = machine.IPv4Address ?? machine.MachineName;
            MachineShutdown result;

            var performer = powerConfiguration.Performer.PerformerCode;
            switch (performer)
            {
                case PowerActionPerformers.NETWORK_RESURRECTOR_SERVER:
                    if (command.Delay.HasValue || command.Force)
                        _logger.LogWarning($"Shutdown options are not implemented for '{PowerActionPerformers.NETWORK_RESURRECTOR_SERVER}' performer.");

                    var shutdownResult = await _resurrectorService.Shutdown(ipAddressOrMachineName);
                    result = new MachineShutdown(shutdownResult.Success, shutdownResult.Status);
                    break;

                case PowerActionPerformers.NETWORK_RESURRECTOR_AGENT:
                    if (machine.Agent == null)
                        throw new Exception($"Cannot use network resurrector agent as shutdown performer for machine '{ipAddressOrMachineName}' because it is not configured.");

                    var owner = new ActionOwner(_configuration.GetValue<string>("Service:Code"));
                    var options = new ActionOptions(command.Delay ?? 0, command.Force);
                    var shutdownResultByAgent = await _resurrectorAgentService.Shutdown(ipAddressOrMachineName, machine.Agent.AgentPort, owner, options);
                    var status = shutdownResultByAgent.Success ? $"Machine '{ipAddressOrMachineName}' successfully shutdown." : $"Machine '{ipAddressOrMachineName}' could not be shutdown.";
                    result = new MachineShutdown(shutdownResultByAgent.Success, status);
                    break;

                default:
                    throw new Exception($"Power action performer {performer} is not implemented.");
            }

            _logger.LogDebug($"Machine {command.MachineId} shutdown finished. Success: {result.Success}; Status: {result.Status}");
            var notificationContext = machine.ToNotificationContext(result.Status, performer);
            await _notificationService.Notify(NotificationType.Shutdown, notificationContext, cancellationToken);

            return result;
        }
    }
}
