using SpaceGame.Api.Universe.Bootstrap;

namespace SpaceGame.Api.Universe.Scenario;

public sealed class ScenarioValidationService(IStaticDataProvider staticData)
{
    public ScenarioDefinition CreateEmptyScenario(
      WorldGenerationOptions worldGenerationOptions,
      IReadOnlyList<SolarSystemDefinition> systems)
    {
        if (systems.Count == 0)
        {
            throw new InvalidOperationException("World generation produced no systems.");
        }

        return new ScenarioDefinition
        {
            WorldGeneration = worldGenerationOptions,
            InitialStations = [],
            ShipFormations = [],
            PatrolRoutes = [],
        };
    }

    public void Validate(
      ScenarioDefinition scenario,
      IReadOnlySet<string> availableSystemIds)
    {
        foreach (var station in scenario.InitialStations)
        {
            ValidateSystemExists(station.SystemId, $"station '{station.Label}' system", availableSystemIds);
            ValidateFactionId(station.FactionId, $"station '{station.Label}'");

            foreach (var moduleId in station.StartingModules)
            {
                ValidateModuleId(moduleId, $"station '{station.Label}' starting module");
            }
        }

        foreach (var formation in scenario.ShipFormations)
        {
            ValidateSystemExists(formation.SystemId, $"ship formation '{formation.ShipId}' system", availableSystemIds);
            ValidateFactionId(formation.FactionId, $"ship formation '{formation.ShipId}' in system '{formation.SystemId}'");
            ValidateShipId(formation.ShipId, $"ship formation in system '{formation.SystemId}'");

            foreach (var itemId in formation.StartingInventory.Keys)
            {
                ValidateItemId(itemId, $"ship formation '{formation.ShipId}' starting inventory");
            }
        }

        foreach (var route in scenario.PatrolRoutes)
        {
            ValidateSystemExists(route.SystemId, "patrol route system", availableSystemIds);
        }
    }

    private static string GetRequiredFactionId(string? factionId, string context)
    {
        if (!string.IsNullOrWhiteSpace(factionId))
        {
            return factionId;
        }

        throw new InvalidOperationException($"Scenario {context} is missing a factionId.");
    }

    private static void ValidateSystemExists(
      string systemId,
      string context,
      IReadOnlySet<string> availableSystemIds)
    {
        if (!availableSystemIds.Contains(systemId))
        {
            throw new InvalidOperationException($"Scenario {context} references unknown generated system '{systemId}'.");
        }
    }

    private void ValidateFactionId(string? factionId, string context)
    {
        var requiredFactionId = GetRequiredFactionId(factionId, context);
        if (!staticData.FactionDefinitions.ContainsKey(requiredFactionId))
        {
            throw new InvalidOperationException($"Scenario {context} references unknown faction '{requiredFactionId}'.");
        }
    }

    private void ValidateModuleId(string moduleId, string context)
    {
        if (!staticData.ModuleDefinitions.ContainsKey(moduleId))
        {
            throw new InvalidOperationException($"Scenario {context} references unknown module '{moduleId}'.");
        }
    }

    private void ValidateShipId(string shipId, string context)
    {
        if (!staticData.ShipDefinitions.ContainsKey(shipId))
        {
            throw new InvalidOperationException($"Scenario {context} references unknown ship '{shipId}'.");
        }
    }

    private void ValidateItemId(string itemId, string context)
    {
        if (!staticData.ItemDefinitions.ContainsKey(itemId))
        {
            throw new InvalidOperationException($"Scenario {context} references unknown item '{itemId}'.");
        }
    }
}
