using Theriapolis.Core.Util;

namespace Theriapolis.Core.World.Generation.Stages;

/// <summary>
/// Stage 15 — SettlementAttributes
/// Assigns procedurally-generated attributes to all placed settlements.
/// Narrative anchors keep their canonical names; others get generated names.
/// </summary>
public sealed class SettlementAttributesStage : IWorldGenStage
{
    public string Name => "SettlementAttributes";

    private const int W = C.WORLD_WIDTH_TILES;
    private const int H = C.WORLD_HEIGHT_TILES;

    private static readonly int[] TierMinPop = { 0, 1_500_000, 30_000, 2_000, 50,   0 };
    private static readonly int[] TierMaxPop = { 0, 2_500_000, 200_000, 30_000, 2_000, 0 };

    public void Run(WorldGenContext ctx)
    {
        var world = ctx.World;
        var rng   = SeededRng.ForSubsystem(world.WorldSeed, C.RNG_SETTLE_ATTR);

        foreach (var s in world.Settlements)
        {
            if (s.IsPoi) { AssignPoiAttributes(world, s, rng); continue; }

            var macro = world.MacroCellForTile(world.TileAt(s.TileX, s.TileY));

            // Name (anchors already named)
            if (string.IsNullOrEmpty(s.Name))
                s.Name = NameGenerator.Generate(rng, macro.BiomeType);

            // Economy
            s.Economy = DeriveEconomy(world, s, macro, rng);

            // Governance
            s.Governance = DeriveGovernance(macro, rng);

            // Population
            if (s.Tier >= 1 && s.Tier <= 4)
            {
                float habBonus = world.Habitability?[s.TileX, s.TileY] ?? 0.5f;
                int min = TierMinPop[s.Tier];
                int max = TierMaxPop[s.Tier];
                s.Population = (int)(min + (max - min) * (0.3f + 0.7f * habBonus));
            }

            // Wealth
            s.WealthLevel = Math.Clamp(
                (world.Habitability?[s.TileX, s.TileY] ?? 0.5f) + rng.NextFloat(-0.1f, 0.1f),
                0f, 1f);

            // Clade ratios (simplified as strings referencing affinities)
            s.CladeRatios = macro.CladeAffinities.Length > 0
                ? macro.CladeAffinities
                : new[] { "mixed" };

            // Hybrid percentage
            var cov = macro.Covenant?.ToLowerInvariant() ?? "";
            s.HybridPct = (cov is "weak" or "nominal")
                ? rng.NextFloat(0.10f, 0.30f)
                : rng.NextFloat(0.005f, 0.05f);

            // Scent profile from economy
            s.ScentProfile = s.Economy switch
            {
                SettlementEconomy.Mining        => "coal, metal, dust",
                SettlementEconomy.Manufacturing => "smoke, timber, oil",
                SettlementEconomy.Fishing       => "salt, fish, tar",
                SettlementEconomy.Trade         => "spice, leather, woodsmoke",
                SettlementEconomy.Military      => "iron, leather, sweat",
                _                               => "grain, livestock, earth",
            };

            // River adjacency
            s.IsOnRiver = IsNearRiver(world, s.TileX, s.TileY);
        }

        ctx.LogMessage("[SettlementAttributes] Assigned attributes to all settlements.");
    }

    private static SettlementEconomy DeriveEconomy(WorldState world, Settlement s, Data.MacroCell macro, SeededRng rng)
    {
        // Count surrounding biomes in 10-tile radius
        int mountain = 0, forest = 0, grass = 0, coastal = 0;
        const int radius = 10;
        for (int dy = -radius; dy <= radius; dy++)
        for (int dx = -radius; dx <= radius; dx++)
        {
            int nx = s.TileX + dx, ny = s.TileY + dy;
            if ((uint)nx >= W || (uint)ny >= H) continue;
            var b = world.Tiles[nx, ny].Biome;
            if (b is BiomeId.MountainAlpine or BiomeId.MountainForested) mountain++;
            if (b is BiomeId.TemperateDeciduous or BiomeId.Boreal or BiomeId.SubtropicalForest) forest++;
            if (b is BiomeId.TemperateGrassland or BiomeId.Scrubland) grass++;
            if (b is BiomeId.Coastal or BiomeId.Beach or BiomeId.TidalFlat) coastal++;
        }

        if (macro.Development?.ToLowerInvariant().Contains("military") == true)
            return SettlementEconomy.Military;
        if (coastal > 15)
            return rng.NextBool() ? SettlementEconomy.Fishing : SettlementEconomy.Trade;
        if (mountain > 20)
            return SettlementEconomy.Mining;
        if (forest > 30)
            return SettlementEconomy.Manufacturing;
        if (grass > 20)
            return SettlementEconomy.Farming;
        if ((world.Habitability?[s.TileX, s.TileY] ?? 0f) > 0.7f)
            return SettlementEconomy.Trade;
        return SettlementEconomy.Farming;
    }

    private static SettlementGovernance DeriveGovernance(Data.MacroCell macro, SeededRng rng)
    {
        var dev = macro.Development?.ToLowerInvariant() ?? "";
        if (dev.Contains("military")) return SettlementGovernance.MilitaryCommandant;
        if (dev.Contains("industrial") || dev.Contains("urban")) return rng.NextBool() ? SettlementGovernance.Mayor : SettlementGovernance.Corporate;

        var clades = string.Join(",", macro.CladeAffinities ?? Array.Empty<string>()).ToLowerInvariant();
        if (clades.Contains("canid")) return rng.NextBool() ? SettlementGovernance.Mayor : SettlementGovernance.MilitaryCommandant;
        if (clades.Contains("cervid") || clades.Contains("bovid")) return SettlementGovernance.Council;

        var cov = macro.Covenant?.ToLowerInvariant() ?? "";
        if (cov is "nominal" or "weak") return SettlementGovernance.Anarchic;

        return (SettlementGovernance)rng.NextInt(0, 6);
    }

    private static bool IsNearRiver(WorldState world, int tx, int ty)
    {
        for (int dy = -2; dy <= 2; dy++)
        for (int dx = -2; dx <= 2; dx++)
        {
            int nx = tx + dx, ny = ty + dy;
            if ((uint)nx >= W || (uint)ny >= H) continue;
            if ((world.Tiles[nx, ny].Features & FeatureFlags.HasRiver) != 0) return true;
        }
        return false;
    }

    private static void AssignPoiAttributes(WorldState world, Settlement s, SeededRng rng)
    {
        if (string.IsNullOrEmpty(s.Name))
        {
            s.Name = s.PoiType switch
            {
                PoiType.ImperiumRuin       => "Imperium Ruin",
                PoiType.AbandonedMine      => "Abandoned Mine",
                PoiType.CultDen            => "Hidden Den",
                PoiType.NaturalCave        => "Natural Cave",
                PoiType.OvergrownSettlement => "Ruins",
                _                          => "Unknown Site",
            };
        }
    }
}
