namespace SpaceGame.Api.PlayerFaction.Simulation; public sealed class PlayerFactionProjectionService { public PlayerFactionSnapshot? ToSnapshot(PlayerFactionRuntime? player) { if (player is null) { return null; } return new PlayerFactionSnapshot( player.Id, player.Label, player.PersonaName, player.RaceId, player.SovereignFactionId, player.RequiresOnboarding, player.Status, player.CreatedAtUtc, player.UpdatedAtUtc, new PlayerAssetRegistrySnapshot( player.AssetRegistry.ShipIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.StationIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.CommanderIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.ClaimIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.ConstructionSiteIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.PolicySetIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.MarketOrderIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.FleetIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.TaskForceIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.StationGroupIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.EconomicRegionIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.FrontIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), player.AssetRegistry.ReserveIds.OrderBy(id => id, StringComparer.Ordinal).ToList()), new PlayerStrategicIntentSnapshot( player.StrategicIntent.StrategicPosture, player.StrategicIntent.EconomicPosture, player.StrategicIntent.MilitaryPosture, player.StrategicIntent.LogisticsPosture, player.StrategicIntent.DesiredReserveRatio, player.StrategicIntent.AllowDelegatedCombatAutomation, player.StrategicIntent.AllowDelegatedEconomicAutomation, player.StrategicIntent.Notes), player.Fleets.Select(fleet => new PlayerFleetSnapshot( fleet.Id, fleet.Label, fleet.Status, fleet.Role, fleet.CommanderId, fleet.FrontId, fleet.HomeSystemId, fleet.HomeStationId, fleet.PolicyId, fleet.AutomationPolicyId, fleet.ReinforcementPolicyId, fleet.AssetIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), fleet.TaskForceIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), fleet.DirectiveIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), fleet.UpdatedAtUtc)).ToList(), player.TaskForces.Select(taskForce => new PlayerTaskForceSnapshot( taskForce.Id, taskForce.Label, taskForce.Status, taskForce.Role, taskForce.FleetId, taskForce.CommanderId, taskForce.FrontId, taskForce.PolicyId, taskForce.AutomationPolicyId, taskForce.AssetIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), taskForce.DirectiveIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), taskForce.UpdatedAtUtc)).ToList(), player.StationGroups.Select(group => new PlayerStationGroupSnapshot( group.Id, group.Label, group.Status, group.Role, group.EconomicRegionId, group.PolicyId, group.AutomationPolicyId, group.StationIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), group.DirectiveIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), group.FocusItemIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), group.UpdatedAtUtc)).ToList(), player.EconomicRegions.Select(region => new PlayerEconomicRegionSnapshot( region.Id, region.Label, region.Status, region.Role, region.SharedEconomicRegionId, region.PolicyId, region.AutomationPolicyId, region.SystemIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), region.StationGroupIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), region.DirectiveIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), region.UpdatedAtUtc)).ToList(), player.Fronts.Select(front => new PlayerFrontSnapshot( front.Id, front.Label, front.Status, front.Priority, front.Posture, front.SharedFrontLineId, front.TargetFactionId, front.SystemIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), front.FleetIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), front.ReserveIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), front.DirectiveIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), front.UpdatedAtUtc)).ToList(), player.Reserves.Select(reserve => new PlayerReserveGroupSnapshot( reserve.Id, reserve.Label, reserve.Status, reserve.ReserveKind, reserve.HomeSystemId, reserve.PolicyId, reserve.AssetIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), reserve.FrontIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), reserve.UpdatedAtUtc)).ToList(), player.Policies.Select(policy => new PlayerFactionPolicySnapshot( policy.Id, policy.Label, policy.ScopeKind, policy.ScopeId, policy.PolicySetId, policy.AllowDelegatedCombat, policy.AllowDelegatedTrade, policy.ReserveCreditsRatio, policy.ReserveMilitaryRatio, policy.TradeAccessPolicy, policy.DockingAccessPolicy, policy.ConstructionAccessPolicy, policy.OperationalRangePolicy, policy.CombatEngagementPolicy, policy.AvoidHostileSystems, policy.FleeHullRatio, policy.BlacklistedSystemIds.OrderBy(id => id, StringComparer.Ordinal).ToList(), policy.Notes, policy.UpdatedAtUtc)).ToList(), player.AutomationPolicies.Select(policy => new PlayerAutomationPolicySnapshot( policy.Id, policy.Label, policy.ScopeKind, policy.ScopeId, policy.Enabled, policy.BehaviorKind, policy.UseOrders, policy.StagingOrderKind, policy.MaxSystemRange, policy.KnownStationsOnly, policy.Radius, policy.WaitSeconds, policy.PreferredItemId, policy.Notes, policy.RepeatOrders.Select(ToShipOrderTemplateSnapshot).ToList(), policy.UpdatedAtUtc)).ToList(), player.ReinforcementPolicies.Select(policy => new PlayerReinforcementPolicySnapshot( policy.Id, policy.Label, policy.ScopeKind, policy.ScopeId, policy.ShipKind, policy.DesiredAssetCount, policy.MinimumReserveCount, policy.AutoTransferReserves, policy.AutoQueueProduction, policy.SourceReserveId, policy.TargetFrontId, policy.Notes, policy.UpdatedAtUtc)).ToList(), player.ProductionPrograms.Select(program => new PlayerProductionProgramSnapshot( program.Id, program.Label, program.Status, program.Kind, program.TargetShipKind, program.TargetModuleId, program.TargetItemId, program.TargetCount, program.CurrentCount, program.StationGroupId, program.ReinforcementPolicyId, program.Notes, program.UpdatedAtUtc)).ToList(), player.Directives.Select(directive => new PlayerDirectiveSnapshot( directive.Id, directive.Label, directive.Status, directive.Kind, directive.ScopeKind, directive.ScopeId, directive.TargetEntityId, directive.TargetSystemId, directive.TargetPosition is null ? null : ToDto(directive.TargetPosition.Value), directive.HomeSystemId, directive.HomeStationId, directive.SourceStationId, directive.DestinationStationId, directive.BehaviorKind, directive.UseOrders, directive.StagingOrderKind, directive.ItemId, directive.PreferredAnchorId, directive.PreferredConstructionSiteId, directive.PreferredModuleId, directive.Priority, directive.Radius, directive.WaitSeconds, directive.MaxSystemRange, directive.KnownStationsOnly, directive.PatrolPoints.Select(ToDto).ToList(), directive.RepeatOrders.Select(ToShipOrderTemplateSnapshot).ToList(), directive.PolicyId, directive.AutomationPolicyId, directive.Notes, directive.CreatedAtUtc, directive.UpdatedAtUtc)).ToList(), player.Assignments.Select(assignment => new PlayerAssignmentSnapshot( assignment.Id, assignment.AssetKind, assignment.AssetId, assignment.FleetId, assignment.TaskForceId, assignment.StationGroupId, assignment.EconomicRegionId, assignment.FrontId, assignment.ReserveId, assignment.DirectiveId, assignment.PolicyId, assignment.AutomationPolicyId, assignment.Role, assignment.Status, assignment.UpdatedAtUtc)).ToList(), player.DecisionLog.Select(entry => new PlayerDecisionLogEntrySnapshot( entry.Id, entry.Kind, entry.Summary, entry.RelatedEntityKind, entry.RelatedEntityId, entry.OccurredAtUtc)).ToList(), player.Alerts.Select(alert => new PlayerAlertSnapshot( alert.Id, alert.Kind, alert.Severity, alert.Summary, alert.AssetKind, alert.AssetId, alert.RelatedDirectiveId, alert.Status, alert.CreatedAtUtc)).ToList()); } private static ShipOrderTemplateSnapshot ToShipOrderTemplateSnapshot(ShipOrderTemplateRuntime template) => new( template.Kind, template.Label, template.TargetEntityId, template.TargetSystemId, template.TargetPosition is null ? null : ToDto(template.TargetPosition.Value), template.SourceStationId, template.DestinationStationId, template.ItemId, template.AnchorId, template.ConstructionSiteId, template.ModuleId, template.WaitSeconds, template.Radius, template.MaxSystemRange, template.KnownStationsOnly); private static Vector3Dto ToDto(Vector3 vector) => new(vector.X, vector.Y, vector.Z); }