using Theriapolis.Core.World.Polylines;

namespace Theriapolis.Core.World;

/// <summary>
/// Phase 5 M5: per-chunk threat-tier index. Drives which template each
/// <see cref="Tactical.SpawnKind"/> instantiates: zone 0 = safest (footpads,
/// pups), zone 4 = deepest wilds (captains, dire wolves, brown bears).
///
/// Computed once per chunk at instantiation time from biome + distance to
/// player-start + distance to nearest road + distance to nearest settlement.
/// Stored on <see cref="Tactical.TacticalChunk.DangerZone"/> and folded into
/// the chunk's hash so determinism tests catch any formula drift.
/// </summary>
public static class DangerZone
{
    /// <summary>
    /// Compute the danger zone for a given world-tile center. Pass the
    /// world's player-start tile so distance-from-start is meaningful even
    /// before the actual <see cref="Entities.PlayerActor"/> spawns.
    /// </summary>
    public static int Compute(int worldTileX, int worldTileY, WorldState world, int startTileX, int startTileY)
    {
        int zone = 0;
        zone += DistanceFromStartZone(worldTileX, worldTileY, startTileX, startTileY);
        zone += DistanceFromRoadZone(worldTileX, worldTileY, world);
        zone += DistanceFromSettlementZone(worldTileX, worldTileY, world);
        zone += BiomeDangerBonus(in world.TileAt(
            System.Math.Clamp(worldTileX, 0, C.WORLD_WIDTH_TILES - 1),
            System.Math.Clamp(worldTileY, 0, C.WORLD_HEIGHT_TILES - 1)));
        return System.Math.Clamp(zone, C.DANGER_ZONE_MIN, C.DANGER_ZONE_MAX);
    }

    /// <summary>
    /// Convenience: pick the player-start tile from the world (Tier-1
    /// settlement if any, else map centre) and compute the zone.
    /// </summary>
    public static int Compute(int worldTileX, int worldTileY, WorldState world)
    {
        var (sx, sy) = ResolveStartTile(world);
        return Compute(worldTileX, worldTileY, world, sx, sy);
    }

    private static int DistanceFromStartZone(int x, int y, int startX, int startY)
    {
        int dist = ChebyshevDistance(x, y, startX, startY);
        return dist / C.DANGER_DIST_FROM_START_PER_ZONE;
    }

    private static int DistanceFromRoadZone(int worldTileX, int worldTileY, WorldState world)
    {
        if (world.Roads.Count == 0) return 1;  // no roads at all → treat as remote
        int worldPxX = worldTileX * C.WORLD_TILE_PIXELS + C.WORLD_TILE_PIXELS / 2;
        int worldPxY = worldTileY * C.WORLD_TILE_PIXELS + C.WORLD_TILE_PIXELS / 2;
        float minDistSq = float.MaxValue;
        foreach (var road in world.Roads)
        {
            foreach (var pt in road.Points)
            {
                float dx = pt.X - worldPxX;
                float dy = pt.Y - worldPxY;
                float d2 = dx * dx + dy * dy;
                if (d2 < minDistSq) minDistSq = d2;
            }
        }
        float distTiles = (float)System.Math.Sqrt(minDistSq) / C.WORLD_TILE_PIXELS;
        return distTiles > C.DANGER_DIST_FROM_ROAD_THRESHOLD ? 1 : 0;
    }

    private static int DistanceFromSettlementZone(int worldTileX, int worldTileY, WorldState world)
    {
        if (world.Settlements.Count == 0) return 1;
        int minDistTiles = int.MaxValue;
        foreach (var s in world.Settlements)
        {
            int d = ChebyshevDistance(worldTileX, worldTileY, s.TileX, s.TileY);
            if (d < minDistTiles) minDistTiles = d;
        }
        return minDistTiles > C.DANGER_DIST_FROM_SETTLE_THRESHOLD ? 1 : 0;
    }

    private static int BiomeDangerBonus(in WorldTile tile) => tile.Biome switch
    {
        // Settled / safe biomes
        BiomeId.TemperateGrassland => 0,
        BiomeId.TemperateDeciduous => 0,
        BiomeId.RiverValley        => 0,
        BiomeId.Beach              => 0,
        BiomeId.Coastal            => 0,
        BiomeId.Ocean              => 0,    // not walkable but won't generate spawns either
        // Mid-danger biomes
        BiomeId.Boreal             => 1,
        BiomeId.Wetland            => 1,
        BiomeId.Tundra             => 1,
        BiomeId.SubtropicalForest  => 1,
        BiomeId.Scrubland          => 1,
        BiomeId.MountainForested   => 1,
        BiomeId.ForestEdge         => 1,
        BiomeId.Foothills          => 1,
        BiomeId.MarshEdge          => 1,
        BiomeId.Mangrove           => 1,
        // High-danger biomes
        BiomeId.MountainAlpine     => 2,
        BiomeId.DesertCold         => 2,
        BiomeId.TidalFlat          => 1,
        BiomeId.Cliff              => 2,
        _                          => 1,
    };

    private static int ChebyshevDistance(int x1, int y1, int x2, int y2)
        => System.Math.Max(System.Math.Abs(x1 - x2), System.Math.Abs(y1 - y2));

    /// <summary>Tier-1 settlement tile if available, else map centre.</summary>
    public static (int x, int y) ResolveStartTile(WorldState world)
    {
        foreach (var s in world.Settlements)
        {
            if (s.Tier == 1 && !s.IsPoi)
                return (s.TileX, s.TileY);
        }
        return (C.WORLD_WIDTH_TILES / 2, C.WORLD_HEIGHT_TILES / 2);
    }
}
