using Theriapolis.Core.Util;

namespace Theriapolis.Core.World.Generation.Stages;

/// <summary>
/// Stage 19 — FactionInfluenceGen
/// Radiates influence for the three primary factions outward from seed settlements.
/// Stored in WorldState.FactionInfluence as a float[3, W, H] map.
/// </summary>
public sealed class FactionInfluenceGenStage : IWorldGenStage
{
    public string Name => "FactionInfluenceGen";

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

    public void Run(WorldGenContext ctx)
    {
        var world = ctx.World;
        var influence = new FactionInfluenceMap();

        // ── Seed points for each faction ──────────────────────────────────────

        // Covenant Enforcers (0): strong in capital, Tier 2 cities, "strong"/"moderate" covenant regions
        foreach (var s in world.Settlements.Where(s => !s.IsPoi && s.Tier <= 2))
        {
            float strength = s.Tier == 1 ? 1.0f : 0.7f;
            RadiateInfluence(world, influence, (int)FactionId.CovenantEnforcers, s.TileX, s.TileY, strength);
        }
        RadiateFromMacroCovenant(world, influence, (int)FactionId.CovenantEnforcers,
            cov => cov is "strong" or "moderate", 0.4f);

        // Inheritors (1): strong in predator-majority frontier zones
        RadiateFromMacroCovenant(world, influence, (int)FactionId.Inheritors,
            cov => cov is "weak" or "nominal", 0.6f,
            dev => dev is "frontier" or "wilderness",
            clades => CladeSetContains(clades, "canid", "felid", "ursid"));

        // Thorn Council (2): strong in prey-majority zones, urban progressive centers
        RadiateFromMacroCovenant(world, influence, (int)FactionId.ThornCouncil,
            cov => cov is "moderate" or "weak", 0.5f,
            dev => dev is "urban" or "agricultural",
            clades => CladeSetContains(clades, "cervid", "bovid", "leporid"));

        // Thorn Council also present in the Tangles
        foreach (var s in world.Settlements.Where(s => s.Anchor == NarrativeAnchor.TheTangles))
            RadiateInfluence(world, influence, (int)FactionId.ThornCouncil, s.TileX, s.TileY, 0.4f);

        // Inheritors: anti-correlated with Enforcers (suppress where Enforcers are strong)
        for (int y = 0; y < H; y++)
        for (int x = 0; x < W; x++)
        {
            float enforcer = influence.Get((int)FactionId.CovenantEnforcers, x, y);
            float inheritor = influence.Get((int)FactionId.Inheritors, x, y);
            influence.Set((int)FactionId.Inheritors, x, y, Math.Max(0f, inheritor - enforcer * 0.6f));
        }

        world.FactionInfluence = influence;
        ctx.LogMessage("[FactionInfluenceGen] Faction influence maps computed.");
    }

    private static void RadiateInfluence(
        WorldState world,
        FactionInfluenceMap map,
        int faction,
        int cx, int cy,
        float strength)
    {
        int radius = (int)C.FACTION_INFLUENCE_RADIUS;
        for (int dy = -radius; dy <= radius; dy++)
        for (int dx = -radius; dx <= radius; dx++)
        {
            int nx = cx + dx, ny = cy + dy;
            if ((uint)nx >= W || (uint)ny >= H) continue;
            float dist = MathF.Sqrt(dx * dx + dy * dy);
            float falloff = Math.Max(0f, 1f - dist / C.FACTION_INFLUENCE_RADIUS);
            map.Add(faction, nx, ny, strength * falloff);
        }
    }

    private static void RadiateFromMacroCovenant(
        WorldState world,
        FactionInfluenceMap map,
        int faction,
        Func<string, bool> covenantFilter,
        float strength,
        Func<string, bool>? devFilter = null,
        Func<string[], bool>? cladeFilter = null)
    {
        for (int my = 0; my < C.MACRO_GRID_HEIGHT; my++)
        for (int mx = 0; mx < C.MACRO_GRID_WIDTH; mx++)
        {
            var cell = world.MacroGrid![mx, my];
            if (!covenantFilter(cell.Covenant?.ToLowerInvariant() ?? "")) continue;
            if (devFilter != null && !devFilter(cell.Development?.ToLowerInvariant() ?? "")) continue;
            if (cladeFilter != null && !cladeFilter(cell.CladeAffinities)) continue;

            // Tile range for this macro cell
            int tileW = W / C.MACRO_GRID_WIDTH;
            int tileH = H / C.MACRO_GRID_HEIGHT;
            int baseTx = mx * tileW + tileW / 2;
            int baseTy = my * tileH + tileH / 2;

            RadiateInfluence(world, map, faction, baseTx, baseTy, strength);
        }
    }

    private static bool CladeSetContains(string[] clades, params string[] targets)
    {
        foreach (var c in clades)
        foreach (var t in targets)
            if (c.ToLowerInvariant().Contains(t)) return true;
        return false;
    }
}
