using UnityEngine;
using System.Collections.Generic;

public class ExpansionManager
{
    private MapMaker2 mapMaker;
    private NoiseGenerator noiseGenerator;
    private System.Random random;

    public ExpansionManager(MapMaker2 mapMaker)
    {
        this.mapMaker = mapMaker;
        this.noiseGenerator = new NoiseGenerator();
        this.random = new System.Random(mapMaker.seed);
    }

    public bool ShouldExpand(Vector2 playerPosition, MapData mapData)
    {
        float distanceThreshold = mapMaker.playerDistanceThreshold;

        // Check distance to each edge
        float distToLeft = playerPosition.x;
        float distToRight = mapData.Width - playerPosition.x;
        float distToTop = mapData.Height - playerPosition.y;
        float distToBottom = playerPosition.y;

        bool shouldExpand = distToLeft < distanceThreshold ||
                           distToRight < distanceThreshold ||
                           distToTop < distanceThreshold ||
                           distToBottom < distanceThreshold;

        if (shouldExpand)
        {
            Debug.Log($"Expansion needed! Threshold: {distanceThreshold}");
            Debug.Log($"Distances - Left: {distToLeft}, Right: {distToRight}, Top: {distToTop}, Bottom: {distToBottom}");
        }

        return shouldExpand;
    }

    public void ExpandMap(MapData mapData, Vector2 playerPosition, int expansionSize)
    {
        float distanceThreshold = mapMaker.playerDistanceThreshold;

        // Determine expansion directions
        bool expandLeft = playerPosition.x < distanceThreshold;
        bool expandRight = mapData.Width - playerPosition.x < distanceThreshold;
        bool expandUp = mapData.Height - playerPosition.y < distanceThreshold;
        bool expandDown = playerPosition.y < distanceThreshold;

        // Calculate new dimensions
        int leftExpansion = expandLeft ? expansionSize : 0;
        int rightExpansion = expandRight ? expansionSize : 0;
        int upExpansion = expandUp ? expansionSize : 0;
        int downExpansion = expandDown ? expansionSize : 0;

        int newWidth = mapData.Width + leftExpansion + rightExpansion;
        int newHeight = mapData.Height + upExpansion + downExpansion;

        // Store old dimensions and expansion info
        ExpansionInfo expansionInfo = new ExpansionInfo
        {
            oldWidth = mapData.Width,
            oldHeight = mapData.Height,
            newWidth = newWidth,
            newHeight = newHeight,
            leftExpansion = leftExpansion,
            rightExpansion = rightExpansion,
            upExpansion = upExpansion,
            downExpansion = downExpansion
        };

        // Expand the map with offset handling
        ExpandMapWithOffset(mapData, expansionInfo);

        // Generate logical terrain and features for new areas using enhanced system
        GenerateEnhancedExpansion(mapData, expansionInfo);

        Debug.Log($"Map expanded from {expansionInfo.oldWidth}x{expansionInfo.oldHeight} to {newWidth}x{newHeight}");
        Debug.Log($"Expansion directions - Left: {leftExpansion}, Right: {rightExpansion}, Up: {upExpansion}, Down: {downExpansion}");
    }

    private struct ExpansionInfo
    {
        public int oldWidth, oldHeight;
        public int newWidth, newHeight;
        public int leftExpansion, rightExpansion;
        public int upExpansion, downExpansion;
    }

    private void ExpandMapWithOffset(MapData mapData, ExpansionInfo info)
    {
        mapData.ExpandMapWithOffset(info.newWidth, info.newHeight, info.leftExpansion, info.downExpansion);
    }

    private void GenerateEnhancedExpansion(MapData mapData, ExpansionInfo info)
    {
        // Enhanced multi-pass terrain generation for natural-looking results
        GenerateNaturalTerrain(mapData, info);
        SmoothTerrainTransitions(mapData, info);
        GenerateNaturalFeatures(mapData, info);
        GenerateSettlementsAndRoads(mapData, info);
    }

    private void GenerateNaturalTerrain(MapData mapData, ExpansionInfo info)
    {
        // Generate each expansion region with proper biome continuity
        if (info.leftExpansion > 0)
            GenerateTerrainRegion(mapData, 0, 0, info.leftExpansion, info.newHeight, ExpansionDirection.Left, info);

        if (info.rightExpansion > 0)
        {
            int startX = info.leftExpansion + info.oldWidth;
            GenerateTerrainRegion(mapData, startX, 0, info.rightExpansion, info.newHeight, ExpansionDirection.Right, info);
        }

        if (info.downExpansion > 0)
            GenerateTerrainRegion(mapData, info.leftExpansion, 0, info.oldWidth, info.downExpansion, ExpansionDirection.Down, info);

        if (info.upExpansion > 0)
        {
            int startY = info.downExpansion + info.oldHeight;
            GenerateTerrainRegion(mapData, info.leftExpansion, startY, info.oldWidth, info.upExpansion, ExpansionDirection.Up, info);
        }
    }

    private enum ExpansionDirection { Left, Right, Up, Down }

    private void GenerateTerrainRegion(MapData mapData, int startX, int startY, int width, int height, ExpansionDirection direction, ExpansionInfo info)
    {
        for (int x = startX; x < startX + width; x++)
        {
            for (int y = startY; y < startY + height; y++)
            {
                GenerateEnhancedTerrain(mapData, x, y, direction, info);
            }
        }
    }

    private void GenerateEnhancedTerrain(MapData mapData, int x, int y, ExpansionDirection direction, ExpansionInfo info)
    {
        MapTile tile = mapData.GetTile(x, y);
        if (tile == null) return;

        // Get reference context from existing terrain
        List<MapTile> referenceTiles = GetExtendedReferenceTiles(mapData, x, y, direction, info);

        // Generate realistic height with proper continuity
        float height = GenerateRealisticHeight(x, y, referenceTiles, direction, info);
        tile.height = height;

        // Determine terrain with natural biome rules
        TerrainType terrainType = DetermineRealisticTerrain(referenceTiles, height, x, y, direction, info);
        tile.terrainType = terrainType;

        tile.isWalkable = terrainType != TerrainType.Mountain && terrainType != TerrainType.Ocean;
    }

    private List<MapTile> GetExtendedReferenceTiles(MapData mapData, int x, int y, ExpansionDirection direction, ExpansionInfo info)
    {
        List<MapTile> referenceTiles = new List<MapTile>();
        int range = 4; // Wider reference for better continuity

        switch (direction)
        {
            case ExpansionDirection.Left:
                for (int refY = y - range; refY <= y + range; refY++)
                {
                    int refX = info.leftExpansion;
                    if (mapData.IsValidPosition(refX, refY))
                        referenceTiles.Add(mapData.GetTile(refX, refY));
                }
                break;

            case ExpansionDirection.Right:
                for (int refY = y - range; refY <= y + range; refY++)
                {
                    int refX = info.leftExpansion + info.oldWidth - 1;
                    if (mapData.IsValidPosition(refX, refY))
                        referenceTiles.Add(mapData.GetTile(refX, refY));
                }
                break;

            case ExpansionDirection.Up:
                for (int refX = x - range; refX <= x + range; refX++)
                {
                    int refY = info.downExpansion + info.oldHeight - 1;
                    if (mapData.IsValidPosition(refX, refY))
                        referenceTiles.Add(mapData.GetTile(refX, refY));
                }
                break;

            case ExpansionDirection.Down:
                for (int refX = x - range; refX <= x + range; refX++)
                {
                    int refY = info.downExpansion;
                    if (mapData.IsValidPosition(refX, refY))
                        referenceTiles.Add(mapData.GetTile(refX, refY));
                }
                break;
        }

        return referenceTiles;
    }

    private float GenerateRealisticHeight(int x, int y, List<MapTile> referenceTiles, ExpansionDirection direction, ExpansionInfo info)
    {
        float distanceFromBoundary = GetDistanceFromBoundary(x, y, direction, info);

        // For very close tiles, copy heights directly
        if (distanceFromBoundary < 6f)
        {
            Vector2Int sourcePos = GetCorrespondingOriginalTile(x, y, direction, info);
            if (IsInOriginalArea(sourcePos.x, sourcePos.y, info))
            {
                MapTile sourceTile = mapMaker.GetMapData().GetTile(sourcePos.x, sourcePos.y);
                if (sourceTile != null)
                {
                    // Add very small noise to avoid perfect repetition
                    float noise = noiseGenerator.GetNoise(x, y, 0.1f) * 0.05f;
                    return sourceTile.height + noise;
                }
            }
        }

        // For farther tiles, use blended approach
        float height = 0f;
        height += noiseGenerator.GetNoise(x, y, 0.02f) * 0.6f;  // Large landforms
        height += noiseGenerator.GetNoise(x, y, 0.06f) * 0.25f; // Medium features  
        height += noiseGenerator.GetNoise(x, y, 0.12f) * 0.1f;  // Small details
        height += noiseGenerator.GetNoise(x, y, 0.25f) * 0.05f; // Fine noise

        // Strong blending with reference heights
        if (referenceTiles.Count > 0)
        {
            float refAverage = 0f;
            foreach (var tile in referenceTiles)
                refAverage += tile.height;
            refAverage /= referenceTiles.Count;

            float blendFactor = Mathf.Lerp(0.8f, 0.2f, Mathf.Clamp01(distanceFromBoundary / 15f));
            height = Mathf.Lerp(height, refAverage, blendFactor);
        }

        return height;
    }

    private float GetDistanceFromBoundary(int x, int y, ExpansionDirection direction, ExpansionInfo info)
    {
        switch (direction)
        {
            case ExpansionDirection.Left:
                return x;
            case ExpansionDirection.Right:
                return (info.newWidth - 1) - x;
            case ExpansionDirection.Down:
                return y;
            case ExpansionDirection.Up:
                return (info.newHeight - 1) - y;
            default:
                return 10f;
        }
    }

    private TerrainType DetermineRealisticTerrain(List<MapTile> referenceTiles, float height, int x, int y, ExpansionDirection direction, ExpansionInfo info)
    {
        // Calculate distance from boundary for continuity strength
        float distanceFromBoundary = GetDistanceFromBoundary(x, y, direction, info);

        // For the first 8 tiles from boundary, use direct terrain copying
        if (distanceFromBoundary < 8f)
        {
            return CopyNearestTerrain(x, y, direction, info);
        }
        else
        {
            // For further tiles, still use heavy continuity
            Dictionary<TerrainType, float> biomeInfluence = AnalyzeBiomeInfluence(referenceTiles);
            TerrainType dominantBiome = GetDominantBiome(biomeInfluence);
            return GenerateContinuousTerrain(dominantBiome, height, x, y);
        }
    }

    private TerrainType CopyNearestTerrain(int x, int y, ExpansionDirection direction, ExpansionInfo info)
    {
        // Find the exact corresponding tile in the original map and copy its terrain
        Vector2Int sourcePos = GetCorrespondingOriginalTile(x, y, direction, info);

        if (IsInOriginalArea(sourcePos.x, sourcePos.y, info))
        {
            MapTile sourceTile = mapMaker.GetMapData().GetTile(sourcePos.x, sourcePos.y);
            if (sourceTile != null)
            {
                return sourceTile.terrainType;
            }
        }

        // Fallback to nearest boundary tile
        return GetNearestBoundaryTerrain(x, y, direction, info);
    }

    private Vector2Int GetCorrespondingOriginalTile(int x, int y, ExpansionDirection direction, ExpansionInfo info)
    {
        switch (direction)
        {
            case ExpansionDirection.Left:
                // Mirror from the left boundary
                int offsetFromLeft = x;
                return new Vector2Int(info.leftExpansion + offsetFromLeft, y);

            case ExpansionDirection.Right:
                // Mirror from the right boundary
                int offsetFromRight = x - (info.leftExpansion + info.oldWidth);
                return new Vector2Int(info.leftExpansion + info.oldWidth - 1 - offsetFromRight, y);

            case ExpansionDirection.Down:
                // Mirror from the bottom boundary
                int offsetFromBottom = y;
                return new Vector2Int(x, info.downExpansion + offsetFromBottom);

            case ExpansionDirection.Up:
                // Mirror from the top boundary
                int offsetFromTop = y - (info.downExpansion + info.oldHeight);
                return new Vector2Int(x, info.downExpansion + info.oldHeight - 1 - offsetFromTop);

            default:
                return new Vector2Int(x, y);
        }
    }

    private TerrainType GetNearestBoundaryTerrain(int x, int y, ExpansionDirection direction, ExpansionInfo info)
    {
        Vector2Int boundaryPos = Vector2Int.zero;

        switch (direction)
        {
            case ExpansionDirection.Left:
                boundaryPos = new Vector2Int(info.leftExpansion, y);
                break;
            case ExpansionDirection.Right:
                boundaryPos = new Vector2Int(info.leftExpansion + info.oldWidth - 1, y);
                break;
            case ExpansionDirection.Down:
                boundaryPos = new Vector2Int(x, info.downExpansion);
                break;
            case ExpansionDirection.Up:
                boundaryPos = new Vector2Int(x, info.downExpansion + info.oldHeight - 1);
                break;
        }

        if (IsInOriginalArea(boundaryPos.x, boundaryPos.y, info))
        {
            MapTile boundaryTile = mapMaker.GetMapData().GetTile(boundaryPos.x, boundaryPos.y);
            if (boundaryTile != null)
                return boundaryTile.terrainType;
        }

        return TerrainType.Plains;
    }

    private Dictionary<TerrainType, float> AnalyzeBiomeInfluence(List<MapTile> referenceTiles)
    {
        Dictionary<TerrainType, float> influence = new Dictionary<TerrainType, float>();

        foreach (var tile in referenceTiles)
        {
            float weight = GetBiomeWeight(tile.terrainType);
            if (influence.ContainsKey(tile.terrainType))
                influence[tile.terrainType] += weight;
            else
                influence[tile.terrainType] = weight;
        }

        return influence;
    }

    private float GetBiomeWeight(TerrainType terrain)
    {
        // Some terrain types are more "influential" for expansion
        switch (terrain)
        {
            case TerrainType.Ocean: return 4.0f; // Much stronger ocean influence
            case TerrainType.Mountain: return 2.5f;
            case TerrainType.Lake: return 2.0f; // Stronger water influence  
            case TerrainType.Forest: return 1.5f;
            case TerrainType.Shore: return 1.5f; // Stronger shore influence
            case TerrainType.River: return 1.2f;
            default: return 0.8f;
        }
    }

    private TerrainType GetDominantBiome(Dictionary<TerrainType, float> influence)
    {
        if (influence.Count == 0) return TerrainType.Plains;

        TerrainType dominant = TerrainType.Plains;
        float maxInfluence = 0f;

        foreach (var kvp in influence)
        {
            if (kvp.Value > maxInfluence)
            {
                maxInfluence = kvp.Value;
                dominant = kvp.Key;
            }
        }

        return dominant;
    }

    private TerrainType GenerateContinuousTerrain(TerrainType dominantBiome, float height, int x, int y)
    {
        switch (dominantBiome)
        {
            case TerrainType.Mountain:
                return TransitionMountain(height, x, y);
            case TerrainType.Forest:
                return TransitionForest(height, x, y);
            case TerrainType.Ocean:
                return TransitionOcean(height, x, y);
            case TerrainType.Lake:
                return TransitionLake(height, x, y);
            case TerrainType.Shore:
                return TransitionShore(height, x, y);
            case TerrainType.River:
                return TransitionRiver(height, x, y);
            default:
                return TerrainType.Plains;
        }
    }

    private TerrainType TransitionMountain(float height, int x, int y)
    {
        // Mountains are very conservative
        if (height > 0.3f) // Lower threshold for mountain continuation
            return TerrainType.Mountain;
        else if (height > 0.0f) // Wide foothill zone
            return TerrainType.Forest; // Mountain foothills
        else
            return TerrainType.Plains;
    }

    private TerrainType TransitionForest(float height, int x, int y)
    {
        // Conservative forest generation
        if (height > 0.2f)
            return TerrainType.Mountain; // Higher areas become mountains
        else if (height > -0.1f) // Wide forest zone
            return TerrainType.Forest;
        else
            return TerrainType.Plains;
    }

    private TerrainType TransitionOcean(float height, int x, int y)
    {
        // Ocean is extremely conservative - almost always continues as ocean
        if (height < 0.0f) // Much more lenient for ocean continuation
            return TerrainType.Ocean;
        else if (height < 0.2f) // Very wide shore zone
            return TerrainType.Shore;
        else if (height < 0.4f)
            return TerrainType.Plains; // Coastal plains
        else
            return TerrainType.Forest;
    }

    private TerrainType TransitionLake(float height, int x, int y)
    {
        // Lakes are very conservative
        if (height < 0.0f) // Much more lenient for lake continuation
            return TerrainType.Lake;
        else if (height < 0.2f) // Wide shore zone
            return TerrainType.Shore;
        else
            return TerrainType.Plains;
    }

    private TerrainType TransitionShore(float height, int x, int y)
    {
        if (height < -0.1f) // More lenient transition to water
            return TerrainType.Ocean;
        else if (height < 0.4f) // Much wider shore zone
            return TerrainType.Shore;
        else
            return TerrainType.Plains;
    }

    private TerrainType TransitionRiver(float height, int x, int y)
    {
        // Rivers are conservative
        if (height < 0.0f)
            return TerrainType.River;
        else if (height < 0.1f)
            return TerrainType.Plains; // River plains
        else
            return TerrainType.Forest;
    }

    private TerrainType GenerateNaturalTerrainFromHeight(float height, int x, int y)
    {
        // Pure height-based generation without noise variation
        if (height > 0.6f) return TerrainType.Mountain;
        else if (height > 0.2f) return TerrainType.Forest;
        else if (height < -0.5f) return TerrainType.Ocean;
        else if (height < -0.2f) return TerrainType.Lake;
        else if (height < 0.0f) return TerrainType.Shore;
        else return TerrainType.Plains;
    }

    private void SmoothTerrainTransitions(MapData mapData, ExpansionInfo info)
    {
        // Much more aggressive smoothing for natural blending
        int smoothRadius = 8; // Increased from 5

        // Process each expansion boundary multiple times
        for (int pass = 0; pass < 3; pass++) // Multiple smoothing passes
        {
            if (info.leftExpansion > 0)
                SmoothBoundaryRegion(mapData, info.leftExpansion - smoothRadius, 0, smoothRadius * 2, info.newHeight);

            if (info.rightExpansion > 0)
                SmoothBoundaryRegion(mapData, info.leftExpansion + info.oldWidth - smoothRadius, 0, smoothRadius * 2, info.newHeight);

            if (info.upExpansion > 0)
                SmoothBoundaryRegion(mapData, 0, info.downExpansion + info.oldHeight - smoothRadius, info.newWidth, smoothRadius * 2);

            if (info.downExpansion > 0)
                SmoothBoundaryRegion(mapData, 0, info.downExpansion - smoothRadius, info.newWidth, smoothRadius * 2);
        }
    }

    private void SmoothBoundaryRegion(MapData mapData, int startX, int startY, int width, int height)
    {
        for (int x = startX; x < startX + width; x++)
        {
            for (int y = startY; y < startY + height; y++)
            {
                if (!mapData.IsValidPosition(x, y)) continue;

                SmoothTileWithNeighbors(mapData, x, y);
            }
        }
    }

    private void SmoothTileWithNeighbors(MapData mapData, int x, int y)
    {
        MapTile centerTile = mapData.GetTile(x, y);
        Dictionary<TerrainType, int> neighborTerrain = GetNeighborTerrain(mapData, x, y, 3); // Larger radius

        // If this tile is very isolated, consider changing it
        if (!neighborTerrain.ContainsKey(centerTile.terrainType) ||
            neighborTerrain[centerTile.terrainType] < 3) // Stricter isolation check
        {
            TerrainType newTerrain = GetMostCommonNeighborTerrain(neighborTerrain);
            if (IsValidTerrainTransition(centerTile.terrainType, newTerrain))
            {
                centerTile.terrainType = newTerrain;

                // Also smooth the height towards neighbors
                float avgHeight = GetAverageNeighborHeight(mapData, x, y, 2);
                centerTile.height = Mathf.Lerp(centerTile.height, avgHeight, 0.5f);
            }
        }
    }

    private Dictionary<TerrainType, int> GetNeighborTerrain(MapData mapData, int centerX, int centerY, int radius)
    {
        Dictionary<TerrainType, int> terrain = new Dictionary<TerrainType, int>();

        for (int x = centerX - radius; x <= centerX + radius; x++)
        {
            for (int y = centerY - radius; y <= centerY + radius; y++)
            {
                if (x == centerX && y == centerY) continue;
                if (!mapData.IsValidPosition(x, y)) continue;

                TerrainType terrainType = mapData.GetTile(x, y).terrainType;
                terrain[terrainType] = terrain.ContainsKey(terrainType) ? terrain[terrainType] + 1 : 1;
            }
        }

        return terrain;
    }

    private float GetAverageNeighborHeight(MapData mapData, int centerX, int centerY, int radius)
    {
        float totalHeight = 0f;
        int count = 0;

        for (int x = centerX - radius; x <= centerX + radius; x++)
        {
            for (int y = centerY - radius; y <= centerY + radius; y++)
            {
                if (x == centerX && y == centerY) continue;
                if (!mapData.IsValidPosition(x, y)) continue;

                totalHeight += mapData.GetTile(x, y).height;
                count++;
            }
        }

        return count > 0 ? totalHeight / count : 0f;
    }

    private TerrainType GetMostCommonNeighborTerrain(Dictionary<TerrainType, int> terrain)
    {
        TerrainType mostCommon = TerrainType.Plains;
        int maxCount = 0;

        foreach (var kvp in terrain)
        {
            if (kvp.Value > maxCount)
            {
                maxCount = kvp.Value;
                mostCommon = kvp.Key;
            }
        }

        return mostCommon;
    }

    private bool IsValidTerrainTransition(TerrainType from, TerrainType to)
    {
        // Some terrain transitions don't make sense (e.g., ocean to mountain directly)
        if (from == TerrainType.Ocean && to == TerrainType.Mountain) return false;
        if (from == TerrainType.Mountain && to == TerrainType.Ocean) return false;
        return true;
    }

    private void GenerateNaturalFeatures(MapData mapData, ExpansionInfo info)
    {
        // Extend rivers naturally
        ExtendRiversIntoExpansion(mapData, info);

        // Create natural water bodies
        GenerateNaturalLakes(mapData, info);
    }

    private void ExtendRiversIntoExpansion(MapData mapData, ExpansionInfo info)
    {
        // Find rivers at boundaries and extend them naturally
        List<Vector2Int> riverEnds = FindRiversAtBoundaries(mapData, info);

        foreach (var riverEnd in riverEnds)
        {
            ExtendRiverNaturally(mapData, riverEnd, info);
        }
    }

    private List<Vector2Int> FindRiversAtBoundaries(MapData mapData, ExpansionInfo info)
    {
        List<Vector2Int> riverPoints = new List<Vector2Int>();

        // Check boundaries for rivers
        CheckBoundaryForRivers(mapData, info.leftExpansion, 0, 1, info.newHeight, riverPoints);
        CheckBoundaryForRivers(mapData, info.leftExpansion + info.oldWidth - 1, 0, 1, info.newHeight, riverPoints);

        return riverPoints;
    }

    private void CheckBoundaryForRivers(MapData mapData, int startX, int startY, int width, int height, List<Vector2Int> riverPoints)
    {
        for (int x = startX; x < startX + width; x++)
        {
            for (int y = startY; y < startY + height; y++)
            {
                if (mapData.IsValidPosition(x, y))
                {
                    MapTile tile = mapData.GetTile(x, y);
                    if (tile.terrainType == TerrainType.River)
                    {
                        riverPoints.Add(new Vector2Int(x, y));
                    }
                }
            }
        }
    }

    private void ExtendRiverNaturally(MapData mapData, Vector2Int start, ExpansionInfo info)
    {
        Vector2Int current = start;
        int maxLength = 25;

        for (int i = 0; i < maxLength; i++)
        {
            Vector2Int next = FindBestRiverDirection(mapData, current);
            if (next == current) break;

            MapTile nextTile = mapData.GetTile(next.x, next.y);
            if (nextTile.terrainType == TerrainType.Plains || nextTile.terrainType == TerrainType.Shore)
            {
                nextTile.terrainType = TerrainType.River;
                current = next;
            }
            else
            {
                break;
            }
        }
    }

    private Vector2Int FindBestRiverDirection(MapData mapData, Vector2Int current)
    {
        Vector2Int[] directions = {
            new Vector2Int(0, 1), new Vector2Int(1, 0), new Vector2Int(0, -1), new Vector2Int(-1, 0),
            new Vector2Int(1, 1), new Vector2Int(-1, 1), new Vector2Int(1, -1), new Vector2Int(-1, -1)
        };

        Vector2Int best = current;
        float bestScore = float.MinValue;

        foreach (var dir in directions)
        {
            Vector2Int candidate = current + dir;
            if (mapData.IsValidPosition(candidate.x, candidate.y))
            {
                float score = EvaluateRiverDirection(mapData, candidate);
                if (score > bestScore)
                {
                    bestScore = score;
                    best = candidate;
                }
            }
        }

        return best;
    }

    private float EvaluateRiverDirection(MapData mapData, Vector2Int position)
    {
        MapTile tile = mapData.GetTile(position.x, position.y);

        // Rivers prefer lower elevations and suitable terrain
        float score = -tile.height; // Lower is better

        if (tile.terrainType == TerrainType.Plains) score += 2f;
        else if (tile.terrainType == TerrainType.Shore) score += 1f;
        else if (tile.terrainType == TerrainType.Forest) score += 0.5f;
        else score -= 10f; // Avoid mountains, existing water, etc.

        return score;
    }

    private void GenerateNaturalLakes(MapData mapData, ExpansionInfo info)
    {
        // Create 1-2 small lakes in suitable locations
        for (int i = 0; i < 2; i++)
        {
            Vector2Int lakeCenter = FindSuitableLakeLocation(mapData, info);
            if (lakeCenter != Vector2Int.zero)
            {
                CreateNaturalLake(mapData, lakeCenter);
            }
        }
    }

    private Vector2Int FindSuitableLakeLocation(MapData mapData, ExpansionInfo info)
    {
        for (int attempts = 0; attempts < 30; attempts++)
        {
            int x = random.Next(0, info.newWidth);
            int y = random.Next(0, info.newHeight);

            // Only in expansion areas
            if (IsInOriginalArea(x, y, info)) continue;

            if (mapData.IsValidPosition(x, y))
            {
                MapTile tile = mapData.GetTile(x, y);
                if (tile.height < -0.1f && tile.terrainType == TerrainType.Plains)
                {
                    return new Vector2Int(x, y);
                }
            }
        }

        return Vector2Int.zero;
    }

    private bool IsInOriginalArea(int x, int y, ExpansionInfo info)
    {
        return x >= info.leftExpansion && x < info.leftExpansion + info.oldWidth &&
               y >= info.downExpansion && y < info.downExpansion + info.oldHeight;
    }

    private void CreateNaturalLake(MapData mapData, Vector2Int center)
    {
        int radius = random.Next(3, 6);

        for (int x = center.x - radius; x <= center.x + radius; x++)
        {
            for (int y = center.y - radius; y <= center.y + radius; y++)
            {
                if (mapData.IsValidPosition(x, y))
                {
                    float distance = Vector2Int.Distance(center, new Vector2Int(x, y));
                    if (distance <= radius)
                    {
                        MapTile tile = mapData.GetTile(x, y);
                        if (distance < radius - 1)
                        {
                            tile.terrainType = TerrainType.Lake;
                        }
                        else if (distance < radius)
                        {
                            tile.terrainType = TerrainType.Shore;
                        }
                    }
                }
            }
        }
    }

    private void GenerateSettlementsAndRoads(MapData mapData, ExpansionInfo info)
    {
        GenerateExpansionSettlements(mapData, info);
        ExtendRoadsToExpansion(mapData, info);
        GenerateExpansionHarbours(mapData, info);
    }

    // Settlement and road generation methods (enhanced versions)
    private void GenerateExpansionSettlements(MapData mapData, ExpansionInfo info)
    {
        int totalNewArea = (info.newWidth * info.newHeight) - (info.oldWidth * info.oldHeight);
        int newSettlementCount = Mathf.Max(1, totalNewArea / 3000); // Fewer, better placed settlements

        Debug.Log($"Generating {newSettlementCount} new settlements for {totalNewArea} new tiles");

        for (int i = 0; i < newSettlementCount; i++)
        {
            Vector2Int position = FindSuitableSettlementPosition(mapData, info);
            if (position != Vector2Int.zero)
            {
                CreateNewSettlement(mapData, position);
                Debug.Log($"Created new settlement at {position}");
            }
        }
    }

    private Vector2Int FindSuitableSettlementPosition(MapData mapData, ExpansionInfo info)
    {
        for (int attempts = 0; attempts < 100; attempts++)
        {
            int x = random.Next(0, info.newWidth);
            int y = random.Next(0, info.newHeight);

            if (IsInOriginalArea(x, y, info)) continue;

            MapTile tile = mapData.GetTile(x, y);
            if (tile != null && tile.terrainType == TerrainType.Plains && tile.featureType == FeatureType.None)
            {
                // Check distance from other settlements
                bool validLocation = true;
                var existingSettlements = mapData.GetTowns();
                existingSettlements.AddRange(mapData.GetVillages());

                foreach (var settlement in existingSettlements)
                {
                    float distance = Vector2Int.Distance(new Vector2Int(x, y), settlement.position);
                    if (distance < 25) // Increased minimum distance for better distribution
                    {
                        validLocation = false;
                        break;
                    }
                }

                if (validLocation)
                    return new Vector2Int(x, y);
            }
        }

        return Vector2Int.zero;
    }

    private void CreateNewSettlement(MapData mapData, Vector2Int position)
    {
        MapTile tile = mapData.GetTile(position.x, position.y);

        SettlementType type = random.NextDouble() < 0.25 ? SettlementType.Town : SettlementType.Village;

        tile.featureType = type == SettlementType.Town ? FeatureType.Town : FeatureType.Village;
        string settlementName = GenerateSettlementName(type);
        tile.name = settlementName;

        Settlement settlement = new Settlement(settlementName, type, position);
        mapData.AddSettlement(settlement);
    }

    private string GenerateSettlementName(SettlementType type)
    {
        string[] townNames = { "Newborough", "Westhold", "Eastport", "Northgate", "Southhaven", "Riverside", "Hillcrest", "Stoneburg" };
        string[] villageNames = { "Millbrook", "Fairfield", "Greendale", "Oakwood", "Rosehaven", "Brightwater", "Meadowbrook", "Willowbend" };

        string[] names = type == SettlementType.Town ? townNames : villageNames;
        return names[random.Next(names.Length)];
    }

    private void ExtendRoadsToExpansion(MapData mapData, ExpansionInfo info)
    {
        var settlements = mapData.GetTowns();
        settlements.AddRange(mapData.GetVillages());

        foreach (var settlement in settlements)
        {
            bool nearExpansionEdge = IsNearExpansionEdge(settlement.position, info);
            if (nearExpansionEdge)
            {
                var nearestNewSettlement = FindNearestNewSettlement(mapData, settlement.position, info);
                if (nearestNewSettlement != Vector2Int.zero)
                {
                    CreateRoadBetween(mapData, settlement.position, nearestNewSettlement);
                }
            }
        }
    }

    private bool IsNearExpansionEdge(Vector2Int position, ExpansionInfo info)
    {
        int edgeDistance = 20;

        bool nearLeft = info.leftExpansion > 0 && position.x < info.leftExpansion + edgeDistance;
        bool nearRight = info.rightExpansion > 0 && position.x > info.leftExpansion + info.oldWidth - edgeDistance;
        bool nearBottom = info.downExpansion > 0 && position.y < info.downExpansion + edgeDistance;
        bool nearTop = info.upExpansion > 0 && position.y > info.downExpansion + info.oldHeight - edgeDistance;

        return nearLeft || nearRight || nearBottom || nearTop;
    }

    private Vector2Int FindNearestNewSettlement(MapData mapData, Vector2Int fromPosition, ExpansionInfo info)
    {
        Vector2Int nearest = Vector2Int.zero;
        float nearestDistance = float.MaxValue;

        var settlements = mapData.GetTowns();
        settlements.AddRange(mapData.GetVillages());

        foreach (var settlement in settlements)
        {
            bool inNewArea = !IsInOriginalArea(settlement.position.x, settlement.position.y, info);
            if (inNewArea)
            {
                float distance = Vector2Int.Distance(fromPosition, settlement.position);
                if (distance < nearestDistance)
                {
                    nearestDistance = distance;
                    nearest = settlement.position;
                }
            }
        }

        return nearest;
    }

    private void CreateRoadBetween(MapData mapData, Vector2Int start, Vector2Int end)
    {
        Vector2Int current = start;

        while (current != end)
        {
            if (current.x < end.x) current.x++;
            else if (current.x > end.x) current.x--;
            else if (current.y < end.y) current.y++;
            else if (current.y > end.y) current.y--;

            if (mapData.IsValidPosition(current.x, current.y))
            {
                MapTile tile = mapData.GetTile(current.x, current.y);
                if (tile.featureType == FeatureType.None && tile.isWalkable)
                {
                    tile.featureType = FeatureType.Road;
                }
            }
        }
    }

    private void GenerateExpansionHarbours(MapData mapData, ExpansionInfo info)
    {
        var settlements = mapData.GetTowns();
        settlements.AddRange(mapData.GetVillages());

        foreach (var settlement in settlements)
        {
            if (!IsInOriginalArea(settlement.position.x, settlement.position.y, info))
            {
                if (IsNearWater(mapData, settlement.position.x, settlement.position.y, 5))
                {
                    Vector2Int harbourPos = FindHarbourPosition(mapData, settlement.position);
                    if (harbourPos != Vector2Int.zero)
                    {
                        mapData.GetTile(harbourPos.x, harbourPos.y).featureType = FeatureType.Harbour;
                    }
                }
            }
        }
    }

    private bool IsNearWater(MapData mapData, int x, int y, int range)
    {
        for (int dx = -range; dx <= range; dx++)
        {
            for (int dy = -range; dy <= range; dy++)
            {
                int checkX = x + dx;
                int checkY = y + dy;

                if (mapData.IsValidPosition(checkX, checkY))
                {
                    MapTile tile = mapData.GetTile(checkX, checkY);
                    if (tile.IsWater())
                        return true;
                }
            }
        }
        return false;
    }

    private Vector2Int FindHarbourPosition(MapData mapData, Vector2Int settlementPos)
    {
        for (int range = 1; range <= 5; range++)
        {
            for (int dx = -range; dx <= range; dx++)
            {
                for (int dy = -range; dy <= range; dy++)
                {
                    int x = settlementPos.x + dx;
                    int y = settlementPos.y + dy;

                    if (mapData.IsValidPosition(x, y))
                    {
                        MapTile tile = mapData.GetTile(x, y);
                        if (tile.terrainType == TerrainType.Shore && tile.featureType == FeatureType.None)
                        {
                            return new Vector2Int(x, y);
                        }
                    }
                }
            }
        }
        return Vector2Int.zero;
    }
}