using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common.Request; using SPTarkov.Server.Core.Models.Eft.ItemEvent; using SPTarkov.Server.Core.Models.Eft.Profile; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Utils; namespace SPTarkov.Server.Core.DI; public interface IOnBeforeEventRequestData; public interface IOnAfterEventRequestData; public record StaticDynamicOnBeforeEventRequestData(string Url, IRequestData RequestData, MongoId SessionId, string Output) : IOnBeforeEventRequestData; public record StaticDynamicOnAfterEventRequestData(string Url, IRequestData RequestData, MongoId SessionId, string Output, object Result) : IOnAfterEventRequestData; public abstract class Router { public event EventHandler? OnBeforeAction; public event EventHandler? OnAfterAction; protected IEnumerable handledRoutes = []; public virtual string GetTopLevelRoute() { return "spt"; } protected abstract IEnumerable GetHandledRoutes(); protected IEnumerable GetInternalHandledRoutes() { if (!handledRoutes.Any()) { handledRoutes = GetHandledRoutes(); } return handledRoutes; } protected void TriggerOnBeforeAction(IOnBeforeEventRequestData requestData) { OnBeforeAction?.Invoke(this, requestData); } protected void TriggerOnAfterAction(IOnAfterEventRequestData requestData) { OnAfterAction?.Invoke(this, requestData); } public bool CanHandle(string url, bool partialMatch = false) { if (partialMatch) { return GetInternalHandledRoutes().Where(r => r.dynamic).Any(r => url.Contains(r.route)); } return GetInternalHandledRoutes().Where(r => !r.dynamic).Any(r => r.route == url); } } public abstract class StaticRouter(JsonUtil jsonUtil, IEnumerable routes) : Router { public async ValueTask HandleStatic(string url, string? body, MongoId sessionId, string output) { var action = routes.Single(route => route.url == url); var type = action.bodyType; IRequestData? info = null; if (type != null && !string.IsNullOrEmpty(body)) { info = (IRequestData?)jsonUtil.Deserialize(body, type); } info ??= new EmptyRequestData(); TriggerOnBeforeAction(new StaticDynamicOnBeforeEventRequestData(url, info, sessionId, output)); var result = await action.action(url, info, sessionId, output); TriggerOnAfterAction(new StaticDynamicOnAfterEventRequestData(url, info, sessionId, output, result)); return result; } protected override IEnumerable GetHandledRoutes() { return routes.Select(route => new HandledRoute(route.url, false)); } } public abstract class DynamicRouter(JsonUtil jsonUtil, IEnumerable routes) : Router { public async ValueTask HandleDynamic(string url, string? body, MongoId sessionId, string output) { var action = routes.First(r => url.Contains(r.url)); var type = action.bodyType; IRequestData? info = null; if (type != null && !string.IsNullOrEmpty(body)) { info = (IRequestData?)jsonUtil.Deserialize(body, type); } info ??= new EmptyRequestData(); TriggerOnBeforeAction(new StaticDynamicOnBeforeEventRequestData(url, info, sessionId, output)); var result = await action.action(url, info, sessionId, output); TriggerOnAfterAction(new StaticDynamicOnAfterEventRequestData(url, info, sessionId, output, result)); return result; } protected override IEnumerable GetHandledRoutes() { return routes.Select(route => new HandledRoute(route.url, true)); } } public record ItemRouterOnBeforeEventRequestData( string Url, PmcData PmcData, BaseInteractionRequestData Body, MongoId SessionId, ItemEventRouterResponse Output) : IOnBeforeEventRequestData; public record ItemRouterOnAfterEventRequestData( string Url, PmcData PmcData, BaseInteractionRequestData Body, MongoId SessionId, ItemEventRouterResponse Output, ValueTask Result) : IOnAfterEventRequestData; public record OnAfterEventRequestData(string Url, T RequestData, MongoId SessionId, R Output, object Result) : IOnAfterEventRequestData; // The name of this class should be ItemEventRouter, but that name is taken, // So instead I added the definition public abstract class ItemEventRouterDefinition : Router { public ValueTask HandleItemEvent( string url, PmcData pmcData, BaseInteractionRequestData body, MongoId sessionID, ItemEventRouterResponse output ) { TriggerOnBeforeAction(new ItemRouterOnBeforeEventRequestData(url, pmcData, body, sessionID, output)); var result = HandleItemEventInternal(url, pmcData, body, sessionID, output); TriggerOnAfterAction(new ItemRouterOnAfterEventRequestData(url, pmcData, body, sessionID, output, result)); return result; } protected abstract ValueTask HandleItemEventInternal( string url, PmcData pmcData, BaseInteractionRequestData body, MongoId sessionID, ItemEventRouterResponse output ); } public record SaveLoadOnBeforeEventRequestData(SptProfile Profile) : IOnBeforeEventRequestData; public record SaveLoadRouterOnAfterEventRequestData(SptProfile Profile) : IOnAfterEventRequestData; public abstract class SaveLoadRouter : Router { public SptProfile HandleLoad(SptProfile profile) { TriggerOnBeforeAction(new SaveLoadOnBeforeEventRequestData(profile)); var result = HandleLoadInternal(profile); TriggerOnAfterAction(new SaveLoadRouterOnAfterEventRequestData(profile)); return result; } protected abstract SptProfile HandleLoadInternal(SptProfile profile); } public record HandledRoute(string route, bool dynamic); public record RouteAction(string url, Func> action, Type? bodyType = null); public record RouteAction(string url, Func> typedAction) : RouteAction(url, async (url, info, sessionId, output) => await typedAction(url, (TRequest)info, sessionId, output), typeof(TRequest)) where TRequest : class;