<commit_before>﻿// Copyright (c) <NAME> (CGO Conseils). All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Navigation;
using Deploy;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Tooling.Connector;
using XrmFramework.DeployUtils.Comparers;
using XrmFramework.DeployUtils.Configuration;
using XrmFramework.DeployUtils.Model;

namespace XrmFramework.DeployUtils
{
    public static class RegistrationHelper
    {
        private static List<PluginAssembly> _list = new List<PluginAssembly>();
        
        public static void RegisterPluginsAndWorkflows<TPlugin>(string projectName)
        {
            var xrmFrameworkConfigSection = ConfigHelper.GetSection();

            var projectConfig = xrmFrameworkConfigSection.Projects.OfType<ProjectElement>()
                .FirstOrDefault(p => p.Name == projectName);

            if (projectConfig == null)
            {
                var defaultColor = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"No reference to the project {projectName} has been found in the xrmFramework.config file.");
                Console.ForegroundColor = defaultColor;
                return;
            }

            var pluginSolutionUniqueName = projectConfig.TargetSolution;

            var connectionString = ConfigurationManager.ConnectionStrings[xrmFrameworkConfigSection.SelectedConnection].ConnectionString;

            Console.WriteLine($"You are about to deploy on {connectionString} organization. If ok press any key.");
            Console.ReadKey();
            Console.WriteLine("Connecting to CRM...");

            CrmServiceClient.MaxConnectionTimeout = TimeSpan.FromMinutes(10);

            var service = new CrmServiceClient(connectionString);

            service.OrganizationServiceProxy?.EnableProxyTypes();

            InitMetadata(service, pluginSolutionUniqueName);

            var pluginAssembly = typeof(TPlugin).Assembly;

            var pluginType = pluginAssembly.GetType("XrmFramework.Plugin");
            var customApiType = pluginAssembly.GetType("XrmFramework.CustomApi");
            var workflowType = pluginAssembly.GetType("XrmFramework.Workflow.CustomWorkflowActivity");

            var pluginList = new List<Plugin>();

            var pluginTypes = pluginAssembly.GetTypes().Where(t => pluginType.IsAssignableFrom(t) && !customApiType.IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract).ToList();

            var workflowTypes = pluginAssembly.GetTypes().Where(t => workflowType.IsAssignableFrom(t) && !t.IsAbstract && t.IsPublic).ToList();
            
            var customApiTypes = pluginAssembly.GetTypes().Where(t => customApiType.IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract).ToList();
            
            foreach (var type in pluginTypes)
            {
                dynamic pluginTemp;
                if (type.GetConstructor(new[] {typeof(string), typeof(string)}) != null)
                {
                    pluginTemp = Activator.CreateInstance(type, new object[] {null, null});
                }
                else
                {
                    pluginTemp = Activator.CreateInstance(type, new object[] { });
                }

                var plugin = Plugin.FromXrmFrameworkPlugin(pluginTemp, false);

                pluginList.Add(plugin);
            }

            foreach (var type in workflowTypes)
            {
                dynamic pluginTemp = Activator.CreateInstance(type, new object[] { });

                var plugin = Plugin.FromXrmFrameworkPlugin(pluginTemp, true);

                pluginList.Add(plugin);
            }

            var customApis = new List<CustomApi>();

            foreach (var type in customApiTypes)
            {
                dynamic customApiTemp;
                if (type.GetConstructor(new[] { typeof(string), typeof(string) }) != null)
                {
                    customApiTemp = Activator.CreateInstance(type, new object[] { null, null });
                }
                else
                {
                    customApiTemp = Activator.CreateInstance(type, new object[] { });
                }

                var customApi = CustomApi.FromXrmFrameworkCustomApi(customApiTemp, _publisher.CustomizationPrefix);

                customApis.Add(customApi);
            }


            var assembly = GetAssemblyByName(service, pluginAssembly.GetName().Name);

            var profilerAssembly = GetProfilerAssembly(service);

            var registeredPluginTypes = new List<PluginType>();
            var registeredCustomApis = new List<CustomApi>();
            var registeredCustomApiRequestParameters = new List<CustomApiRequestParameter>();
            var registeredCustomApiResponseProperties = new List<CustomApiResponseProperty>();
            var profiledSteps = new List<SdkMessageProcessingStep>();
            ICollection<SdkMessageProcessingStep> registeredSteps = Enumerable.Empty<SdkMessageProcessingStep>().ToList();

            var assemblyPath = pluginAssembly.Location;

            if (assembly == null)
            {
                Console.WriteLine("Registering assembly");

                assembly = GetAssemblyToRegister(pluginAssembly, assemblyPath);

                assembly.Id = service.Create(assembly);
            }
            else
            {
                Console.WriteLine("Updating plugin assembly");

                var updatedAssembly = new Entity("pluginassembly")
                {
                    Id = assembly.Id, 
                    ["content"] = Convert.ToBase64String(File.ReadAllBytes(assemblyPath))
                };

                registeredPluginTypes = GetRegisteredPluginTypes(service, assembly.Id).ToList();
                registeredCustomApis = GetRegisteredCustomApis(service, assembly.Id).ToList();
                registeredCustomApiRequestParameters = GetRegisteredCustomApiRequestParameters(service, assembly.Id).ToList();
                registeredCustomApiResponseProperties = GetRegisteredCustomApiResponseProperties(service, assembly.Id).ToList();

                registeredSteps = GetRegisteredSteps(service, assembly.Id);

                if (profilerAssembly != null)
                {
                    profiledSteps = GetRegisteredSteps(service, profilerAssembly.Id).ToList();
                }
                
                foreach (var registeredType in registeredPluginTypes)
                {
                    if (pluginList.All(p => p.FullName != registeredType.Name) && pluginList.Where(p => p.IsWorkflow).All(c => c.FullName != registeredType.TypeName) && customApis.All(c => c.FullName !=  registeredType.TypeName))
                    {
                        var registeredStepsForPluginType = registeredSteps.Where(s => s.EventHandler.Id == registeredType.Id).ToList();
                        foreach (var step in registeredStepsForPluginType)
                        {
                            service.Delete(SdkMessageProcessingStep.EntityLogicalName, step.Id);
                            registeredSteps.Remove(step);
                        }

                        foreach (var customApi in registeredCustomApis.Where(c => c.PluginTypeId?.Id == registeredType.Id))
                        {
                            service.Delete(customApi.LogicalName, customApi.Id);
                        }

                        service.Delete(PluginType.EntityLogicalName, registeredType.Id);
                    }
                }

                service.Update(updatedAssembly);
            }

            AddSolutionComponentToSolution(service, pluginSolutionUniqueName, assembly.ToEntityReference());

            var registeredImages = GetRegisteredImages(service, assembly.Id);

            Console.WriteLine();
            Console.WriteLine(@"Registering Plugins");

            foreach (var plugin in pluginList.Where(p => !p.IsWorkflow))
            {
                Console.WriteLine($@"  - {plugin.FullName}");

                var registeredPluginType = registeredPluginTypes.FirstOrDefault(p => p.Name == plugin.FullName);

                if (registeredPluginType == null)
                {
                    registeredPluginType = GetPluginTypeToRegister(assembly.Id, plugin.FullName);
                    registeredPluginType.Id = service.Create(registeredPluginType);
                }

                var registeredStepsForPluginType = registeredSteps.Where(s => s.EventHandler.Id == registeredPluginType.Id).ToList();

                var comparer = new SdkMessageStepComparer();

                foreach (var convertedStep in plugin.Steps)
                {
                    var stepToRegister = GetStepToRegister(registeredPluginType.Id, convertedStep);

                    var registeredStep = registeredStepsForPluginType.FirstOrDefault(s => comparer.Equals(s, stepToRegister));

                    if (registeredStep == null)
                    {
                        stepToRegister.Id = service.Create(stepToRegister);
                    }
                    else
                    {
                        registeredSteps.Remove(registeredStep);
                        stepToRegister.Id = registeredStep.Id;
                        if (comparer.NeedsUpdate(stepToRegister, registeredStep))
                        {
                            var profiledStep = profiledSteps.FirstOrDefault(p => p.Name.StartsWith(registeredStep.Name));

                            if (profiledStep != null)
                            {
                                service.Delete(profiledStep.LogicalName, profiledStep.Id);

                                service.Execute(new SetStateRequest
                                {
                                    EntityMoniker = registeredStep.ToEntityReference(),
                                    State = new OptionSetValue((int)SdkMessageProcessingStepState.Enabled),
                                    Status = new OptionSetValue(-1)
                                });
                            }

                            service.Update(stepToRegister);
                        }
                    }

                    AddSolutionComponentToSolution(service, pluginSolutionUniqueName, stepToRegister.ToEntityReference());

                    if (convertedStep.Message != Messages.Associate.ToString() && convertedStep.Message != Messages.Lose.ToString() && convertedStep.Message != Messages.Win.ToString())
                    {
                        var registeredPostImage = registeredImages.FirstOrDefault(i => i.Name == "PostImage" && i.SdkMessageProcessingStepId.Id == stepToRegister.Id);

                        if (convertedStep.PostImageUsed && convertedStep.Message != Messages.Delete.ToString())
                        {
                            if (registeredPostImage == null)
                            {
                                registeredPostImage = GetImageToRegister(service, stepToRegister.Id, convertedStep, false);
                                registeredPostImage.Id = service.Create(registeredPostImage);

                            }
                            else if (registeredPostImage.Attributes1 != convertedStep.JoinedPostImageAttributes)
                            {
                                registeredPostImage.Attributes1 = convertedStep.JoinedPostImageAttributes;
                                service.Update(registeredPostImage);
                            }
                        }
                        else if (registeredPostImage != null)
                        {
                            service.Delete(registeredPostImage.LogicalName, registeredPostImage.Id);
                        }

                        var registeredPreImage = registeredImages.FirstOrDefault(i => i.Name == "PreImage" && i.SdkMessageProcessingStepId.Id == stepToRegister.Id);

                        if (convertedStep.PreImageUsed)
                        {
                            if (registeredPreImage == null)
                            {
                                registeredPreImage = GetImageToRegister(service, stepToRegister.Id, convertedStep, true);
                                registeredPreImage.Id = service.Create(registeredPreImage);
                            }
                            else if (registeredPreImage.Attributes1 != convertedStep.JoinedPreImageAttributes)
                            {
                                registeredPreImage.Attributes1 = convertedStep.JoinedPreImageAttributes;
                                service.Update(registeredPreImage);
                            }
                        }
                        else if (registeredPreImage != null)
                        {
                            service.Delete(registeredPreImage.LogicalName, registeredPreImage.Id);
                        }
                    }
                }
            }

            Console.WriteLine();
            Console.WriteLine(@"Registering Custom Workflow activities");

            foreach (var customWf in pluginList.Where(p => p.IsWorkflow))
            {
                var registeredPluginType = registeredPluginTypes.FirstOrDefault(p => p.TypeName == customWf.FullName);

                Console.WriteLine($@"  - {customWf.FullName}");

                if (registeredPluginType == null)
                {
                    registeredPluginType = GetCustomWorkflowTypeToRegister(assembly.Id, customWf.FullName, customWf.DisplayName);
                    registeredPluginType.Id = service.Create(registeredPluginType);
                }
                if (registeredPluginType.Name != customWf.DisplayName)
                {
                    registeredPluginType.Name = customWf.DisplayName;
                    service.Update(registeredPluginType);
                }
            }

            Console.WriteLine();
            Console.WriteLine(@"Registering Custom Apis");
            
            var customApiEntityTypeCode = GetEntityTypeCode("customapi", service);
            var customApiParameterEntityTypeCode = GetEntityTypeCode("customapirequestparameter", service);
            var customApiResponseEntityTypeCode = GetEntityTypeCode("customapiresponseproperty", service);

            foreach (var customApi in customApis)
            {
                Console.WriteLine($@"  - {customApi.FullName}");

                var registeredPluginType = registeredPluginTypes.FirstOrDefault(p => p.Name == customApi.FullName);

                if (registeredPluginType == null)
                {
                    registeredPluginType = GetPluginTypeToRegister(assembly.Id, customApi.FullName);
                    registeredPluginType.Id = service.Create(registeredPluginType);
                }

                customApi.PluginTypeId = new EntityReference(PluginType.EntityLogicalName, registeredPluginType.Id);

                var existingCustomApi = registeredCustomApis.FirstOrDefault(c => c.UniqueName == customApi.UniqueName);

                if (existingCustomApi == null)
                {
                    existingCustomApi = customApi;
                    existingCustomApi.Id = service.Create(customApi);
                    customApi.Id = existingCustomApi.Id;
                }
                else
                {
                    customApi.Id = existingCustomApi.Id;
                    service.Update(customApi);
                }

                foreach (var customApiRequestParameter in customApi.InArguments)
                {
                    var existingRequestParameter = registeredCustomApiRequestParameters.FirstOrDefault(p => p.UniqueName == customApiRequestParameter.UniqueName && p.CustomApiId.Id == existingCustomApi.Id);

                    customApiRequestParameter.CustomApiId = new EntityReference(CustomApi.EntityLogicalName, existingCustomApi.Id);

                    if (existingRequestParameter == null)
                    {
                        customApiRequestParameter.Id = service.Create(customApiRequestParameter);
                    }
                    else
                    {
                        customApiRequestParameter.Id = existingRequestParameter.Id;
                        service.Update(customApiRequestParameter);
                        registeredCustomApiRequestParameters.Remove(existingRequestParameter);
                    }

                    AddSolutionComponentToSolution(service, pluginSolutionUniqueName, customApiRequestParameter.ToEntityReference(),
                        customApiParameterEntityTypeCode);
                }

                foreach (var customApiResponseProperty in customApi.OutArguments)
                {
                    var existingResponseProperty = registeredCustomApiResponseProperties.FirstOrDefault(p => p.UniqueName == customApiResponseProperty.UniqueName && p.CustomApiId.Id == existingCustomApi.Id);

                    customApiResponseProperty.CustomApiId = new EntityReference(CustomApi.EntityLogicalName, existingCustomApi.Id);

                    if (existingResponseProperty == null)
                    {
                        customApiResponseProperty.Id = service.Create(customApiResponseProperty);
                    }
                    else
                    {
                        customApiResponseProperty.Id = existingResponseProperty.Id;
                        service.Update(customApiResponseProperty);
                        registeredCustomApiResponseProperties.Remove(existingResponseProperty);
                    }

                    AddSolutionComponentToSolution(service, pluginSolutionUniqueName, customApiResponseProperty.ToEntityReference(),
                        customApiResponseEntityTypeCode);
                }


                AddSolutionComponentToSolution(service, pluginSolutionUniqueName, customApi.ToEntityReference(),
                    customApiEntityTypeCode);
            }

            if (registeredCustomApiRequestParameters.Any() || registeredCustomApiResponseProperties.Any())
            {
                Console.WriteLine("Deleting unnecessary request parameters and response properties");

                foreach (var parameterToRemove in registeredCustomApiRequestParameters)
                {
                    service.Delete(CustomApiRequestParameter.EntityLogicalName, parameterToRemove.Id);
                }

                foreach (var responseToRemove in registeredCustomApiResponseProperties)
                {
                    service.Delete(CustomApiResponseProperty.EntityLogicalName, responseToRemove.Id);
                }
            }

            Console.WriteLine();

            foreach (var step in registeredSteps)
            {
                service.Delete(SdkMessageProcessingStep.EntityLogicalName, step.Id);
            }
        }

        private static int GetEntityTypeCode(string logicalName, CrmServiceClient service)
        {
            var entityRequest = new RetrieveEntityRequest {LogicalName = logicalName};

            var entityResponse = (RetrieveEntityResponse) service.Execute(entityRequest);

            return entityResponse.EntityMetadata.ObjectTypeCode.GetValueOrDefault();
        }

        private static PluginAssembly GetProfilerAssembly(IOrganizationService service)
        {
            var assemblies = GetAssemblies(service);

            return assemblies.FirstOrDefault(a => a.Name.Contains("PluginProfiler"));
        }

        private static IEnumerable<PluginType> GetRegisteredPluginTypes(IOrganizationService service, Guid pluginAssemblyId)
        {
            var list = new List<PluginType>();

            var query = new QueryExpression(PluginType.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, pluginAssemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<PluginType>());
            }

            return list;
        }

        private static IEnumerable<CustomApi> GetRegisteredCustomApis(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<CustomApi>();

            var query = new QueryExpression(CustomApi.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("ismanaged", ConditionOperator.Equal, false); 
            var linkPluginType = query.AddLink(PluginType.EntityLogicalName, "plugintypeid", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<CustomApi>());
            }

            return list;
        }

        private static IEnumerable<CustomApiRequestParameter> GetRegisteredCustomApiRequestParameters(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<CustomApiRequestParameter>();

            var query = new QueryExpression(CustomApiRequestParameter.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("ismanaged", ConditionOperator.Equal, false);

            var linkCustomApi = query.AddLink(CustomApi.EntityLogicalName, "customapiid", "customapiid");
            var linkPluginType = linkCustomApi.AddLink(PluginType.EntityLogicalName, "plugintypeid", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<CustomApiRequestParameter>());
            }

            return list;
        }

        private static IEnumerable<CustomApiResponseProperty> GetRegisteredCustomApiResponseProperties(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<CustomApiResponseProperty>();

            var query = new QueryExpression(CustomApiResponseProperty.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("ismanaged", ConditionOperator.Equal, false);

            var linkCustomApi = query.AddLink(CustomApi.EntityLogicalName, "customapiid", "customapiid");
            var linkPluginType = linkCustomApi.AddLink(PluginType.EntityLogicalName, "plugintypeid", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<CustomApiResponseProperty>());
            }

            return list;
        }

        private static ICollection<SdkMessageProcessingStep> GetRegisteredSteps(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<SdkMessageProcessingStep>();

            var query = new QueryExpression("sdkmessageprocessingstep");
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("stage", ConditionOperator.NotEqual, 30);

            var linkPluginType = query.AddLink(PluginType.EntityLogicalName, "eventhandler", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<SdkMessageProcessingStep>());
            }

            return list;
        }

        private static ICollection<SdkMessageProcessingStepImage> GetRegisteredImages(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<SdkMessageProcessingStepImage>();

            var query = new QueryExpression("sdkmessageprocessingstepimage");
            query.ColumnSet.AllColumns = true;
            var stepLink = query.AddLink(SdkMessageProcessingStep.EntityLogicalName, "sdkmessageprocessingstepid", "sdkmessageprocessingstepid");
            var linkPluginType = stepLink.AddLink(PluginType.EntityLogicalName, "eventhandler", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<SdkMessageProcessingStepImage>());
            }

            return list;
        }

        private static IEnumerable<PluginAssembly> GetAssemblies(IOrganizationService service)
        {
            if (_list.Count == 0)
            {
                var query = new QueryExpression("pluginassembly");<commit_msg>[Cosmetics] Changed Hardcoded Strings to Static Strings<commit_after>﻿// Copyright (c) <NAME> (CGO Conseils). All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Navigation;
using Deploy;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Tooling.Connector;
using XrmFramework.DeployUtils.Comparers;
using XrmFramework.DeployUtils.Configuration;
using XrmFramework.DeployUtils.Model;

namespace XrmFramework.DeployUtils
{
    public static class RegistrationHelper
    {
        private static List<PluginAssembly> _list = new List<PluginAssembly>();
        
        public static void RegisterPluginsAndWorkflows<TPlugin>(string projectName)
        {
            var xrmFrameworkConfigSection = ConfigHelper.GetSection();

            var projectConfig = xrmFrameworkConfigSection.Projects.OfType<ProjectElement>()
                .FirstOrDefault(p => p.Name == projectName);

            if (projectConfig == null)
            {
                var defaultColor = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"No reference to the project {projectName} has been found in the xrmFramework.config file.");
                Console.ForegroundColor = defaultColor;
                return;
            }

            var pluginSolutionUniqueName = projectConfig.TargetSolution;

            var connectionString = ConfigurationManager.ConnectionStrings[xrmFrameworkConfigSection.SelectedConnection].ConnectionString;

            Console.WriteLine($"You are about to deploy on {connectionString} organization. If ok press any key.");
            Console.ReadKey();
            Console.WriteLine("Connecting to CRM...");

            CrmServiceClient.MaxConnectionTimeout = TimeSpan.FromMinutes(10);

            var service = new CrmServiceClient(connectionString);

            service.OrganizationServiceProxy?.EnableProxyTypes();

            InitMetadata(service, pluginSolutionUniqueName);

            var pluginAssembly = typeof(TPlugin).Assembly;

            var pluginType = pluginAssembly.GetType("XrmFramework.Plugin");
            var customApiType = pluginAssembly.GetType("XrmFramework.CustomApi");
            var workflowType = pluginAssembly.GetType("XrmFramework.Workflow.CustomWorkflowActivity");

            var pluginList = new List<Plugin>();

            var pluginTypes = pluginAssembly.GetTypes().Where(t => pluginType.IsAssignableFrom(t) && !customApiType.IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract).ToList();

            var workflowTypes = pluginAssembly.GetTypes().Where(t => workflowType.IsAssignableFrom(t) && !t.IsAbstract && t.IsPublic).ToList();
            
            var customApiTypes = pluginAssembly.GetTypes().Where(t => customApiType.IsAssignableFrom(t) && t.IsPublic && !t.IsAbstract).ToList();
            
            foreach (var type in pluginTypes)
            {
                dynamic pluginTemp;
                if (type.GetConstructor(new[] {typeof(string), typeof(string)}) != null)
                {
                    pluginTemp = Activator.CreateInstance(type, new object[] {null, null});
                }
                else
                {
                    pluginTemp = Activator.CreateInstance(type, new object[] { });
                }

                var plugin = Plugin.FromXrmFrameworkPlugin(pluginTemp, false);

                pluginList.Add(plugin);
            }

            foreach (var type in workflowTypes)
            {
                dynamic pluginTemp = Activator.CreateInstance(type, new object[] { });

                var plugin = Plugin.FromXrmFrameworkPlugin(pluginTemp, true);

                pluginList.Add(plugin);
            }

            var customApis = new List<CustomApi>();

            foreach (var type in customApiTypes)
            {
                dynamic customApiTemp;
                if (type.GetConstructor(new[] { typeof(string), typeof(string) }) != null)
                {
                    customApiTemp = Activator.CreateInstance(type, new object[] { null, null });
                }
                else
                {
                    customApiTemp = Activator.CreateInstance(type, new object[] { });
                }

                var customApi = CustomApi.FromXrmFrameworkCustomApi(customApiTemp, _publisher.CustomizationPrefix);

                customApis.Add(customApi);
            }


            var assembly = GetAssemblyByName(service, pluginAssembly.GetName().Name);

            var profilerAssembly = GetProfilerAssembly(service);

            var registeredPluginTypes = new List<PluginType>();
            var registeredCustomApis = new List<CustomApi>();
            var registeredCustomApiRequestParameters = new List<CustomApiRequestParameter>();
            var registeredCustomApiResponseProperties = new List<CustomApiResponseProperty>();
            var profiledSteps = new List<SdkMessageProcessingStep>();
            ICollection<SdkMessageProcessingStep> registeredSteps = Enumerable.Empty<SdkMessageProcessingStep>().ToList();

            var assemblyPath = pluginAssembly.Location;

            if (assembly == null)
            {
                Console.WriteLine("Registering assembly");

                assembly = GetAssemblyToRegister(pluginAssembly, assemblyPath);

                assembly.Id = service.Create(assembly);
            }
            else
            {
                Console.WriteLine("Updating plugin assembly");

                var updatedAssembly = new Entity("pluginassembly")
                {
                    Id = assembly.Id, 
                    ["content"] = Convert.ToBase64String(File.ReadAllBytes(assemblyPath))
                };

                registeredPluginTypes = GetRegisteredPluginTypes(service, assembly.Id).ToList();
                registeredCustomApis = GetRegisteredCustomApis(service, assembly.Id).ToList();
                registeredCustomApiRequestParameters = GetRegisteredCustomApiRequestParameters(service, assembly.Id).ToList();
                registeredCustomApiResponseProperties = GetRegisteredCustomApiResponseProperties(service, assembly.Id).ToList();

                registeredSteps = GetRegisteredSteps(service, assembly.Id);

                if (profilerAssembly != null)
                {
                    profiledSteps = GetRegisteredSteps(service, profilerAssembly.Id).ToList();
                }
                
                foreach (var registeredType in registeredPluginTypes)
                {
                    if (pluginList.All(p => p.FullName != registeredType.Name) && pluginList.Where(p => p.IsWorkflow).All(c => c.FullName != registeredType.TypeName) && customApis.All(c => c.FullName !=  registeredType.TypeName))
                    {
                        var registeredStepsForPluginType = registeredSteps.Where(s => s.EventHandler.Id == registeredType.Id).ToList();
                        foreach (var step in registeredStepsForPluginType)
                        {
                            service.Delete(SdkMessageProcessingStep.EntityLogicalName, step.Id);
                            registeredSteps.Remove(step);
                        }

                        foreach (var customApi in registeredCustomApis.Where(c => c.PluginTypeId?.Id == registeredType.Id))
                        {
                            service.Delete(customApi.LogicalName, customApi.Id);
                        }

                        service.Delete(PluginType.EntityLogicalName, registeredType.Id);
                    }
                }

                service.Update(updatedAssembly);
            }

            AddSolutionComponentToSolution(service, pluginSolutionUniqueName, assembly.ToEntityReference());

            var registeredImages = GetRegisteredImages(service, assembly.Id);

            Console.WriteLine();
            Console.WriteLine(@"Registering Plugins");

            foreach (var plugin in pluginList.Where(p => !p.IsWorkflow))
            {
                Console.WriteLine($@"  - {plugin.FullName}");

                var registeredPluginType = registeredPluginTypes.FirstOrDefault(p => p.Name == plugin.FullName);

                if (registeredPluginType == null)
                {
                    registeredPluginType = GetPluginTypeToRegister(assembly.Id, plugin.FullName);
                    registeredPluginType.Id = service.Create(registeredPluginType);
                }

                var registeredStepsForPluginType = registeredSteps.Where(s => s.EventHandler.Id == registeredPluginType.Id).ToList();

                var comparer = new SdkMessageStepComparer();

                foreach (var convertedStep in plugin.Steps)
                {
                    var stepToRegister = GetStepToRegister(registeredPluginType.Id, convertedStep);

                    var registeredStep = registeredStepsForPluginType.FirstOrDefault(s => comparer.Equals(s, stepToRegister));

                    if (registeredStep == null)
                    {
                        stepToRegister.Id = service.Create(stepToRegister);
                    }
                    else
                    {
                        registeredSteps.Remove(registeredStep);
                        stepToRegister.Id = registeredStep.Id;
                        if (comparer.NeedsUpdate(stepToRegister, registeredStep))
                        {
                            var profiledStep = profiledSteps.FirstOrDefault(p => p.Name.StartsWith(registeredStep.Name));

                            if (profiledStep != null)
                            {
                                service.Delete(profiledStep.LogicalName, profiledStep.Id);

                                service.Execute(new SetStateRequest
                                {
                                    EntityMoniker = registeredStep.ToEntityReference(),
                                    State = new OptionSetValue((int)SdkMessageProcessingStepState.Enabled),
                                    Status = new OptionSetValue(-1)
                                });
                            }

                            service.Update(stepToRegister);
                        }
                    }

                    AddSolutionComponentToSolution(service, pluginSolutionUniqueName, stepToRegister.ToEntityReference());

                    if (convertedStep.Message != Messages.Associate.ToString() && convertedStep.Message != Messages.Lose.ToString() && convertedStep.Message != Messages.Win.ToString())
                    {
                        var registeredPostImage = registeredImages.FirstOrDefault(i => i.Name == "PostImage" && i.SdkMessageProcessingStepId.Id == stepToRegister.Id);

                        if (convertedStep.PostImageUsed && convertedStep.Message != Messages.Delete.ToString())
                        {
                            if (registeredPostImage == null)
                            {
                                registeredPostImage = GetImageToRegister(service, stepToRegister.Id, convertedStep, false);
                                registeredPostImage.Id = service.Create(registeredPostImage);

                            }
                            else if (registeredPostImage.Attributes1 != convertedStep.JoinedPostImageAttributes)
                            {
                                registeredPostImage.Attributes1 = convertedStep.JoinedPostImageAttributes;
                                service.Update(registeredPostImage);
                            }
                        }
                        else if (registeredPostImage != null)
                        {
                            service.Delete(registeredPostImage.LogicalName, registeredPostImage.Id);
                        }

                        var registeredPreImage = registeredImages.FirstOrDefault(i => i.Name == "PreImage" && i.SdkMessageProcessingStepId.Id == stepToRegister.Id);

                        if (convertedStep.PreImageUsed)
                        {
                            if (registeredPreImage == null)
                            {
                                registeredPreImage = GetImageToRegister(service, stepToRegister.Id, convertedStep, true);
                                registeredPreImage.Id = service.Create(registeredPreImage);
                            }
                            else if (registeredPreImage.Attributes1 != convertedStep.JoinedPreImageAttributes)
                            {
                                registeredPreImage.Attributes1 = convertedStep.JoinedPreImageAttributes;
                                service.Update(registeredPreImage);
                            }
                        }
                        else if (registeredPreImage != null)
                        {
                            service.Delete(registeredPreImage.LogicalName, registeredPreImage.Id);
                        }
                    }
                }
            }

            Console.WriteLine();
            Console.WriteLine(@"Registering Custom Workflow activities");

            foreach (var customWf in pluginList.Where(p => p.IsWorkflow))
            {
                var registeredPluginType = registeredPluginTypes.FirstOrDefault(p => p.TypeName == customWf.FullName);

                Console.WriteLine($@"  - {customWf.FullName}");

                if (registeredPluginType == null)
                {
                    registeredPluginType = GetCustomWorkflowTypeToRegister(assembly.Id, customWf.FullName, customWf.DisplayName);
                    registeredPluginType.Id = service.Create(registeredPluginType);
                }
                if (registeredPluginType.Name != customWf.DisplayName)
                {
                    registeredPluginType.Name = customWf.DisplayName;
                    service.Update(registeredPluginType);
                }
            }

            Console.WriteLine();
            Console.WriteLine(@"Registering Custom Apis");
            
            var customApiEntityTypeCode = GetEntityTypeCode("customapi", service);
            var customApiParameterEntityTypeCode = GetEntityTypeCode("customapirequestparameter", service);
            var customApiResponseEntityTypeCode = GetEntityTypeCode("customapiresponseproperty", service);

            foreach (var customApi in customApis)
            {
                Console.WriteLine($@"  - {customApi.FullName}");

                var registeredPluginType = registeredPluginTypes.FirstOrDefault(p => p.Name == customApi.FullName);

                if (registeredPluginType == null)
                {
                    registeredPluginType = GetPluginTypeToRegister(assembly.Id, customApi.FullName);
                    registeredPluginType.Id = service.Create(registeredPluginType);
                }

                customApi.PluginTypeId = new EntityReference(PluginType.EntityLogicalName, registeredPluginType.Id);

                var existingCustomApi = registeredCustomApis.FirstOrDefault(c => c.UniqueName == customApi.UniqueName);

                if (existingCustomApi == null)
                {
                    existingCustomApi = customApi;
                    existingCustomApi.Id = service.Create(customApi);
                    customApi.Id = existingCustomApi.Id;
                }
                else
                {
                    customApi.Id = existingCustomApi.Id;
                    service.Update(customApi);
                }

                foreach (var customApiRequestParameter in customApi.InArguments)
                {
                    var existingRequestParameter = registeredCustomApiRequestParameters.FirstOrDefault(p => p.UniqueName == customApiRequestParameter.UniqueName && p.CustomApiId.Id == existingCustomApi.Id);

                    customApiRequestParameter.CustomApiId = new EntityReference(CustomApi.EntityLogicalName, existingCustomApi.Id);

                    if (existingRequestParameter == null)
                    {
                        customApiRequestParameter.Id = service.Create(customApiRequestParameter);
                    }
                    else
                    {
                        customApiRequestParameter.Id = existingRequestParameter.Id;
                        service.Update(customApiRequestParameter);
                        registeredCustomApiRequestParameters.Remove(existingRequestParameter);
                    }

                    AddSolutionComponentToSolution(service, pluginSolutionUniqueName, customApiRequestParameter.ToEntityReference(),
                        customApiParameterEntityTypeCode);
                }

                foreach (var customApiResponseProperty in customApi.OutArguments)
                {
                    var existingResponseProperty = registeredCustomApiResponseProperties.FirstOrDefault(p => p.UniqueName == customApiResponseProperty.UniqueName && p.CustomApiId.Id == existingCustomApi.Id);

                    customApiResponseProperty.CustomApiId = new EntityReference(CustomApi.EntityLogicalName, existingCustomApi.Id);

                    if (existingResponseProperty == null)
                    {
                        customApiResponseProperty.Id = service.Create(customApiResponseProperty);
                    }
                    else
                    {
                        customApiResponseProperty.Id = existingResponseProperty.Id;
                        service.Update(customApiResponseProperty);
                        registeredCustomApiResponseProperties.Remove(existingResponseProperty);
                    }

                    AddSolutionComponentToSolution(service, pluginSolutionUniqueName, customApiResponseProperty.ToEntityReference(),
                        customApiResponseEntityTypeCode);
                }


                AddSolutionComponentToSolution(service, pluginSolutionUniqueName, customApi.ToEntityReference(),
                    customApiEntityTypeCode);
            }

            if (registeredCustomApiRequestParameters.Any() || registeredCustomApiResponseProperties.Any())
            {
                Console.WriteLine("Deleting unnecessary request parameters and response properties");

                foreach (var parameterToRemove in registeredCustomApiRequestParameters)
                {
                    service.Delete(CustomApiRequestParameter.EntityLogicalName, parameterToRemove.Id);
                }

                foreach (var responseToRemove in registeredCustomApiResponseProperties)
                {
                    service.Delete(CustomApiResponseProperty.EntityLogicalName, responseToRemove.Id);
                }
            }

            Console.WriteLine();

            foreach (var step in registeredSteps)
            {
                service.Delete(SdkMessageProcessingStep.EntityLogicalName, step.Id);
            }
        }

        private static int GetEntityTypeCode(string logicalName, CrmServiceClient service)
        {
            var entityRequest = new RetrieveEntityRequest {LogicalName = logicalName};

            var entityResponse = (RetrieveEntityResponse) service.Execute(entityRequest);

            return entityResponse.EntityMetadata.ObjectTypeCode.GetValueOrDefault();
        }

        private static PluginAssembly GetProfilerAssembly(IOrganizationService service)
        {
            var assemblies = GetAssemblies(service);

            return assemblies.FirstOrDefault(a => a.Name.Contains("PluginProfiler"));
        }

        private static IEnumerable<PluginType> GetRegisteredPluginTypes(IOrganizationService service, Guid pluginAssemblyId)
        {
            var list = new List<PluginType>();

            var query = new QueryExpression(PluginType.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, pluginAssemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<PluginType>());
            }

            return list;
        }

        private static IEnumerable<CustomApi> GetRegisteredCustomApis(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<CustomApi>();

            var query = new QueryExpression(CustomApi.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("ismanaged", ConditionOperator.Equal, false); 
            var linkPluginType = query.AddLink(PluginType.EntityLogicalName, "plugintypeid", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<CustomApi>());
            }

            return list;
        }

        private static IEnumerable<CustomApiRequestParameter> GetRegisteredCustomApiRequestParameters(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<CustomApiRequestParameter>();

            var query = new QueryExpression(CustomApiRequestParameter.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("ismanaged", ConditionOperator.Equal, false);

            var linkCustomApi = query.AddLink(CustomApi.EntityLogicalName, "customapiid", "customapiid");
            var linkPluginType = linkCustomApi.AddLink(PluginType.EntityLogicalName, "plugintypeid", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<CustomApiRequestParameter>());
            }

            return list;
        }

        private static IEnumerable<CustomApiResponseProperty> GetRegisteredCustomApiResponseProperties(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<CustomApiResponseProperty>();

            var query = new QueryExpression(CustomApiResponseProperty.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("ismanaged", ConditionOperator.Equal, false);

            var linkCustomApi = query.AddLink(CustomApi.EntityLogicalName, "customapiid", "customapiid");
            var linkPluginType = linkCustomApi.AddLink(PluginType.EntityLogicalName, "plugintypeid", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<CustomApiResponseProperty>());
            }

            return list;
        }

        private static ICollection<SdkMessageProcessingStep> GetRegisteredSteps(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<SdkMessageProcessingStep>();

            var query = new QueryExpression(SdkMessageProcessingStep.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            query.Criteria.AddCondition("stage", ConditionOperator.NotEqual, 30);

            var linkPluginType = query.AddLink(PluginType.EntityLogicalName, "eventhandler", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<SdkMessageProcessingStep>());
            }

            return list;
        }

        private static ICollection<SdkMessageProcessingStepImage> GetRegisteredImages(IOrganizationService service, Guid assemblyId)
        {
            var list = new List<SdkMessageProcessingStepImage>();

            var query = new QueryExpression(SdkMessageProcessingStepImage.EntityLogicalName);
            query.ColumnSet.AllColumns = true;
            var stepLink = query.AddLink(SdkMessageProcessingStep.EntityLogicalName, "sdkmessageprocessingstepid", "sdkmessageprocessingstepid");
            var linkPluginType = stepLink.AddLink(PluginType.EntityLogicalName, "eventhandler", "plugintypeid");
            linkPluginType.LinkCriteria.AddCondition("pluginassemblyid", ConditionOperator.Equal, assemblyId);

            var result = RetrieveAll(service, query);
            foreach (var type in result)
            {
                list.Add(type.ToEntity<SdkMessageProcessingStepImage>());
            }

            return list;
        }

        private static IEnumerable<PluginAssembly> GetAssemblies(IOrganizationService service)
        {
            if (_list.Count == 0)
            {
                var query = new QueryExpression(PluginAssembly.EntityLogicalName);
{'fraction_non_alphanumeric': 0.07917928945194161, 'fraction_numerical': 0.0004721249557382854, 'mean_word_length': 1.9141825269433617, 'pattern_counts': {'":': 0, '<': 69, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6954563', 'n_tokens_mistral': 11743, 'n_tokens_neox': 10937, 'n_words': 2007}