using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;
using SpaceGame.Api.Shared.Runtime;
using SpaceGame.Api.Shared.Runtime;
using SpaceGame.Api.Universe.Simulation;

namespace SpaceGame.Api.Definitions;

public sealed class ConstructionDefinition
{
    public string? RecipeId { get; set; }
    public string FacilityCategory { get; set; } = "station";
    public List<string> RequiredModules { get; set; } = [];
    public List<RecipeInputDefinition> Requirements { get; set; } = [];
    public float CycleTime { get; set; }
    public float BatchSize { get; set; } = 1f;
    public float ProductsPerHour { get; set; }
    public float MaxEfficiency { get; set; } = 1f;
    public int Priority { get; set; }
}

public sealed class ItemPriceDefinition
{
    public float Min { get; set; }
    public float Max { get; set; }
    public float Avg { get; set; }
}

public sealed class ItemEffectDefinition
{
    public required string Type { get; set; }
    public float Product { get; set; }
}

public sealed class ItemProductionDefinition
{
    public float Time { get; set; }
    public float Amount { get; set; }
    public string Method { get; set; } = "default";
    public string Name { get; set; } = "Universal";
    public List<RecipeInputDefinition> Wares { get; set; } = [];
    public List<ItemEffectDefinition> Effects { get; set; } = [];
}

public sealed class StarDefinition
{
    public string Kind { get; set; } = "main-sequence";
    public required string Color { get; set; }
    public required string Glow { get; set; }
    public float Size { get; set; }
    public float OrbitRadius { get; set; }
    public float OrbitSpeed { get; set; }
    public float OrbitPhaseAtEpoch { get; set; }
}

public sealed class MoonDefinition
{
    public required string Label { get; set; }
    public float Size { get; set; }
    public required string Color { get; set; }
    public float OrbitRadius { get; set; }
    public float OrbitSpeed { get; set; }
    public float OrbitPhaseAtEpoch { get; set; }
    public float OrbitInclination { get; set; }
    public float OrbitLongitudeOfAscendingNode { get; set; }
}

public sealed class SolarSystemDefinition
{
    public required string Id { get; set; }
    public required string Label { get; set; }
    public required float[] Position { get; set; }
    public required List<StarDefinition> Stars { get; set; }
    public required AsteroidFieldDefinition AsteroidField { get; set; }
    public required List<ResourceNodeDefinition> ResourceNodes { get; set; }
    public required List<PlanetDefinition> Planets { get; set; }
}

public sealed class RaceDefinition
{
    public required string Id { get; set; }
    public required string Name { get; set; }
    public string Description { get; set; } = string.Empty;
    public string Icon { get; set; } = string.Empty;
    [JsonIgnore]
    public string Label => Name;
}

public sealed class FactionDefinition
{
    public required string Id { get; set; }
    public int Version { get; set; }
    public required string Name { get; set; }
    public string Description { get; set; } = string.Empty;
    public string Icon { get; set; } = string.Empty;
    public string? Race { get; set; }
    public List<FactionLicenseDefinition> Licenses { get; set; } = [];
    [JsonIgnore]
    public string Label => Name;
    [JsonIgnore]
    public string? RaceId => Race;
}

public sealed class FactionLicenseDefinition
{
    public required string Type { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Icon { get; set; } = string.Empty;
    public float Price { get; set; }
}

public sealed class AsteroidFieldDefinition
{
    public int DecorationCount { get; set; }
    public float RadiusOffset { get; set; }
    public float RadiusVariance { get; set; }
    public float HeightVariance { get; set; }
}

public sealed class ResourceNodeDefinition
{
    public string SourceKind { get; set; } = "local-space";
    public string? AnchorReference { get; set; }
    public float Angle { get; set; }
    public float RadiusOffset { get; set; }
    public float InclinationDegrees { get; set; }
    public int? AnchorPlanetIndex { get; set; }
    public int? AnchorMoonIndex { get; set; }
    public float OreAmount { get; set; }
    public required string ItemId { get; set; }
    public int ShardCount { get; set; }
}

public sealed class ItemDefinition
{
    public required string Id { get; set; }
    public required string Name { get; set; }
    public string Description { get; set; } = string.Empty;
    [JsonIgnore]
    public StorageKind? CargoKind { get; set; }
    public float Volume { get; set; } = 1f;
    public int Version { get; set; }
    public string FactoryName { get; set; } = string.Empty;
    public string Icon { get; set; } = string.Empty;
    public string Group { get; set; } = string.Empty;
    public ItemPriceDefinition? Price { get; set; }
    public List<string> Illegal { get; set; } = [];
    public List<ItemProductionDefinition> Production { get; set; } = [];
    public ConstructionDefinition? Construction { get; set; }
    [JsonPropertyName("transport")]
    public string Transport
    {
        get => CargoKind?.ToDataValue() ?? string.Empty;
        set => CargoKind = value.ToNullableStorageKind();
    }
}

public sealed class RecipeOutputDefinition
{
    public required string ItemId { get; set; }
    public float Amount { get; set; }
}

public sealed class RecipeInputDefinition
{
    public string ItemId { get; set; } = string.Empty;
    public float Amount { get; set; }
    [JsonPropertyName("ware")]
    public string Ware
    {
        set => ItemId = value;
    }
}

public sealed class ModuleDockDefinition
{
    public int Capacity { get; set; }
    public required string Size { get; set; }
}

public sealed class ModuleCargoDefinition
{
    public float Max { get; set; }
    public required string Type { get; set; }
}

public sealed class ModuleWorkforceDefinition
{
    [JsonPropertyName("capacity")]
    public float SupportedPopulation { get; set; }

    [JsonPropertyName("max")]
    public float RequiredWorkforce { get; set; }

    public string Race { get; set; } = string.Empty;
}

public sealed class ModuleMountDefinition
{
    public required string Group { get; set; }
    public required string Size { get; set; }
    public bool Hittable { get; set; }
    public List<string> Types { get; set; } = [];
}

public sealed class ModuleBuildRecipeDefinition
{
    public float Time { get; set; }
    public float Amount { get; set; }
    public string Method { get; set; } = "default";
    public string Name { get; set; } = "Universal";
    public List<RecipeInputDefinition> Wares { get; set; } = [];
}

public class ModuleDefinition
{
    public ModuleDefinition()
    {
    }

    [SetsRequiredMembers]
    protected ModuleDefinition(ModuleDefinition source)
    {
        Id = source.Id;
        Name = source.Name;
        Description = source.Description;
        Type = source.Type;
        ModuleType = source.ModuleType;
        ProductIds = [.. source.ProductIds];
        Radius = source.Radius;
        Hull = source.Hull;
        Version = source.Version;
        Macro = source.Macro;
        MakerRace = source.MakerRace;
        ExplosionDamage = source.ExplosionDamage;
        Price = source.Price;
        Owners = [.. source.Owners];
        Cargo = source.Cargo;
        SerializedWorkforce = source.SerializedWorkforce;
        Docks = [.. source.Docks];
        Shields = [.. source.Shields];
        Turrets = [.. source.Turrets];
        BuildRecipes = [.. source.BuildRecipes];
    }

    public required string Id { get; set; }
    public required string Name { get; set; }
    public string Description { get; set; } = string.Empty;
    public required string Type { get; set; }
    [JsonIgnore]
    public ModuleType ModuleType { get; set; }
    [JsonPropertyName("product")]
    public List<string> ProductIds { get; set; } = [];
    [JsonIgnore]
    public virtual IReadOnlyList<string> ProductItemIds => [];
    public float Radius { get; set; } = 12f;
    public float Hull { get; set; } = 100f;
    public int Version { get; set; }
    public string Macro { get; set; } = string.Empty;
    public string MakerRace { get; set; } = string.Empty;
    public int ExplosionDamage { get; set; }
    public ItemPriceDefinition? Price { get; set; }
    public List<string> Owners { get; set; } = [];
    public ModuleCargoDefinition? Cargo { get; set; }
    [JsonPropertyName("workForce")]
    public ModuleWorkforceDefinition? SerializedWorkforce { get; set; }
    public List<ModuleDockDefinition> Docks { get; set; } = [];
    public List<ModuleMountDefinition> Shields { get; set; } = [];
    public List<ModuleMountDefinition> Turrets { get; set; } = [];
    [JsonPropertyName("production")]
    public List<ModuleBuildRecipeDefinition> BuildRecipes { get; set; } = [];
}

public abstract class ProductionLaneModuleDefinition : ModuleDefinition
{
    [SetsRequiredMembers]
    protected ProductionLaneModuleDefinition(ModuleDefinition source, float requiredWorkforce)
        : base(source)
    {
        RequiredWorkforce = requiredWorkforce;
    }

    public float RequiredWorkforce { get; init; }
}

public sealed class ProductionModuleDefinition : ProductionLaneModuleDefinition
{
    [SetsRequiredMembers]
    internal ProductionModuleDefinition(ModuleDefinition source, float requiredWorkforce)
        : base(source, requiredWorkforce)
    {
        ProductItemIds = [.. source.ProductIds];
    }

    public override IReadOnlyList<string> ProductItemIds { get; } = [];
}

public sealed class BuildModuleDefinition : ProductionLaneModuleDefinition
{
    [SetsRequiredMembers]
    internal BuildModuleDefinition(ModuleDefinition source, float requiredWorkforce)
        : base(source, requiredWorkforce)
    {
    }
}

public sealed class HabitationModuleDefinition : ModuleDefinition
{
    [SetsRequiredMembers]
    internal HabitationModuleDefinition(ModuleDefinition source, float supportedPopulation)
        : base(source)
    {
        SupportedPopulation = supportedPopulation;
    }

    public float SupportedPopulation { get; init; }
}

public sealed class StorageModuleDefinition : ModuleDefinition
{
    [SetsRequiredMembers]
    internal StorageModuleDefinition(ModuleDefinition source, StorageKind storageKind, float storageCapacity)
        : base(source)
    {
        StorageKind = storageKind;
        StorageCapacity = storageCapacity;
    }

    public StorageKind StorageKind { get; init; }
    public float StorageCapacity { get; init; }
}

public sealed class ModuleRecipeDefinition
{
    public required string ModuleId { get; set; }
    public float Duration { get; set; }
    public required List<RecipeInputDefinition> Inputs { get; set; }
}

public sealed class RecipeDefinition
{
    public required string Id { get; set; }
    public required string Label { get; set; }
    public required string FacilityCategory { get; set; }
    public float Duration { get; set; }
    public int Priority { get; set; }
    public List<string> RequiredModules { get; set; } = [];
    public List<RecipeInputDefinition> Inputs { get; set; } = [];
    public List<RecipeOutputDefinition> Outputs { get; set; } = [];
    public string? ShipOutputId { get; set; }
}

public sealed class PlanetDefinition
{
    public required string Label { get; set; }
    public string PlanetType { get; set; } = "terrestrial";
    public string Shape { get; set; } = "sphere";
    public List<MoonDefinition> Moons { get; set; } = [];
    public float OrbitRadius { get; set; }
    public float OrbitSpeed { get; set; }
    public float OrbitEccentricity { get; set; }
    public float OrbitInclination { get; set; }
    public float OrbitLongitudeOfAscendingNode { get; set; }
    public float OrbitArgumentOfPeriapsis { get; set; }
    public float OrbitPhaseAtEpoch { get; set; }
    public float Size { get; set; }
    public required string Color { get; set; }
    public float Tilt { get; set; }
    public bool HasRing { get; set; }
}

public enum ShipPurpose
{
    [JsonStringEnumMemberName("auxiliary")]
    Auxiliary,
    [JsonStringEnumMemberName("mine")]
    Mine,
    [JsonStringEnumMemberName("build")]
    Build,
    [JsonStringEnumMemberName("fight")]
    Fight,
    [JsonStringEnumMemberName("trade")]
    Trade,
    [JsonStringEnumMemberName("salvage")]
    Salvage,
    [JsonStringEnumMemberName("dismantling")]
    Dismantling,
}

public enum ShipType
{
    [JsonStringEnumMemberName("resupplier")]
    Resupplier,
    [JsonStringEnumMemberName("miner")]
    Miner,
    [JsonStringEnumMemberName("carrier")]
    Carrier,
    [JsonStringEnumMemberName("fighter")]
    Fighter,
    [JsonStringEnumMemberName("heavyfighter")]
    HeavyFighter,
    [JsonStringEnumMemberName("destroyer")]
    Destroyer,
    [JsonStringEnumMemberName("largeminer")]
    LargeMiner,
    [JsonStringEnumMemberName("freighter")]
    Freighter,
    [JsonStringEnumMemberName("bomber")]
    Bomber,
    [JsonStringEnumMemberName("scavenger")]
    Scavenger,
    [JsonStringEnumMemberName("frigate")]
    Frigate,
    [JsonStringEnumMemberName("transporter")]
    Transporter,
    [JsonStringEnumMemberName("interceptor")]
    Interceptor,
    [JsonStringEnumMemberName("scout")]
    Scout,
    [JsonStringEnumMemberName("courier")]
    Courier,
    [JsonStringEnumMemberName("builder")]
    Builder,
    [JsonStringEnumMemberName("corvette")]
    Corvette,
    [JsonStringEnumMemberName("police")]
    Police,
    [JsonStringEnumMemberName("battleship")]
    Battleship,
    [JsonStringEnumMemberName("gunboat")]
    Gunboat,
    [JsonStringEnumMemberName("tug")]
    Tug,
    [JsonStringEnumMemberName("compactor")]
    Compactor,
}

public sealed class ShipDefinition
{
    public required string Id { get; set; }
    public int Version { get; set; }
    public required string Name { get; set; }
    public string Description { get; set; } = string.Empty;
    public string Size { get; set; } = string.Empty;
    public float ExplosionDamage { get; set; }
    public float Hull { get; set; }
    public Dictionary<string, float> Storage { get; set; } = new(StringComparer.Ordinal);
    public int People { get; set; }
    public ShipPurpose Purpose { get; set; }
    public string Thruster { get; set; } = string.Empty;
    public ShipType Type { get; set; }
    public float Mass { get; set; }
    public ShipInertiaDefinition? Inertia { get; set; }
    public ShipDragDefinition? Drag { get; set; }
    public List<ShipMountDefinition> Engines { get; set; } = [];
    public List<ShipMountDefinition> Shields { get; set; } = [];
    public List<ShipMountDefinition> Weapons { get; set; } = [];
    public List<ShipMountDefinition> Turrets { get; set; } = [];
    public List<ShipCargoDefinition> Cargo { get; set; } = [];
    public List<ModuleDockDefinition> Docks { get; set; } = [];
    public List<string> Owners { get; set; } = [];
    public ItemPriceDefinition? Price { get; set; }
    public List<ItemProductionDefinition> Production { get; set; } = [];
    [JsonIgnore]
    public float Speed => InferLocalSpeed(Size);
    [JsonIgnore]
    public float WarpSpeed => InferWarpSpeed(Size);
    [JsonIgnore]
    public float FtlSpeed => InferFtlSpeed(Size);
    [JsonIgnore]
    public float SpoolTime => InferSpoolTime(Size);
    public float GetTotalCargoCapacity() => Cargo.Sum(entry => entry.Max);

    public float GetCargoCapacity(StorageKind kind) =>
      Cargo
        .Where(entry => entry.Types.Any(type => type.ToNullableStorageKind() == kind))
        .Sum(entry => entry.Max);

    public bool SupportsCargoKind(StorageKind kind) =>
      GetCargoCapacity(kind) > 0f;

    private static float InferWarpSpeed(string size) =>
      size switch
      {
          "extrasmall" => 4.8f,
          "small" => 4.2f,
          "medium" => 3.4f,
          "large" => 2.4f,
          "extralarge" => 1.8f,
          _ => 3f,
      };

    private static float InferLocalSpeed(string size) =>
      size switch
      {
          "extrasmall" => 420f,
          "small" => 320f,
          "medium" => 230f,
          "large" => 150f,
          "extralarge" => 110f,
          _ => 200f,
      };

    private static float InferFtlSpeed(string size) =>
      size switch
      {
          "extrasmall" => 1f,
          "small" => 0.85f,
          "medium" => 0.7f,
          "large" => 0.55f,
          "extralarge" => 0.45f,
          _ => 0.6f,
      };

    private static float InferSpoolTime(string size) =>
      size switch
      {
          "extrasmall" => 0.8f,
          "small" => 1f,
          "medium" => 1.4f,
          "large" => 2f,
          "extralarge" => 2.6f,
          _ => 1.5f,
      };
}

public sealed class ShipInertiaDefinition
{
    public float Pitch { get; set; }
    public float Yaw { get; set; }
    public float Roll { get; set; }
}

public sealed class ShipDragDefinition
{
    public float Forward { get; set; }
    public float Reverse { get; set; }
    public float Horizontal { get; set; }
    public float Vertical { get; set; }
    public float Pitch { get; set; }
    public float Yaw { get; set; }
    public float Roll { get; set; }
}

public sealed class ShipMountDefinition
{
    public string? Group { get; set; }
    public required string Size { get; set; }
    public bool Hittable { get; set; }
    public List<string> Types { get; set; } = [];
}

public sealed class ShipCargoDefinition
{
    public float Max { get; set; }
    public List<string> Types { get; set; } = [];
}

public sealed class ScenarioDefinition
{
    public required WorldGenerationOptions WorldGeneration { get; set; }
    public required List<InitialStationDefinition> InitialStations { get; set; }
    public required List<ShipFormationDefinition> ShipFormations { get; set; }
    public required List<PatrolRouteDefinition> PatrolRoutes { get; set; }
}

public sealed class InitialStationDefinition
{
    public required string SystemId { get; set; }
    public string Label { get; set; } = "Orbital Station";
    public string Color { get; set; } = "#8df0d2";
    public string Objective { get; set; } = "general";
    public List<string> StartingModules { get; set; } = [];
    public string? FactionId { get; set; }
    public int? PlanetIndex { get; set; }
    public int? LagrangeSide { get; set; }
    public float[]? Position { get; set; }
}

public sealed class ShipFormationDefinition
{
    public required string ShipId { get; set; }
    public int Count { get; set; }
    public required float[] Center { get; set; }
    public required string SystemId { get; set; }
    public string? FactionId { get; set; }
    public Dictionary<string, float> StartingInventory { get; set; } = new(StringComparer.Ordinal);
}

public sealed class PatrolRouteDefinition
{
    public required string SystemId { get; set; }
    public required List<float[]> Points { get; set; }
}
