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.PreferredNodeId,
            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.NodeId,
        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);
}
