using System;
using System.Diagnostics;
using System.Linq;
using System.Management;

namespace NetworkResurrector.Server.Application.Services
{
    public class ShutdownService : IShutdownService
    {
        /// <summary>
        /// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb491003(v=technet.10)?redirectedfrom=MSDN
        /// </summary>
        /// <param name="ipAddress"></param>
        /// <returns></returns>
        public string ShutdownMachine(string ipAddressOrMachineName)
        {
            Process commandProcess = new Process();

            commandProcess.StartInfo.FileName = "cmd.exe";
            commandProcess.StartInfo.UseShellExecute = false;
            commandProcess.StartInfo.CreateNoWindow = true;
            commandProcess.StartInfo.RedirectStandardError = true;
            commandProcess.StartInfo.RedirectStandardInput = true;
            commandProcess.StartInfo.RedirectStandardOutput = true;
            commandProcess.Start();
            commandProcess.StandardInput.WriteLine($"shutdown /r /m {ipAddressOrMachineName} /t 200 /f");
            commandProcess.StandardInput.WriteLine("exit");

            for (; !commandProcess.HasExited;) //wait executed  
            {
                System.Threading.Thread.Sleep(1);
            }

            string errorOutput = commandProcess.StandardError.ReadToEnd();
            string output = commandProcess.StandardOutput.ReadToEnd();

            if (commandProcess != null)
                commandProcess.Dispose();

            return $"Output:{Environment.NewLine}{output}{Environment.NewLine}{Environment.NewLine}Errors:{Environment.NewLine}{errorOutput}";
        }

        public string ShutdownMachineWithManagementScope(string ipAddressOrMachineName)
            => ShutdownMachineWithManagementScope(ipAddressOrMachineName, false, null, null, null);

        /// <summary>
        /// https://msdn.microsoft.com/en-us/library/system.management.managementscope(v=vs.110).aspx 
        /// </summary>
        /// <param name="ipAddress"></param>
        /// <param name="user"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public string ShutdownMachineWithManagementScope(string ipAddressOrMachineName, string user, string password, string domain)
            => ShutdownMachineWithManagementScope(ipAddressOrMachineName, true, user, password, domain);

        private string ShutdownMachineWithManagementScope(string ipAddressOrMachineName, bool useSpecificCredentials, string user, string password, string domain)
        {
            Validate(ipAddressOrMachineName, useSpecificCredentials, user, password, domain);
            /* Build an options object for the remote connection 
             * if you plan to connect to the remote computer with a different user name and password than the one you are currently using

             ConnectionOptions options = new ConnectionOptions();

             * and then set the options.Username and options.Password properties to the correct values
             * and also set options.Authority = "ntlmdomain:DOMAIN";
             * and replace DOMAIN with the remote computer's domain.  You can also use Kerberos instead of ntlmdomain.
             */

            if (!OperatingSystem.IsWindows())
            {
                throw new PlatformNotSupportedException("ShutdownMachineWithManagementScope is only supported on Windows.");
            }

            ConnectionOptions options = new ConnectionOptions
            {
                Username = user,
                Password = password,
                Authority = $"ntlmdomain:{domain}"
            };

            // Make a connection to a remote computer.  
            ManagementScope scope = new ManagementScope($"\\\\{ipAddressOrMachineName}\\root\\cimv2", options);
            scope.Connect();

            //Query system for Operating System information
            ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_OperatingSystem");

            ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
            ManagementObjectCollection queryCollection = searcher.Get();

            foreach (ManagementObject obj in queryCollection)
            {
                // Display the remote computer information
                Console.WriteLine("Computer Name : {0}", obj["csname"]);
                Console.WriteLine("Windows Directory : {0}", obj["WindowsDirectory"]);
                Console.WriteLine("Operating System: {0}", obj["Caption"]);
                Console.WriteLine("Version: {0}", obj["Version"]);
                Console.WriteLine("Manufacturer : {0}", obj["Manufacturer"]);

                obj.InvokeMethod("ShutDown", null); //shutdown  
            }

            return "x";
        }

        private void Validate(string ipAddressOrMachineName, bool useSpecificCredentials, string user, string password, string domain)
        {
            if (string.IsNullOrEmpty(ipAddressOrMachineName))
                throw new ArgumentException("The provided ipAddressOrMachineName input is null or empty.", nameof(ipAddressOrMachineName));

            if (useSpecificCredentials && OneOfStringsIsNullOrEmpty(user, password, domain))
                throw new ArgumentException($"One of user, password or domain inputs is null or empty.");
        }

        private bool OneOfStringsIsNullOrEmpty(params string[] inputs)
        {
            return inputs.Any(z => string.IsNullOrEmpty(z));
        }
    }
}
