// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using System.Threading.Tasks;
using Microsoft.PowerToys.Run.Plugin.Service.Properties;
using Wox.Infrastructure;
using Wox.Plugin;
using Wox.Plugin.Logger;

namespace Microsoft.PowerToys.Run.Plugin.Service.Helpers
{
    public static class ServiceHelper
    {
        public static IEnumerable<Result> Search(string search, string icoPath, PluginInitContext context)
        {
            var services = ServiceController.GetServices();

            return services
                .Where(s => s.DisplayName.StartsWith(search, StringComparison.OrdinalIgnoreCase) || s.ServiceName.StartsWith(search, StringComparison.OrdinalIgnoreCase) || GetResultTitle(s).StartsWith(search, StringComparison.OrdinalIgnoreCase))
                .Select(s =>
                {
                    ServiceResult serviceResult = new ServiceResult(s);
                    Func<ActionContext, bool> serviceAction;
                    if (serviceResult.IsRunning)
                    {
                        serviceAction = _ =>
                        {
                            Task.Run(() => ServiceHelper.ChangeStatus(serviceResult, Action.Stop, context.API));
                            return true;
                        };
                    }
                    else
                    {
                        serviceAction = _ =>
                        {
                            Task.Run(() => ServiceHelper.ChangeStatus(serviceResult, Action.Start, context.API));
                            return true;
                        };
                    }

                    return new Result
                    {
                        Title = ServiceHelper.GetResultTitle(s),
                        SubTitle = ServiceHelper.GetResultSubTitle(s),
                        IcoPath = icoPath,
                        ContextData = serviceResult,
                        Action = serviceAction,
                    };
                });
        }

        public static void ChangeStatus(ServiceResult serviceResult, Action action, IPublicAPI contextAPI)
        {
            if (serviceResult == null)
            {
                throw new ArgumentNullException(nameof(serviceResult));
            }

            if (contextAPI == null)
            {
                throw new ArgumentNullException(nameof(contextAPI));
            }

            try
            {
                var info = new ProcessStartInfo
                {
                    FileName = "net",
                    Verb = "runas",
                    UseShellExecute = true,
                    WindowStyle = ProcessWindowStyle.Hidden,
                };

                if (action == Action.Start)
                {
                    info.Arguments = string.Join(' ', "start", serviceResult.ServiceName);
                }
                else if (action == Action.Stop)
                {
                    info.Arguments = string.Join(' ', "stop", serviceResult.ServiceName);
                }
                else if (action == Action.Restart)
                {
                    info.FileName = "cmd";
                    info.Arguments = string.Join(' ', "/c net stop", serviceResult.ServiceName, "&&", "net start", serviceResult.ServiceName);
                }

                var process = Process.Start(info);
                process.WaitForExit();
                var exitCode = process.ExitCode;

                if (exitCode == 0)
                {
                    contextAPI.ShowNotification(GetLocalizedMessage(action), serviceResult.DisplayName);
                }
                else
                {
                    contextAPI.ShowNotification(GetLocalizedErrorMessage(action), serviceResult.DisplayName);
                    Log.Error($"The command returned {exitCode}", MethodBase.GetCurrentMethod().DeclaringType);
                }
            }
            catch (Win32Exception ex)
            {
                Log.Error(ex.Message, MethodBase.GetCurrentMethod().DeclaringType);
            }
        }

        public static void OpenServices()
        {
            Helper.OpenInShell("services.msc");
        }

        private static string GetResultTitle(ServiceController serviceController)
        {
            if (serviceController == null)
            {
                throw new ArgumentNullException(nameof(serviceController));
            }

            var suffix = $"({serviceController.ServiceName})";
            return serviceController.DisplayName.EndsWith(suffix, StringComparison.CurrentCulture) ? serviceController.DisplayName : $"{serviceController.DisplayName} {suffix}";
        }

        private static string GetResultSubTitle(ServiceController serviceController)
        {
            if (serviceController == null)
            {
                throw new ArgumentNullException(nameof(serviceController));
            }

            return $"{Resources.wox_plugin_service_status}: {GetLocalizedStatus(serviceController.Status)} - {Resources.wox_plugin_service_startup}: {GetLocalizedStartType(serviceController.StartType)}";
        }

        private static string GetLocalizedStatus(ServiceControllerStatus status)
        {
            if (status == ServiceControllerStatus.Stopped)
            {
                return Resources.wox_plugin_service_stopped;
            }
            else if (status == ServiceControllerStatus.StartPending)
            {
                return Resources.wox_plugin_service_start_pending;
            }
            else if (status == ServiceControllerStatus.StopPending)
            {
                return Resources.wox_plugin_service_stop_pending;
            }
            else if (status == ServiceControllerStatus.Running)
            {
                return Resources.wox_plugin_service_running;
            }
            else if (status == ServiceControllerStatus.ContinuePending)
            {
                return Resources.wox_plugin_service_continue_pending;
            }
            else if (status == ServiceControllerStatus.PausePending)
            {
                return Resources.wox_plugin_service_pause_pending;
            }
            else if (status == ServiceControllerStatus.Paused)
            {
                return Resources.wox_plugin_service_paused;
            }
            else
            {
                return status.ToString();
            }
        }

        private static string GetLocalizedStartType(ServiceStartMode startMode)
        {
            if (startMode == ServiceStartMode.Boot)
            {
                return Resources.wox_plugin_service_start_mode_boot;
            }
            else if (startMode == ServiceStartMode.System)
            {
                return Resources.wox_plugin_service_start_mode_system;
            }
            else if (startMode == ServiceStartMode.Automatic)
            {
                return Resources.wox_plugin_service_start_mode_automatic;
            }
            else if (startMode == ServiceStartMode.Manual)
            {
                return Resources.wox_plugin_service_start_mode_manual;
            }
            else if (startMode == ServiceStartMode.Disabled)
            {
                return Resources.wox_plugin_service_start_mode_disabled;
            }
            else
            {
                return startMode.ToString();
            }
        }

        private static string GetLocalizedMessage(Action action)
        {
            if (action == Action.Start)
            {
                return Resources.wox_plugin_service_started_notification;
            }
            else if (action == Action.Stop)
            {
                return Resources.wox_plugin_service_stopped_notification;
            }
            else if (action == Action.Restart)
            {
                return Resources.wox_plugin_service_restarted_notification;
            }
            else
            {
                return string.Empty;
            }
        }

        private static string GetLocalizedErrorMessage(Action action)
        {
            if (action == Action.Start)
            {
                return Resources.wox_plugin_service_start_error_notification;
            }
            else if (action == Action.Stop)
            {
                return Resources.wox_plugin_service_stop_error_notification;
            }
            else if (action == Action.Restart)
            {
                return Resources.wox_plugin_service_restart_error_notification;
            }
            else
            {
                return string.Empty;
            }
        }
    }
}
