using System.Threading.Channels;
using Microsoft.Extensions.Options;
using SpaceGame.Api.Universe.Bootstrap;
using SpaceGame.Api.Universe.Scenario;
using static SpaceGame.Api.Shared.Runtime.ShipBehaviorKinds;
using static SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport;

namespace SpaceGame.Api.Universe.Simulation;

public sealed class WorldService
{
    private const int DeltaHistoryLimit = 256;
    private const string StarterPlayerShipId = "ship_arg_s_scout_01_a";

    private readonly Lock _sync = new();
    private readonly OrbitalSimulationSnapshot _orbitalSimulation;
    private readonly SimulationEngine _engine;
    private readonly IPlayerIdentityResolver _playerIdentityResolver;
    private readonly IPlayerStateStore _playerStateStore;
    private readonly PlayerFactionProjectionService _playerFactionProjection;
    private readonly ScenarioLoader _scenarioLoader;
    private readonly WorldBuilder _worldBuilder;
    private readonly IStaticDataProvider _staticData;
    private readonly WorldSeedingService _worldSeedingService;
    private readonly PlayerFactionService _playerFaction = new();
    private readonly Dictionary<Guid, SubscriptionState> _subscribers = [];
    private readonly Queue<WorldDelta> _history = [];
    private SimulationWorld _world = null!;
    private string? _currentScenarioPath;
    private WorldGenerationOptions? _currentWorldGenerationOptions;
    private long _sequence;

    public WorldService(
      ScenarioLoader scenarioLoader,
      WorldBuilder worldBuilder,
      IStaticDataProvider staticData,
      WorldSeedingService worldSeedingService,
      IPlayerStateStore playerStateStore,
      IPlayerIdentityResolver playerIdentityResolver,
      PlayerFactionProjectionService playerFactionProjection,
      IBalanceService balance,
      IOptions<OrbitalSimulationOptions> orbitalSimulationOptions)
    {
        _orbitalSimulation = new OrbitalSimulationSnapshot(orbitalSimulationOptions.Value.SimulatedSecondsPerRealSecond);
        _playerStateStore = playerStateStore;
        _playerIdentityResolver = playerIdentityResolver;
        _playerFactionProjection = playerFactionProjection;
        _scenarioLoader = scenarioLoader;
        _worldBuilder = worldBuilder;
        _staticData = staticData;
        _worldSeedingService = worldSeedingService;
        _engine = new SimulationEngine(orbitalSimulationOptions.Value, balance, playerStateStore);
    }

    public void New(WorldGenerationOptions options)
    {
        lock (_sync)
        {
            _currentScenarioPath = null;
            _currentWorldGenerationOptions = options;
            ReplaceWorldUnsafe(_worldBuilder.BuildFromGeneration(options), "new", "Generated new world");
        }
    }

    public void LoadFromScenario(string scenarioPath)
    {
        lock (_sync)
        {
            _currentScenarioPath = scenarioPath;
            _currentWorldGenerationOptions = null;
            ReplaceWorldUnsafe(_worldBuilder.BuildFromScenario(_scenarioLoader.Load(scenarioPath)), "load-scenario", $"Loaded scenario {scenarioPath}");
        }
    }

    public WorldSnapshot GetSnapshot()
    {
        lock (_sync)
        {
            return _engine.BuildSnapshot(_world, _sequence);
        }
    }

    public (long Sequence, DateTimeOffset GeneratedAtUtc) GetStatus()
    {
        lock (_sync)
        {
            return (_sequence, _world.GeneratedAtUtc);
        }
    }

    public (int ConnectedClients, int DeltaHistoryCount) GetConnectionStats()
    {
        lock (_sync)
        {
            return (_subscribers.Count, _history.Count);
        }
    }

    public ShipSnapshot? EnqueueShipOrder(string shipId, ShipOrderCommandRequest request)
    {
        lock (_sync)
        {
            ValidateShipOrderRequestUnsafe(shipId, request);
            var ship = CanCurrentActorAccessGm()
              ? EnqueueGmShipOrderUnsafe(shipId, request)
              : _playerFaction.EnqueueDirectShipOrder(_world, _playerStateStore, GetCurrentPlayerKey(), shipId, request);
            if (ship is null)
            {
                return null;
            }

            return GetShipSnapshotUnsafe(ship.Id);
        }
    }

    public ShipSnapshot? RemoveShipOrder(string shipId, string orderId)
    {
        lock (_sync)
        {
            var ship = CanCurrentActorAccessGm()
              ? RemoveGmShipOrderUnsafe(shipId, orderId)
              : _playerFaction.RemoveDirectShipOrder(_world, _playerStateStore, GetCurrentPlayerKey(), shipId, orderId);
            if (ship is null)
            {
                return null;
            }

            return GetShipSnapshotUnsafe(ship.Id);
        }
    }

    public ShipSnapshot? UpdateShipDefaultBehavior(string shipId, ShipDefaultBehaviorCommandRequest request)
    {
        lock (_sync)
        {
            var ship = CanCurrentActorAccessGm()
              ? ConfigureGmShipBehaviorUnsafe(shipId, request)
              : _playerFaction.ConfigureDirectShipBehavior(_world, _playerStateStore, GetCurrentPlayerKey(), shipId, request);
            if (ship is null)
            {
                return null;
            }

            return GetShipSnapshotUnsafe(ship.Id);
        }
    }

    public PlayerFactionSnapshot? GetPlayerFaction()
    {
        lock (_sync)
        {
            var playerKey = GetCurrentPlayerKey();
            var player = _playerFaction.TryGetDomain(_playerStateStore, playerKey)
              ?? _playerFaction.EnsureDomain(_world, _playerStateStore, playerKey);
            return _playerFactionProjection.ToSnapshot(player);
        }
    }

    public PlayerFactionSnapshot? CompletePlayerOnboarding(CompletePlayerOnboardingRequest request)
    {
        lock (_sync)
        {
            if (!_staticData.RaceDefinitions.TryGetValue(request.RaceId.Trim(), out var race))
            {
                throw new InvalidOperationException($"Race '{request.RaceId}' is not defined in static data.");
            }

            var playerKey = GetCurrentPlayerKey();
            var player = _playerFaction.CompleteOnboarding(_world, _playerStateStore, playerKey, request);
            var playerFaction = CreatePlayerOwnedFactionUnsafe(player, race);
            var starterSystemId = ResolveStarterSystemIdUnsafe();
            SpawnPlayerStarterShipUnsafe(playerFaction, starterSystemId);
            _playerFaction.EnsureInitializedDomain(_world, _playerStateStore, playerKey);
            PublishSnapshotRefreshUnsafe("player-onboarding", $"Initialized player {player.PersonaName}", "faction", playerFaction.Id);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? CreatePlayerOrganization(PlayerOrganizationCommandRequest request)
    {
        lock (_sync)
        {
            _playerFaction.CreateOrganization(_world, _playerStateStore, GetCurrentPlayerKey(), request);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? DeletePlayerOrganization(string organizationId)
    {
        lock (_sync)
        {
            _playerFaction.DeleteOrganization(_world, _playerStateStore, GetCurrentPlayerKey(), organizationId);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? UpdatePlayerOrganizationMembership(string organizationId, PlayerOrganizationMembershipCommandRequest request)
    {
        lock (_sync)
        {
            _playerFaction.UpdateOrganizationMembership(_world, _playerStateStore, GetCurrentPlayerKey(), organizationId, request);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? UpsertPlayerDirective(string? directiveId, PlayerDirectiveCommandRequest request)
    {
        lock (_sync)
        {
            _playerFaction.UpsertDirective(_world, _playerStateStore, GetCurrentPlayerKey(), directiveId, request);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? DeletePlayerDirective(string directiveId)
    {
        lock (_sync)
        {
            _playerFaction.DeleteDirective(_world, _playerStateStore, GetCurrentPlayerKey(), directiveId);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? UpsertPlayerPolicy(string? policyId, PlayerPolicyCommandRequest request)
    {
        lock (_sync)
        {
            _playerFaction.UpsertPolicy(_world, _playerStateStore, GetCurrentPlayerKey(), policyId, request);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? UpsertPlayerAutomationPolicy(string? automationPolicyId, PlayerAutomationPolicyCommandRequest request)
    {
        lock (_sync)
        {
            _playerFaction.UpsertAutomationPolicy(_world, _playerStateStore, GetCurrentPlayerKey(), automationPolicyId, request);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? UpsertPlayerReinforcementPolicy(string? reinforcementPolicyId, PlayerReinforcementPolicyCommandRequest request)
    {
        lock (_sync)
        {
            _playerFaction.UpsertReinforcementPolicy(_world, _playerStateStore, GetCurrentPlayerKey(), reinforcementPolicyId, request);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? UpsertPlayerProductionProgram(string? productionProgramId, PlayerProductionProgramCommandRequest request)
    {
        lock (_sync)
        {
            _playerFaction.UpsertProductionProgram(_world, _playerStateStore, GetCurrentPlayerKey(), productionProgramId, request);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? UpsertPlayerAssignment(string assetId, PlayerAssetAssignmentCommandRequest request)
    {
        lock (_sync)
        {
            _playerFaction.UpsertAssignment(_world, _playerStateStore, GetCurrentPlayerKey(), assetId, request);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public PlayerFactionSnapshot? UpdatePlayerStrategicIntent(PlayerStrategicIntentCommandRequest request)
    {
        lock (_sync)
        {
            _playerFaction.UpdateStrategicIntent(_world, _playerStateStore, GetCurrentPlayerKey(), request);
            return GetPlayerFactionSnapshotUnsafe();
        }
    }

    public FactionSnapshot CreateFaction(string factionId)
    {
        lock (_sync)
        {
            if (_world.Factions.Any(candidate => string.Equals(candidate.Id, factionId, StringComparison.Ordinal)))
            {
                throw new InvalidOperationException($"Faction '{factionId}' already exists in the current world.");
            }

            var faction = _worldSeedingService.CreateFaction(factionId);
            _world.Factions.Add(faction);

            var policy = _worldSeedingService.CreatePolicies([faction]).Single();
            _world.Policies.Add(policy);

            var factionCommander = CreateFactionCommander(faction);
            _world.Commanders.Add(factionCommander);
            faction.CommanderIds.Add(factionCommander.Id);

            new GeopoliticalSimulationService().Update(_world, 0f, []);
            PublishSnapshotRefreshUnsafe("create-faction", $"Created faction {factionId}", "faction", factionId);
            return _engine.BuildSnapshot(_world, _sequence).Factions.First(candidate => candidate.Id == factionId);
        }
    }

    public ShipSnapshot SpawnShip(SpawnShipCommandRequest request)
    {
        lock (_sync)
        {
            var faction = _world.Factions.FirstOrDefault(candidate => string.Equals(candidate.Id, request.FactionId, StringComparison.Ordinal))
              ?? throw new InvalidOperationException($"Faction '{request.FactionId}' does not exist in the current world.");
            var system = _world.Systems.FirstOrDefault(candidate => string.Equals(candidate.Definition.Id, request.SystemId, StringComparison.Ordinal))
              ?? throw new InvalidOperationException($"System '{request.SystemId}' does not exist in the current world.");
            var definition = ResolveShipDefinition(request, faction.Id);
            var shipId = $"ship-{faction.Id}-{definition.Id}-{Guid.NewGuid():N}".ToLowerInvariant();
            var spawnPosition = ResolveSpawnPosition(system.Definition.Id);
            var homeStation = _world.Stations.FirstOrDefault(candidate =>
                string.Equals(candidate.FactionId, faction.Id, StringComparison.Ordinal)
                && string.Equals(candidate.SystemId, system.Definition.Id, StringComparison.Ordinal));
            var defaultBehavior = CreateSpawnBehavior(request, definition, system.Definition.Id, homeStation);

            var ship = new ShipRuntime
            {
                Id = shipId,
                SystemId = system.Definition.Id,
                Definition = definition,
                FactionId = faction.Id,
                Position = spawnPosition,
                TargetPosition = spawnPosition,
                SpatialState = SpatialBuilder.CreateInitialShipSpatialState(system.Definition.Id, spawnPosition, _world.Celestials),
                DefaultBehavior = defaultBehavior,
                Skills = ShipBootstrapPolicy.CreateSkills(definition),
                Health = definition.Hull,
            };

            _world.Ships.Add(ship);
            EnsureShipCommander(faction, ship);
            new GeopoliticalSimulationService().Update(_world, 0f, []);
            PublishSnapshotRefreshUnsafe("spawn-ship", $"Spawned ship {ship.Id}", "ship", ship.Id);
            return GetShipSnapshotUnsafe(ship.Id)
              ?? throw new InvalidOperationException($"Ship '{ship.Id}' could not be projected.");
        }
    }

    public StationSnapshot SpawnStation(SpawnStationCommandRequest request)
    {
        lock (_sync)
        {
            var faction = _world.Factions.FirstOrDefault(candidate => string.Equals(candidate.Id, request.FactionId, StringComparison.Ordinal))
              ?? throw new InvalidOperationException($"Faction '{request.FactionId}' does not exist in the current world.");
            var system = _world.Systems.FirstOrDefault(candidate => string.Equals(candidate.Definition.Id, request.SystemId, StringComparison.Ordinal))
              ?? throw new InvalidOperationException($"System '{request.SystemId}' does not exist in the current world.");
            var objective = StationSimulationService.NormalizeStationObjective(request.Objective);
            var label = string.IsNullOrWhiteSpace(request.Label)
              ? $"{faction.Label} {ToTitleCaseToken(objective)} {CountFactionStationsInSystem(faction.Id, system.Definition.Id) + 1}"
              : request.Label.Trim();
            var stationId = $"station-{faction.Id}-{objective}-{Guid.NewGuid():N}".ToLowerInvariant();
            var position = ResolveStationSpawnPosition(system.Definition.Id);
            var station = new StationRuntime
            {
                Id = stationId,
                SystemId = system.Definition.Id,
                Label = label,
                Color = faction.Color,
                Objective = objective,
                Position = position,
                FactionId = faction.Id,
                PolicySetId = faction.DefaultPolicySetId,
                Health = 600f,
                MaxHealth = 600f,
            };

            foreach (var moduleId in BuildStarterStationModules(faction.Id, objective))
            {
                AddStationModule(_world, station, moduleId);
            }

            station.PopulationCapacity = GetStationSupportedPopulation(_world.ModuleDefinitions, station);
            station.WorkforceRequired = GetStationRequiredWorkforce(_world.ModuleDefinitions, station);
            _world.Stations.Add(station);

            new GeopoliticalSimulationService().Update(_world, 0f, []);
            PublishSnapshotRefreshUnsafe("spawn-station", $"Spawned station {station.Id}", "station", station.Id);
            return _engine.BuildSnapshot(_world, _sequence).Stations.First(candidate => candidate.Id == station.Id);
        }
    }

    public ChannelReader<WorldDelta> Subscribe(ObserverScope scope, long afterSequence, CancellationToken cancellationToken)
    {
        var channel = Channel.CreateUnbounded<WorldDelta>(new UnboundedChannelOptions
        {
            SingleReader = true,
            SingleWriter = false,
        });

        Guid subscriberId;
        lock (_sync)
        {
            subscriberId = Guid.NewGuid();
            _subscribers.Add(subscriberId, new SubscriptionState(scope, channel));

            foreach (var delta in _history.Where((candidate) => candidate.Sequence > afterSequence))
            {
                var filtered = FilterDeltaForScope(delta, scope);
                if (HasMeaningfulDelta(filtered))
                {
                    channel.Writer.TryWrite(filtered);
                }
            }
        }

        cancellationToken.Register(() => Unsubscribe(subscriberId));
        return channel.Reader;
    }

    public void Tick(float deltaSeconds)
    {
        WorldDelta? delta = null;
        lock (_sync)
        {
            delta = _engine.Tick(_world, deltaSeconds, ++_sequence);
            if (!HasMeaningfulDelta(delta))
            {
                return;
            }

            _history.Enqueue(delta);
            while (_history.Count > DeltaHistoryLimit)
            {
                _history.Dequeue();
            }

            foreach (var subscriber in _subscribers.Values.ToList())
            {
                var filtered = FilterDeltaForScope(delta, subscriber.Scope);
                if (HasMeaningfulDelta(filtered))
                {
                    subscriber.Channel.Writer.TryWrite(filtered);
                }
            }
        }
    }

    public WorldSnapshot Reset()
    {
        lock (_sync)
        {
            if (_currentScenarioPath is not null)
            {
                ReplaceWorldUnsafe(
                  _worldBuilder.BuildFromScenario(_scenarioLoader.Load(_currentScenarioPath)),
                  "reset",
                  "World reset requested");
            }
            else if (_currentWorldGenerationOptions is not null)
            {
                ReplaceWorldUnsafe(
                  _worldBuilder.BuildFromGeneration(_currentWorldGenerationOptions),
                  "reset",
                  "World reset requested");
            }
            else
            {
                throw new InvalidOperationException("No world source is configured.");
            }

            return _engine.BuildSnapshot(_world, _sequence);
        }
    }

    private void ReplaceWorldUnsafe(SimulationWorld world, string eventKind, string eventMessage)
    {
        _world = world;
        _playerStateStore.Clear();
        _sequence += 1;
        _history.Clear();

        var eventTime = DateTimeOffset.UtcNow;
        var worldDelta = new WorldDelta(
          _sequence,
          _world.TickIntervalMs,
          _world.OrbitalTimeSeconds,
          _orbitalSimulation,
          eventTime,
          true,
          [new SimulationEventRecord("world", "world", eventKind, eventMessage, eventTime, "world", "universe", "world")],
          [],
          [],
          [],
          [],
          [],
          [],
          [],
          [],
          [],
          null);

        _history.Enqueue(worldDelta);
        foreach (var subscriber in _subscribers.Values.ToList())
        {
            subscriber.Channel.Writer.TryWrite(FilterDeltaForScope(worldDelta, subscriber.Scope));
        }
    }

    private void PublishSnapshotRefreshUnsafe(
      string eventKind,
      string eventMessage,
      string entityKind,
      string entityId,
      string scopeKind = "universe",
      string? scopeEntityId = null)
    {
        _sequence += 1;
        var eventTime = DateTimeOffset.UtcNow;
        var worldDelta = new WorldDelta(
          _sequence,
          _world.TickIntervalMs,
          _world.OrbitalTimeSeconds,
          _orbitalSimulation,
          eventTime,
          true,
          [new SimulationEventRecord(entityKind, entityId, eventKind, eventMessage, eventTime, "world", scopeKind, scopeEntityId)],
          [],
          [],
          [],
          [],
          [],
          [],
          [],
          [],
          [],
          null);

        _history.Enqueue(worldDelta);
        while (_history.Count > DeltaHistoryLimit)
        {
            _history.Dequeue();
        }

        foreach (var subscriber in _subscribers.Values.ToList())
        {
            subscriber.Channel.Writer.TryWrite(FilterDeltaForScope(worldDelta, subscriber.Scope));
        }
    }

    private ShipSnapshot? GetShipSnapshotUnsafe(string shipId) =>
      _engine.BuildSnapshot(_world, _sequence).Ships.FirstOrDefault(ship => ship.Id == shipId);

    private PlayerFactionSnapshot? GetPlayerFactionSnapshotUnsafe() =>
      _playerFactionProjection.ToSnapshot(_playerFaction.TryGetDomain(_playerStateStore, GetCurrentPlayerKey()));

    private FactionRuntime CreatePlayerOwnedFactionUnsafe(PlayerFactionRuntime player, RaceDefinition race)
    {
        var playerFaction = new FactionRuntime
        {
            Id = player.SovereignFactionId,
            Label = player.PersonaName ?? player.Label,
            Color = ResolvePlayerFactionColor(race.Id),
            Credits = 25000f,
        };

        _world.Factions.Add(playerFaction);

        var policy = _worldSeedingService.CreatePolicies([playerFaction]).Single();
        var templateFaction = _staticData.FactionDefinitions.Values
          .Where(candidate => string.Equals(candidate.RaceId, race.Id, StringComparison.Ordinal))
          .OrderBy(candidate => candidate.Id, StringComparer.Ordinal)
          .Select(candidate => _world.Factions.FirstOrDefault(worldFaction => string.Equals(worldFaction.Id, candidate.Id, StringComparison.Ordinal)))
          .FirstOrDefault(candidate => candidate is not null);
        if (templateFaction?.DefaultPolicySetId is { } racePolicyId
            && _world.Policies.FirstOrDefault(candidate => candidate.Id == racePolicyId) is { } racePolicy)
        {
            policy.TradeAccessPolicy = racePolicy.TradeAccessPolicy;
            policy.DockingAccessPolicy = racePolicy.DockingAccessPolicy;
            policy.ConstructionAccessPolicy = racePolicy.ConstructionAccessPolicy;
            policy.OperationalRangePolicy = racePolicy.OperationalRangePolicy;
            policy.CombatEngagementPolicy = racePolicy.CombatEngagementPolicy;
            policy.FleeHullRatio = racePolicy.FleeHullRatio;
            policy.AvoidHostileSystems = racePolicy.AvoidHostileSystems;
            foreach (var systemId in racePolicy.BlacklistedSystemIds)
            {
                policy.BlacklistedSystemIds.Add(systemId);
            }
        }
        _world.Policies.Add(policy);

        var factionCommander = CreateFactionCommander(playerFaction);
        _world.Commanders.Add(factionCommander);
        playerFaction.CommanderIds.Add(factionCommander.Id);
        return playerFaction;
    }

    private string ResolveStarterSystemIdUnsafe()
    {
        return _world.Systems
                 .Select(system => system.Definition.Id)
                 .OrderBy(systemId => systemId, StringComparer.Ordinal)
                 .FirstOrDefault()
               ?? throw new InvalidOperationException("No systems are available for player onboarding.");
    }

    private void SpawnPlayerStarterShipUnsafe(FactionRuntime playerFaction, string systemId)
    {
        var request = new SpawnShipCommandRequest(
          playerFaction.Id,
          systemId,
          StarterPlayerShipId,
          Idle);
        var system = _world.Systems.First(candidate => string.Equals(candidate.Definition.Id, request.SystemId, StringComparison.Ordinal));
        var definition = ResolveShipDefinition(request, playerFaction.Id);
        var shipId = $"ship-{playerFaction.Id}-{definition.Id}-{Guid.NewGuid():N}".ToLowerInvariant();
        var spawnPosition = ResolveSpawnPosition(system.Definition.Id);
        var defaultBehavior = CreateSpawnBehavior(request, definition, system.Definition.Id, null);

        var ship = new ShipRuntime
        {
            Id = shipId,
            SystemId = system.Definition.Id,
            Definition = definition,
            FactionId = playerFaction.Id,
            Position = spawnPosition,
            TargetPosition = spawnPosition,
            SpatialState = SpatialBuilder.CreateInitialShipSpatialState(system.Definition.Id, spawnPosition, _world.Celestials),
            DefaultBehavior = defaultBehavior,
            Skills = ShipBootstrapPolicy.CreateSkills(definition),
            Health = definition.Hull,
        };

        _world.Ships.Add(ship);
        EnsureShipCommander(playerFaction, ship);
        new GeopoliticalSimulationService().Update(_world, 0f, []);
    }

    private string ResolvePlayerFactionColor(string raceId) =>
      raceId switch
      {
          "argon" => "#3b82f6",
          "boron" => "#14b8a6",
          "paranid" => "#eab308",
          "split" => "#b91c1c",
          "teladi" => "#22c55e",
          "terran" => "#38bdf8",
          "xenon" => "#9ca3af",
          _ => "#94a3b8",
      };

    private string GetCurrentPlayerKey() => _playerIdentityResolver.GetRequiredEffectivePlayerId().ToString("N");

    private bool CanCurrentActorAccessGm() => _playerIdentityResolver.CanAccessGm();

    private string GetCurrentActorSourceId() =>
      _playerIdentityResolver.GetCurrentPlayerId()?.ToString("N") ?? "gm";

    private void ValidateShipOrderRequestUnsafe(string shipId, ShipOrderCommandRequest request)
    {
        var ship = _world.Ships.FirstOrDefault(candidate => candidate.Id == shipId)
          ?? throw new InvalidOperationException($"Ship '{shipId}' was not found.");

        if (!string.Equals(request.Kind, ShipOrderKinds.MineAndDeliver, StringComparison.Ordinal))
        {
            return;
        }

        if (!IsMiningShip(ship.Definition))
        {
            throw new InvalidOperationException($"{ship.Definition.Name} cannot accept Mine Resource because it does not have mining capability.");
        }

        if (string.IsNullOrWhiteSpace(request.ItemId))
        {
            throw new InvalidOperationException("Mine Resource requires a ware.");
        }

        if (!_world.ItemDefinitions.TryGetValue(request.ItemId, out var itemDefinition))
        {
            throw new InvalidOperationException($"Mine Resource references unknown ware '{request.ItemId}'.");
        }

        if (itemDefinition.CargoKind is null)
        {
            throw new InvalidOperationException($"Mine Resource ware '{request.ItemId}' is not mineable.");
        }

        if (!ship.Definition.SupportsCargoKind(itemDefinition.CargoKind.Value))
        {
            throw new InvalidOperationException($"{ship.Definition.Name} cannot mine '{request.ItemId}' because it cannot store '{itemDefinition.CargoKind.Value.ToDataValue()}'.");
        }
    }

    private ShipRuntime? EnqueueGmShipOrderUnsafe(string shipId, ShipOrderCommandRequest request)
    {
        var ship = _world.Ships.FirstOrDefault(candidate => candidate.Id == shipId);
        if (ship is null)
        {
            return null;
        }

        if (ship.OrderQueue.Count >= 8)
        {
            throw new InvalidOperationException("Order queue is full.");
        }

        ship.OrderQueue.Add(new ShipOrderRuntime
        {
            Id = $"order-{ship.Id}-{Guid.NewGuid():N}",
            Kind = request.Kind,
            SourceKind = ShipOrderSourceKind.Player,
            SourceId = GetCurrentActorSourceId(),
            Priority = request.Priority,
            InterruptCurrentPlan = request.InterruptCurrentPlan,
            Label = request.Label,
            TargetEntityId = request.TargetEntityId,
            TargetSystemId = request.TargetSystemId,
            TargetPosition = request.TargetPosition is null ? null : new Vector3(request.TargetPosition.X, request.TargetPosition.Y, request.TargetPosition.Z),
            SourceStationId = request.SourceStationId,
            DestinationStationId = request.DestinationStationId,
            ItemId = request.ItemId,
            NodeId = request.NodeId,
            ConstructionSiteId = request.ConstructionSiteId,
            ModuleId = request.ModuleId,
            WaitSeconds = MathF.Max(0f, request.WaitSeconds ?? 0f),
            Radius = MathF.Max(0f, request.Radius ?? 0f),
            MaxSystemRange = request.MaxSystemRange,
            KnownStationsOnly = request.KnownStationsOnly ?? false,
        });

        ship.ControlSourceKind = "gm-order";
        ship.ControlSourceId = ship.OrderQueue
          .Where(order => order.SourceKind == ShipOrderSourceKind.Player)
          .OrderByDescending(order => order.Priority)
          .ThenBy(order => order.CreatedAtUtc)
          .Select(order => order.Id)
          .FirstOrDefault();
        ship.ControlReason = request.Label ?? request.Kind;
        ship.NeedsReplan = true;
        ship.LastReplanReason = "gm-order-enqueued";
        ship.LastDeltaSignature = string.Empty;
        return ship;
    }

    private ShipRuntime? RemoveGmShipOrderUnsafe(string shipId, string orderId)
    {
        var ship = _world.Ships.FirstOrDefault(candidate => candidate.Id == shipId);
        if (ship is null)
        {
            return null;
        }

        ship.OrderQueue.RemoveAll(order => order.Id == orderId);
        ship.ControlSourceKind = ship.OrderQueue.Any(order => order.SourceKind == ShipOrderSourceKind.Player)
          ? "gm-order"
          : "gm-manual";
        ship.ControlSourceId = ship.OrderQueue
          .Where(order => order.SourceKind == ShipOrderSourceKind.Player)
          .OrderByDescending(order => order.Priority)
          .ThenBy(order => order.CreatedAtUtc)
          .Select(order => order.Id)
          .FirstOrDefault();
        ship.ControlReason = ship.OrderQueue
          .Where(order => order.SourceKind == ShipOrderSourceKind.Player)
          .OrderByDescending(order => order.Priority)
          .ThenBy(order => order.CreatedAtUtc)
          .Select(order => order.Label ?? order.Kind)
          .FirstOrDefault()
          ?? "manual-gm-control";
        ship.NeedsReplan = true;
        ship.LastReplanReason = "gm-order-removed";
        ship.LastDeltaSignature = string.Empty;
        return ship;
    }

    private ShipRuntime? ConfigureGmShipBehaviorUnsafe(string shipId, ShipDefaultBehaviorCommandRequest request)
    {
        var ship = _world.Ships.FirstOrDefault(candidate => candidate.Id == shipId);
        if (ship is null)
        {
            return null;
        }

        ship.DefaultBehavior.Kind = request.Kind;
        ship.DefaultBehavior.HomeSystemId = request.HomeSystemId ?? ship.SystemId;
        ship.DefaultBehavior.HomeStationId = request.HomeStationId;
        ship.DefaultBehavior.AreaSystemId = request.AreaSystemId;
        ship.DefaultBehavior.TargetEntityId = request.TargetEntityId;
        ship.DefaultBehavior.ItemId = request.ItemId;
        ship.DefaultBehavior.PreferredNodeId = request.PreferredNodeId;
        ship.DefaultBehavior.PreferredConstructionSiteId = request.PreferredConstructionSiteId;
        ship.DefaultBehavior.PreferredModuleId = request.PreferredModuleId;
        ship.DefaultBehavior.TargetPosition = request.TargetPosition is null
          ? null
          : new Vector3(request.TargetPosition.X, request.TargetPosition.Y, request.TargetPosition.Z);
        ship.DefaultBehavior.WaitSeconds = MathF.Max(0f, request.WaitSeconds ?? ship.DefaultBehavior.WaitSeconds);
        ship.DefaultBehavior.Radius = MathF.Max(0f, request.Radius ?? ship.DefaultBehavior.Radius);
        ship.DefaultBehavior.MaxSystemRange = Math.Max(0, request.MaxSystemRange ?? ship.DefaultBehavior.MaxSystemRange);
        ship.DefaultBehavior.KnownStationsOnly = request.KnownStationsOnly ?? ship.DefaultBehavior.KnownStationsOnly;
        ship.DefaultBehavior.PatrolPoints =
          (request.PatrolPoints ?? [])
          .Select(point => new Vector3(point.X, point.Y, point.Z))
          .ToList();
        ship.DefaultBehavior.PatrolIndex = 0;
        ship.DefaultBehavior.RepeatOrders =
          (request.RepeatOrders ?? [])
          .Select(template => new ShipOrderTemplateRuntime
          {
              Kind = template.Kind,
              Label = template.Label,
              TargetEntityId = template.TargetEntityId,
              TargetSystemId = template.TargetSystemId,
              TargetPosition = template.TargetPosition is null ? null : new Vector3(template.TargetPosition.X, template.TargetPosition.Y, template.TargetPosition.Z),
              SourceStationId = template.SourceStationId,
              DestinationStationId = template.DestinationStationId,
              ItemId = template.ItemId,
              NodeId = template.NodeId,
              ConstructionSiteId = template.ConstructionSiteId,
              ModuleId = template.ModuleId,
              WaitSeconds = template.WaitSeconds ?? 0f,
              Radius = template.Radius ?? 0f,
              MaxSystemRange = template.MaxSystemRange,
              KnownStationsOnly = template.KnownStationsOnly ?? false,
          })
          .ToList();
        ship.DefaultBehavior.RepeatIndex = 0;

        ship.ControlSourceKind = "gm-manual";
        ship.ControlSourceId = GetCurrentActorSourceId();
        ship.ControlReason = request.Kind;
        ship.NeedsReplan = true;
        ship.LastReplanReason = "gm-behavior-updated";
        ship.LastDeltaSignature = string.Empty;
        return ship;
    }

    private CommanderRuntime CreateFactionCommander(FactionRuntime faction) => new()
    {
        Id = $"commander-faction-{faction.Id}",
        Kind = CommanderKind.Faction,
        FactionId = faction.Id,
        ControlledEntityId = faction.Id,
        PolicySetId = faction.DefaultPolicySetId,
        Doctrine = "strategic-control",
    };

    private void EnsureShipCommander(FactionRuntime faction, ShipRuntime ship)
    {
        var factionCommander = _world.Commanders.FirstOrDefault(candidate =>
            string.Equals(candidate.Kind, CommanderKind.Faction, StringComparison.Ordinal)
            && string.Equals(candidate.FactionId, faction.Id, StringComparison.Ordinal));
        if (factionCommander is null)
        {
            return;
        }

        var commander = new CommanderRuntime
        {
            Id = $"commander-ship-{ship.Id}",
            Kind = CommanderKind.Ship,
            FactionId = faction.Id,
            ParentCommanderId = factionCommander.Id,
            ControlledEntityId = ship.Id,
            PolicySetId = factionCommander.PolicySetId,
            Doctrine = "ship-control",
            Skills = new CommanderSkillProfileRuntime
            {
                Leadership = Math.Clamp((ship.Skills.Navigation + ship.Skills.Combat + 1) / 2, 2, 5),
                Coordination = Math.Clamp((ship.Skills.Trade + ship.Skills.Mining + 1) / 2, 2, 5),
                Strategy = Math.Clamp((ship.Skills.Combat + ship.Skills.Construction + 1) / 2, 2, 5),
            },
        };

        ship.CommanderId = commander.Id;
        ship.PolicySetId = factionCommander.PolicySetId;
        factionCommander.SubordinateCommanderIds.Add(commander.Id);
        faction.CommanderIds.Add(commander.Id);
        _world.Commanders.Add(commander);
    }

    private ShipDefinition ResolveShipDefinition(SpawnShipCommandRequest request, string factionId)
    {
        if (!string.IsNullOrWhiteSpace(request.ShipId))
        {
            return _staticData.ShipDefinitions.TryGetValue(request.ShipId, out var explicitDefinition)
              ? explicitDefinition
              : throw new InvalidOperationException($"Ship '{request.ShipId}' is not defined in static data.");
        }

        return _staticData.ShipDefinitions.Values
          .Where(IsMiningShip)
          .OrderBy(definition => !definition.Owners.Contains(factionId, StringComparer.Ordinal))
          .ThenBy(definition => !definition.SupportsCargoKind(StorageKind.Solid))
          .ThenBy(definition => definition.Size != "small")
          .ThenBy(definition => definition.Id, StringComparer.Ordinal)
          .FirstOrDefault()
          ?? throw new InvalidOperationException("No mining ship definition is available in static data.");
    }

    private Vector3 ResolveSpawnPosition(string systemId)
    {
        var shipsInSystem = _world.Ships.Count(candidate => string.Equals(candidate.SystemId, systemId, StringComparison.Ordinal));
        var angle = shipsInSystem * 0.73f;
        return new Vector3(60f + (shipsInSystem * 12f), 0f, MathF.Sin(angle) * 34f);
    }

    private Vector3 ResolveStationSpawnPosition(string systemId)
    {
        var stationsInSystem = _world.Stations.Count(candidate => string.Equals(candidate.SystemId, systemId, StringComparison.Ordinal));
        var angle = stationsInSystem * 0.91f;
        var radius = 160f + (stationsInSystem * 42f);
        return new Vector3(MathF.Cos(angle) * radius, 0f, MathF.Sin(angle) * radius);
    }

    private IReadOnlyList<string> BuildStarterStationModules(string factionId, string objective)
    {
        var modules = new List<string>();

        EnsureStationModule(modules, StarterStationLayoutResolver.ResolveDockModuleId(factionId, _staticData.ModuleDefinitions));

        var powerModuleId = StarterStationLayoutResolver.ResolvePowerModuleId(factionId, _staticData.ModuleDefinitions);
        EnsureStationModule(modules, powerModuleId);

        var defaultContainerStorageModuleId = StarterStationLayoutResolver.ResolveRequiredStorageModuleIds(
          powerModuleId,
          factionId,
          _staticData.ModuleDefinitions,
          _staticData.ItemDefinitions,
          _staticData.Recipes)
          .FirstOrDefault(moduleId =>
          {
              return _staticData.ModuleDefinitions.TryGetValue(moduleId, out var definition)
                && definition is StorageModuleDefinition storageDefinition
                && storageDefinition.StorageKind == StorageKind.Container;
          });

        if (defaultContainerStorageModuleId is not null)
        {
            EnsureStationModule(modules, defaultContainerStorageModuleId);
        }

        var defaultSolidStorageModuleId = StarterStationLayoutResolver.ResolveRequiredStorageModuleIds(
          powerModuleId,
          factionId,
          _staticData.ModuleDefinitions,
          _staticData.ItemDefinitions,
          _staticData.Recipes)
          .FirstOrDefault(moduleId =>
          {
              return _staticData.ModuleDefinitions.TryGetValue(moduleId, out var definition)
                && definition is StorageModuleDefinition storageDefinition
                && storageDefinition.StorageKind == StorageKind.Solid;
          });

        if (defaultSolidStorageModuleId is not null)
        {
            EnsureStationModule(modules, defaultSolidStorageModuleId);
        }

        var objectiveModuleId = StarterStationLayoutResolver.ResolveObjectiveModuleId(objective, factionId, _staticData.ModuleDefinitions);
        if (!string.IsNullOrWhiteSpace(objectiveModuleId))
        {
            EnsureStationModule(modules, objectiveModuleId);
            foreach (var storageModuleId in StarterStationLayoutResolver.ResolveRequiredStorageModuleIds(
              objectiveModuleId,
              factionId,
              _staticData.ModuleDefinitions,
              _staticData.ItemDefinitions,
              _staticData.Recipes))
            {
                EnsureStationModule(modules, storageModuleId);
            }
        }

        return modules;
    }

    private static void EnsureStationModule(List<string> modules, string moduleId)
    {
        if (!modules.Contains(moduleId, StringComparer.Ordinal))
        {
            modules.Add(moduleId);
        }
    }

    private int CountFactionStationsInSystem(string factionId, string systemId) =>
      _world.Stations.Count(candidate =>
        string.Equals(candidate.FactionId, factionId, StringComparison.Ordinal)
        && string.Equals(candidate.SystemId, systemId, StringComparison.Ordinal));

    private static string ToTitleCaseToken(string value) =>
      string.Join(" ",
        value
          .Split(['-', '_', ' '], StringSplitOptions.RemoveEmptyEntries)
          .Select(part => part.Length == 0 ? part : char.ToUpperInvariant(part[0]) + part[1..]));

    private static DefaultBehaviorRuntime CreateSpawnBehavior(
      SpawnShipCommandRequest request,
      ShipDefinition definition,
      string systemId,
      StationRuntime? homeStation)
    {
        var requestedBehavior = request.BehaviorKind?.Trim();
        if (!string.IsNullOrWhiteSpace(requestedBehavior))
        {
            return new DefaultBehaviorRuntime
            {
                Kind = requestedBehavior,
                HomeSystemId = systemId,
                HomeStationId = homeStation?.Id,
                AreaSystemId = systemId,
                ItemId = string.Equals(requestedBehavior, LocalAutoMine, StringComparison.Ordinal) ? "ore" : null,
            };
        }

        if (IsMiningShip(definition) && homeStation is not null)
        {
            return new DefaultBehaviorRuntime
            {
                Kind = LocalAutoMine,
                HomeSystemId = systemId,
                HomeStationId = homeStation.Id,
                AreaSystemId = systemId,
            };
        }

        if (IsMiningShip(definition))
        {
            return new DefaultBehaviorRuntime
            {
                Kind = LocalAutoMine,
                HomeSystemId = systemId,
                HomeStationId = null,
                AreaSystemId = systemId,
                ItemId = "ore",
            };
        }

        return new DefaultBehaviorRuntime
        {
            Kind = HoldPosition,
            HomeSystemId = systemId,
            HomeStationId = homeStation?.Id,
            AreaSystemId = systemId,
            WaitSeconds = 4f,
            Radius = 24f,
        };
    }

    private static bool HasMeaningfulDelta(WorldDelta delta) =>
      delta.RequiresSnapshotRefresh
      || delta.Events.Count > 0
      || delta.Celestials.Count > 0
      || delta.Nodes.Count > 0
      || delta.Stations.Count > 0
      || delta.Claims.Count > 0
      || delta.ConstructionSites.Count > 0
      || delta.MarketOrders.Count > 0
      || delta.Policies.Count > 0
      || delta.Ships.Count > 0
      || delta.Factions.Count > 0
      || delta.Geopolitics is not null;

    private void Unsubscribe(Guid subscriberId)
    {
        lock (_sync)
        {
            if (!_subscribers.Remove(subscriberId, out var subscription))
            {
                return;
            }

            subscription.Channel.Writer.TryComplete();
        }
    }

    private WorldDelta FilterDeltaForScope(WorldDelta delta, ObserverScope scope)
    {
        if (string.Equals(scope.ScopeKind, "universe", StringComparison.OrdinalIgnoreCase))
        {
            return delta with
            {
                Events = delta.Events.Select((evt) => EnrichEventScope(evt)).ToList(),
                Scope = scope,
            };
        }

        var systemFilter = scope.SystemId;
        if (string.Equals(scope.ScopeKind, "local-celestial", StringComparison.OrdinalIgnoreCase) && systemFilter is null && scope.CelestialId is not null)
        {
            systemFilter = ResolveCelestialSystemId(scope.CelestialId);
        }

        return delta with
        {
            Events = delta.Events
            .Select((evt) => EnrichEventScope(evt))
            .Where((evt) => IsEventVisibleToScope(evt, scope, systemFilter))
            .ToList(),
            Celestials = delta.Celestials.Where((celestial) => systemFilter is null || celestial.SystemId == systemFilter).ToList(),
            Nodes = delta.Nodes.Where((node) => systemFilter is null || node.SystemId == systemFilter).ToList(),
            Stations = delta.Stations.Where((station) => systemFilter is null || station.SystemId == systemFilter).ToList(),
            Claims = delta.Claims.Where((claim) => systemFilter is null || claim.SystemId == systemFilter).ToList(),
            ConstructionSites = delta.ConstructionSites.Where((site) => systemFilter is null || site.SystemId == systemFilter).ToList(),
            MarketOrders = delta.MarketOrders.Where((order) => IsOrderVisibleToScope(order, systemFilter)).ToList(),
            Policies = string.Equals(scope.ScopeKind, "universe", StringComparison.OrdinalIgnoreCase) ? delta.Policies : [],
            Ships = delta.Ships.Where((ship) => systemFilter is null || ship.SystemId == systemFilter).ToList(),
            Factions = string.Equals(scope.ScopeKind, "universe", StringComparison.OrdinalIgnoreCase) ? delta.Factions : [],
            Geopolitics = string.Equals(scope.ScopeKind, "universe", StringComparison.OrdinalIgnoreCase) ? delta.Geopolitics : null,
            Scope = scope,
        };
    }

    private SimulationEventRecord EnrichEventScope(SimulationEventRecord evt)
    {
        if (!string.Equals(evt.ScopeKind, "universe", StringComparison.OrdinalIgnoreCase) || evt.ScopeEntityId is not null)
        {
            return evt;
        }

        return evt.EntityKind switch
        {
            "ship" => WithEntityScope(evt, "system", _world.Ships.FirstOrDefault((ship) => ship.Id == evt.EntityId)?.SystemId),
            "station" => WithEntityScope(evt, "system", _world.Stations.FirstOrDefault((station) => station.Id == evt.EntityId)?.SystemId),
            "node" => WithEntityScope(evt, "system", _world.Nodes.FirstOrDefault((node) => node.Id == evt.EntityId)?.SystemId),
            "celestial" => WithEntityScope(evt, "system", _world.Celestials.FirstOrDefault((c) => c.Id == evt.EntityId)?.SystemId),
            "claim" => WithEntityScope(evt, "system", _world.Claims.FirstOrDefault((claim) => claim.Id == evt.EntityId)?.SystemId),
            "construction-site" => WithEntityScope(evt, "system", _world.ConstructionSites.FirstOrDefault((site) => site.Id == evt.EntityId)?.SystemId),
            "market-order" => WithEntityScope(evt, "system", ResolveMarketOrderSystemId(evt.EntityId)),
            _ => evt,
        };
    }

    private static SimulationEventRecord WithEntityScope(SimulationEventRecord evt, string scopeKind, string? scopeEntityId) =>
      evt with
      {
          Family = evt.Kind.Contains("power", StringComparison.Ordinal) ? "power" :
          evt.Kind.Contains("construction", StringComparison.Ordinal) ? "construction" :
          evt.Kind.Contains("population", StringComparison.Ordinal) ? "population" :
          evt.Kind.Contains("claim", StringComparison.Ordinal) ? "claim" :
          "simulation",
          ScopeKind = scopeKind,
          ScopeEntityId = scopeEntityId,
      };

    private string? ResolveCelestialSystemId(string celestialId) =>
      _world.Celestials.FirstOrDefault((c) => c.Id == celestialId)?.SystemId;

    private string? ResolveMarketOrderSystemId(string orderId)
    {
        var order = _world.MarketOrders.FirstOrDefault((candidate) => candidate.Id == orderId);
        if (order?.StationId is not null)
        {
            return _world.Stations.FirstOrDefault((station) => station.Id == order.StationId)?.SystemId;
        }

        if (order?.ConstructionSiteId is not null)
        {
            return _world.ConstructionSites.FirstOrDefault((site) => site.Id == order.ConstructionSiteId)?.SystemId;
        }

        return null;
    }

    private bool IsOrderVisibleToScope(MarketOrderDelta order, string? systemFilter)
    {
        if (systemFilter is null)
        {
            return true;
        }

        if (order.StationId is not null)
        {
            return _world.Stations.Any((station) => station.Id == order.StationId && station.SystemId == systemFilter);
        }

        if (order.ConstructionSiteId is not null)
        {
            return _world.ConstructionSites.Any((site) => site.Id == order.ConstructionSiteId && site.SystemId == systemFilter);
        }

        return false;
    }

    private static bool IsEventVisibleToScope(SimulationEventRecord evt, ObserverScope scope, string? systemFilter)
    {
        return scope.ScopeKind switch
        {
            "universe" => true,
            "system" => evt.ScopeKind == "universe" || evt.ScopeEntityId == systemFilter,
            "local-celestial" => evt.ScopeKind == "universe" || evt.ScopeEntityId == systemFilter,
            _ => true,
        };
    }

    private sealed record SubscriptionState(ObserverScope Scope, Channel<WorldDelta> Channel);
}
