using Insight.Setup.Constants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Runtime.Versioning;

namespace Insight.Setup.Services
{
    [SupportedOSPlatform("windows")]
    internal class SetupService : BackgroundService
    {
        private readonly Uri _uri;

        private readonly HttpClient _httpClient;
        private readonly IHostApplicationLifetime _lifetime;
        private readonly ILogger<SetupService> _logger;

        public SetupService(HttpClient httpClient, IHostApplicationLifetime lifetime, IConfiguration configuration, ILogger<SetupService> logger)
        {
            _httpClient = httpClient;
            _lifetime = lifetime;
            _uri = configuration.GetValue<Uri?>("api") ?? new Uri("https://insight.webmatic.de/api"); //throw new Exception($"api value not set (appsettings)");
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            _logger.LogTrace("ExecuteAsync");

            Console.WriteLine("1: Install");
            Console.WriteLine("2: Update");
            Console.WriteLine("3: Uninstall");

            Console.WriteLine("\n");

            var key = Console.ReadKey();

            Console.Clear();

            try
            {
                switch (key.Key)
                {
                    case ConsoleKey.NumPad1:
                    case ConsoleKey.D1:
                        {
                            await InstallAsync(cancellationToken);
                            break;
                        }
                    case ConsoleKey.NumPad2:
                    case ConsoleKey.D2:
                        {
                            await UpdateAsync(cancellationToken);
                            break;
                        }
                    case ConsoleKey.NumPad3:
                    case ConsoleKey.D3:
                        {
                            await UninstallAsync();
                            break;
                        }
                    default:
                        {
                            Console.WriteLine("invalid selection");
                            break;
                        }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            finally
            {
                _lifetime.StopApplication();
            }
        }

        private async ValueTask InstallAsync(CancellationToken cancellationToken)
        {
            Console.WriteLine("Install Runtime");
            await InstallRuntimeAsync(cancellationToken);

            Console.WriteLine("Install ASP Runtime");
            await InstallAspRuntimeAsync(cancellationToken);

            // UPDATER
            Console.WriteLine("Install Updater");

            var updaterResult = await Deployment.Windows.Service.InstallAsync(
                _httpClient,
                Deploy.GetUpdateHref(_uri, Deploy.Updater.Name),
                Deploy.GetAppExecutable(Deploy.Updater.Name),
                Deploy.Updater.ServiceName,
                Deploy.Updater.Description,
                Deploy.Updater.Description,
                true,
                cancellationToken);

            Console.WriteLine($"Updater: {updaterResult.Success}");
            Console.WriteLine($"Updater: {string.Concat(updaterResult.Errors)}");

            // AGENT
            Console.WriteLine("Install Agent");

            var agentResult = await Deployment.Windows.Service.InstallAsync(
                _httpClient,
                Deploy.GetUpdateHref(_uri, Deploy.Agent.Name),
                Deploy.GetAppExecutable(Deploy.Agent.Name),
                Deploy.Agent.ServiceName,
                Deploy.Agent.Description,
                Deploy.Agent.Description,
                true,
                cancellationToken);

            Console.WriteLine($"Agent: {agentResult}");
            Console.WriteLine($"Agent: {string.Concat(agentResult.Errors)}");
        }

        private async ValueTask UpdateAsync(CancellationToken cancellationToken)
        {
            // UPDATER
            Console.WriteLine("Update Updater");

            var updateResult = await Deployment.Windows.Service.UpdateAsync(
                _httpClient,
                Deploy.GetUpdateHref(_uri, Deploy.Updater.Name),
                Deploy.GetAppExecutable(Deploy.Updater.Name),
                Deploy.Updater.ServiceName,
                cancellationToken);

            Console.WriteLine($"Result: {updateResult}");

            // AGENT
            Console.WriteLine("Update Agent");

            var agentResult = await Deployment.Windows.Service.UpdateAsync(
                _httpClient,
                Deploy.GetUpdateHref(_uri, Deploy.Agent.Name),
                Deploy.GetAppExecutable(Deploy.Agent.Name),
                Deploy.Agent.ServiceName,
                cancellationToken);

            Console.WriteLine($"Result: {agentResult}");
        }

        private static async ValueTask UninstallAsync()
        {
            // UPDATER
            Console.WriteLine("Uninstall Updater");

            var updaterResult = await Deployment.Windows.Service.UninstallAsync(Deploy.Updater.ServiceName);
            Console.WriteLine($"Result: {updaterResult}");

            // AGENT
            Console.WriteLine("Uninstall Agent");

            var agentResult = await Deployment.Windows.Service.UninstallAsync(Deploy.Agent.ServiceName);
            Console.WriteLine($"Result: {agentResult}");
        }

        private async ValueTask InstallRuntimeAsync(CancellationToken cancellationToken)
        {
            // try get dotnet folders
            var coreDirectory = Deploy.Runtime.Core.Directory;

            // Runtime Installation Check...
            if (coreDirectory.Exists && coreDirectory.EnumerateDirectories().Any(x => x.Name == Deploy.Runtime.Core.Version)) return;

            // Downloading Runtime...
            var queryResult = await _httpClient.GetAsync(Deploy.Runtime.Core.Download, cancellationToken);

            var tempDir = Directory.CreateTempSubdirectory();

            await File.WriteAllBytesAsync(
                $@"{tempDir.FullName}/runtime.exe",
                await queryResult.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken);

            // Installing Runtime...
            var runtimeFile = new FileInfo($@"{tempDir.FullName}/runtime.exe");

            using var process = new Process()
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = runtimeFile.FullName,
                    ArgumentList =
                    {
                        @"/install",
                        @"/quiet",
                        @"/norestart"
                    },
                    UseShellExecute = false,
                    RedirectStandardOutput = true
                }
            };

            process.Start();
            await process.WaitForExitAsync(cancellationToken);

            if (runtimeFile.Exists) runtimeFile.Delete();
        }

        private async ValueTask InstallAspRuntimeAsync(CancellationToken cancellationToken)
        {
            // try get dotnet folders
            var aspDirectory = Deploy.Runtime.Asp.Directory;

            // Runtime Installation Check...
            if (aspDirectory.Exists && aspDirectory.EnumerateDirectories().Any(x => x.Name == Deploy.Runtime.Asp.Version)) return;

            // Downloading Runtime...
            var queryResult = await _httpClient.GetAsync(Deploy.Runtime.Asp.Download, cancellationToken);

            var tempDir = Directory.CreateTempSubdirectory();

            await File.WriteAllBytesAsync(
                $@"{tempDir.FullName}/runtime.exe",
                await queryResult.Content.ReadAsByteArrayAsync(cancellationToken), cancellationToken);

            // Installing Runtime...
            var runtimeFile = new FileInfo($@"{tempDir.FullName}/runtime.exe");

            using var process = new Process()
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = runtimeFile.FullName,
                    ArgumentList =
                    {
                        @"/install",
                        @"/quiet",
                        @"/norestart"
                    },
                    UseShellExecute = false,
                    RedirectStandardOutput = true
                }
            };

            process.Start();
            await process.WaitForExitAsync(cancellationToken);

            if (runtimeFile.Exists) runtimeFile.Delete();
        }
    }
}