using Medusa.Core.Attributes; using Medusa.Core.Handlers; using System.Reflection; using System.Xml.Linq; namespace Medusa.Core.Services { public class HandlerService(IServiceProvider serviceProvider, ILogger logger) : IHandlerService { private readonly IServiceProvider _serviceProvider = serviceProvider; private readonly ILogger _logger = logger; public List Handlers { get; set; } = []; public async Task Handle(string model, string module, string method, XDocument body) { foreach(var handler in Handlers) { //Module and service are on the attribute var handlerAttribute = handler.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(HandlerAttribute)); if(handlerAttribute is null) { var response = await HandleInheritanceClass(handler, model, module, method, body); if(response is not null) { return response; } continue; } var attributeModule = handlerAttribute.ConstructorArguments[0].Value.ToString(); var attributeMethod = handlerAttribute.ConstructorArguments[1].Value.ToString(); if(attributeModule == module && attributeMethod == method) { // Body param is optional so check for it bool requiresXDocumentConstructor = handler.GetConstructors() .Any(c => c.GetParameters().Any(p => p.ParameterType == typeof(XDocument))); IHandler handlerInstance = requiresXDocumentConstructor ? (IHandler)ActivatorUtilities.CreateInstance(_serviceProvider, handler, body) : (IHandler)ActivatorUtilities.CreateInstance(_serviceProvider, handler); return await handlerInstance.HandleAsync(model); } } //If no handler is found return an empty document _logger.LogWarning($"No handler found for {model}/{module}/{method}"); return new XDocument(); } private async Task HandleInheritanceClass(Type handler, string model, string module, string method, XDocument body) { bool requiredXdocumentConstructor = handler.GetConstructors() .Any(c => c.GetParameters().Any(p => p.ParameterType == typeof(XDocument))); Handler handlerInstance = requiredXdocumentConstructor ? (Handler)ActivatorUtilities.CreateInstance(_serviceProvider, handler, body) : (Handler)ActivatorUtilities.CreateInstance(_serviceProvider, handler); try { var configureMethod = handler.GetMethod("Configure"); configureMethod?.Invoke(handlerInstance, null); } catch(NotImplementedException e) { _logger.LogError(e, "Configure method not implemented for {Handler}", handler.Name); return null; } var handlerModule = handlerInstance.HandlerModule; var handlerMethod = handlerInstance.HandlerMethod; if(string.IsNullOrEmpty(handlerModule)) { _logger.LogError("Module not set for {Handler}", handler.Name); return null; } if(string.IsNullOrEmpty(handlerMethod)) { _logger.LogError("Method not set for {Handler}", handler.Name); return null; } if(handlerModule != module && handlerMethod != method) return null; var handleMethod = handlerInstance.GetType().GetMethod("Handle", [typeof(string)]); var handleAsyncMethod = handlerInstance.GetType().GetMethod("HandleAsync", [typeof(string)]); var response = new XDocument(); response = handleMethod?.DeclaringType != typeof(Handler) ? handlerInstance.Handle(model) : handleAsyncMethod?.DeclaringType != typeof(Handler) ? await handlerInstance.HandleAsync(model) : null; if(response is null) { _logger.LogError("Handle and HandleAsync not implemented for {Handler}", handler.Name); return null; } return response; } } }