using UnityEngine;
using System.Collections.Generic;

public class MapVisualizer : MonoBehaviour
{
    [Header("Terrain Sprites")]
    public Sprite defaultSprite; // Default square sprite
    public Sprite plainsSprite;
    public Sprite forestSprite;
    public Sprite oceanSprite;
    public Sprite lakeSprite;
    public Sprite shoreSprite;
    public Sprite riverSprite;
    public Sprite mountainSprite;
    public Sprite forestRiverSprite; // New hybrid sprite

    [Header("Feature Sprites")]
    public Sprite roadSprite;
    public Sprite bridgeSprite;
    public Sprite tunnelSprite;
    public Sprite harbourSprite;
    public Sprite townSprite;
    public Sprite villageSprite;

    [Header("Settings")]
    public float tileSize = 1f;
    public Transform mapParent;

    private Dictionary<Vector2Int, GameObject> terrainTiles = new Dictionary<Vector2Int, GameObject>();
    private Dictionary<Vector2Int, GameObject> featureTiles = new Dictionary<Vector2Int, GameObject>();
    private Sprite generatedDefaultSprite; // Generated sprite for fallback

    void Start()
    {
        if (mapParent == null)
        {
            GameObject mapContainer = new GameObject("MapContainer");
            mapParent = mapContainer.transform;
        }

        // Create a default white square sprite if none exist
        CreateDefaultSprite();
    }

    private void CreateDefaultSprite()
    {
        // Create a simple white square texture if defaultSprite is not assigned
        if (generatedDefaultSprite == null)
        {
            Texture2D texture = new Texture2D(32, 32);
            Color[] pixels = new Color[32 * 32];
            for (int i = 0; i < pixels.Length; i++)
            {
                pixels[i] = Color.white;
            }
            texture.SetPixels(pixels);
            texture.Apply();

            // Use 32 pixels per unit instead of 100 to make tiles bigger
            generatedDefaultSprite = Sprite.Create(texture, new Rect(0, 0, 32, 32), new Vector2(0.5f, 0.5f), 32f);
        }
    }

    public void VisualizeMap(MapData mapData)
    {
        // Clear existing tiles that are out of bounds
        ClearOutOfBoundsTiles(mapData);

        // Generate visual tiles for the map
        for (int x = 0; x < mapData.Width; x++)
        {
            for (int y = 0; y < mapData.Height; y++)
            {
                Vector2Int pos = new Vector2Int(x, y);
                MapTile tile = mapData.GetTile(x, y);

                // Create or update terrain tile
                CreateOrUpdateTerrainTile(pos, tile);

                // Create or update feature tile
                CreateOrUpdateFeatureTile(pos, tile);
            }
        }

        // Position camera to see the map
        PositionCameraToMap(mapData);
    }

    private void PositionCameraToMap(MapData mapData)
    {
        Camera mainCam = Camera.main;
        if (mainCam != null)
        {
            // Check if we're in exploration mode and need to account for world coordinate offset
            Vector3 mapCenter;

            // Try to get exploration manager from MapMaker2 to determine proper camera positioning
            MapMaker2 mapMaker = Object.FindFirstObjectByType<MapMaker2>();
            if (mapMaker != null && mapMaker.useExplorationSystem)
            {
                var explorationManager = mapMaker.GetExplorationManager();
                if (explorationManager != null)
                {
                    // Get the explored bounds to find the actual world coordinate center
                    var bounds = explorationManager.GetExploredBoundsForCamera();

                    // Center camera on the actual world coordinates of the explored area
                    mapCenter = new Vector3(
                        (bounds.x + bounds.width / 2f) * tileSize,
                        (bounds.y + bounds.height / 2f) * tileSize,
                        mainCam.transform.position.z
                    );

                    Debug.Log($"🎥 Camera positioned for exploration: bounds({bounds.x}, {bounds.y}, {bounds.width}, {bounds.height}) → center({mapCenter.x:F1}, {mapCenter.y:F1})");
                }
                else
                {
                    // Fallback to standard positioning
                    mapCenter = new Vector3(
                        (mapData.Width * tileSize) / 2f,
                        (mapData.Height * tileSize) / 2f,
                        mainCam.transform.position.z
                    );
                }
            }
            else
            {
                // Standard positioning for non-exploration mode
                mapCenter = new Vector3(
                    (mapData.Width * tileSize) / 2f,
                    (mapData.Height * tileSize) / 2f,
                    mainCam.transform.position.z
                );
            }

            mainCam.transform.position = mapCenter;

            // Adjust orthographic size to fit map - make it bigger so you can zoom out more
            if (mainCam.orthographic)
            {
                float mapHeight = mapData.Height * tileSize;
                float mapWidth = mapData.Width * tileSize;
                float aspectRatio = (float)Screen.width / Screen.height;

                float requiredHeight = mapHeight / 2f;
                float requiredWidth = mapWidth / (2f * aspectRatio);

                // Calculate required size but don't override if camera is already zoomed out further
                float suggestedSize = Mathf.Max(requiredHeight, requiredWidth) + 20f;

                // Only set the camera size if it's smaller than what we need
                // This preserves user's zoom level while ensuring the map is initially visible
                if (mainCam.orthographicSize < suggestedSize)
                {
                    mainCam.orthographicSize = suggestedSize;
                    Debug.Log($"🎥 Camera zoom adjusted to fit map: {suggestedSize:F1}");
                }
                else
                {
                    Debug.Log($"🎥 Camera zoom preserved: {mainCam.orthographicSize:F1} (suggested: {suggestedSize:F1})");
                }
            }
        }
    }

    /// <summary>
    /// Calculate world position for a tile, accounting for exploration bounds offset
    /// </summary>
    private Vector3 CalculateWorldPosition(Vector2Int visiblePos)
    {
        // Try to get exploration manager to determine world coordinate offset
        MapMaker2 mapMaker = Object.FindFirstObjectByType<MapMaker2>();
        if (mapMaker != null && mapMaker.useExplorationSystem)
        {
            var explorationManager = mapMaker.GetExplorationManager();
            if (explorationManager != null)
            {
                // Get the explored bounds to find the world coordinate offset
                var bounds = explorationManager.GetExploredBoundsForCamera();

                // Convert visible coordinates to world coordinates
                Vector3 worldPos = new Vector3(
                    (bounds.x + visiblePos.x) * tileSize,
                    0,
                    (bounds.y + visiblePos.y) * tileSize
                );

                // Debug log for the first few tiles to verify positioning
                if (visiblePos.x < 3 && visiblePos.y < 3)
                {
                    Debug.Log($"🔧 Tile positioning: visible({visiblePos.x}, {visiblePos.y}) + bounds({bounds.x}, {bounds.y}) → world({worldPos.x:F1}, {worldPos.z:F1})");
                }

                return worldPos;
            }
        }

        // Fallback to direct positioning for non-exploration mode
        return new Vector3(visiblePos.x * tileSize, 0, visiblePos.y * tileSize);
    }

    private void ClearOutOfBoundsTiles(MapData mapData)
    {
        List<Vector2Int> tilesToRemove = new List<Vector2Int>();

        // Check terrain tiles
        foreach (var kvp in terrainTiles)
        {
            if (kvp.Key.x >= mapData.Width || kvp.Key.y >= mapData.Height ||
                kvp.Key.x < 0 || kvp.Key.y < 0)
            {
                tilesToRemove.Add(kvp.Key);
            }
        }

        foreach (var pos in tilesToRemove)
        {
            if (terrainTiles[pos] != null)
                DestroyImmediate(terrainTiles[pos]);
            terrainTiles.Remove(pos);
        }

        tilesToRemove.Clear();

        // Check feature tiles
        foreach (var kvp in featureTiles)
        {
            if (kvp.Key.x >= mapData.Width || kvp.Key.y >= mapData.Height ||
                kvp.Key.x < 0 || kvp.Key.y < 0)
            {
                tilesToRemove.Add(kvp.Key);
            }
        }

        foreach (var pos in tilesToRemove)
        {
            if (featureTiles[pos] != null)
                DestroyImmediate(featureTiles[pos]);
            featureTiles.Remove(pos);
        }
    }

    private void CreateOrUpdateTerrainTile(Vector2Int pos, MapTile tile)
    {
        GameObject tileObject;

        if (!terrainTiles.ContainsKey(pos))
        {
            // Create new tile
            tileObject = new GameObject($"Terrain_{pos.x}_{pos.y}");
            tileObject.transform.parent = mapParent;

            // Calculate world position accounting for exploration bounds offset
            Vector3 worldPosition = CalculateWorldPosition(pos);
            tileObject.transform.position = worldPosition;

            SpriteRenderer sr = tileObject.AddComponent<SpriteRenderer>();
            sr.sortingOrder = 0;

            // Scale the tile to make it visible
            tileObject.transform.localScale = Vector3.one * tileSize;

            // Rotate sprite to be flat on the ground for top-down view
            tileObject.transform.rotation = Quaternion.Euler(90f, 0f, 0f);

            terrainTiles[pos] = tileObject;
        }
        else
        {
            tileObject = terrainTiles[pos];
        }

        // Update sprite and color based on terrain type
        SpriteRenderer spriteRenderer = tileObject.GetComponent<SpriteRenderer>();

        // Use assigned sprite or fallback to default sprite
        Sprite terrainSprite = GetTerrainSprite(tile.terrainType);
        spriteRenderer.sprite = terrainSprite != null ? terrainSprite : (defaultSprite != null ? defaultSprite : generatedDefaultSprite);
        spriteRenderer.color = GetTerrainColor(tile.terrainType);

        // For ForestRiver tiles, add a simple river overlay if no custom sprite is provided
        if (tile.terrainType == TerrainType.ForestRiver && forestRiverSprite == null)
        {
            CreateSimpleForestRiverEffect(tileObject);
        }
    }

    private void CreateOrUpdateFeatureTile(Vector2Int pos, MapTile tile)
    {
        if (tile.featureType == FeatureType.None)
        {
            // Remove feature tile if it exists
            if (featureTiles.ContainsKey(pos))
            {
                if (featureTiles[pos] != null)
                    DestroyImmediate(featureTiles[pos]);
                featureTiles.Remove(pos);
            }
            return;
        }

        GameObject featureObject;

        if (!featureTiles.ContainsKey(pos))
        {
            // Create new feature tile
            featureObject = new GameObject($"Feature_{pos.x}_{pos.y}_{tile.featureType}");
            featureObject.transform.parent = mapParent;

            // Calculate world position accounting for exploration bounds offset
            Vector3 worldPosition = CalculateWorldPosition(pos);
            featureObject.transform.position = worldPosition;

            SpriteRenderer sr = featureObject.AddComponent<SpriteRenderer>();
            sr.sortingOrder = 1; // Above terrain

            // Scale the tile to make it visible
            featureObject.transform.localScale = Vector3.one * tileSize;

            // Rotate sprite to be flat on the ground for top-down view
            featureObject.transform.rotation = Quaternion.Euler(90f, 0f, 0f);

            featureTiles[pos] = featureObject;
        }
        else
        {
            featureObject = featureTiles[pos];
        }

        // Update sprite and color based on feature type
        SpriteRenderer spriteRenderer = featureObject.GetComponent<SpriteRenderer>();

        // Use assigned sprite or fallback sprite
        Sprite featureSprite = GetFeatureSprite(tile.featureType);
        spriteRenderer.sprite = featureSprite != null ? featureSprite : (defaultSprite != null ? defaultSprite : generatedDefaultSprite);
        spriteRenderer.color = GetFeatureColor(tile.featureType);
    }

    private Sprite GetTerrainSprite(TerrainType terrainType)
    {
        return terrainType switch
        {
            TerrainType.Plains => plainsSprite,
            TerrainType.Forest => forestSprite,
            TerrainType.Ocean => oceanSprite,
            TerrainType.Lake => lakeSprite,
            TerrainType.Shore => shoreSprite,
            TerrainType.River => riverSprite,
            TerrainType.Mountain => mountainSprite,
            TerrainType.ForestRiver => forestRiverSprite != null ? forestRiverSprite : riverSprite,
            _ => null
        };
    }

    private Sprite GetFeatureSprite(FeatureType featureType)
    {
        return featureType switch
        {
            FeatureType.Road => roadSprite,
            FeatureType.Bridge => bridgeSprite,
            FeatureType.Tunnel => tunnelSprite,
            FeatureType.Harbour => harbourSprite,
            FeatureType.Ferry => roadSprite, // Use road sprite for ferry routes (will be colored differently)
            FeatureType.Town => townSprite,
            FeatureType.Village => villageSprite,
            _ => null
        };
    }

    private Color GetTerrainColor(TerrainType terrainType)
    {
        return terrainType switch
        {
            TerrainType.Plains => new Color(0.6f, 0.8f, 0.4f), // Light green
            TerrainType.Forest => new Color(0.2f, 0.6f, 0.2f), // Dark green
            TerrainType.Ocean => new Color(0.2f, 0.4f, 0.8f), // Blue
            TerrainType.Lake => new Color(0.3f, 0.5f, 0.9f), // Light blue
            TerrainType.Shore => new Color(0.8f, 0.7f, 0.5f), // Sandy
            TerrainType.River => new Color(0.4f, 0.6f, 1f), // River blue
            TerrainType.Mountain => new Color(0.5f, 0.5f, 0.5f), // Gray
            TerrainType.ForestRiver => new Color(0.3f, 0.5f, 0.7f), // Blue-green mix
            _ => Color.white
        };
    }

    private Color GetFeatureColor(FeatureType featureType)
    {
        return featureType switch
        {
            FeatureType.Road => new Color(0.6f, 0.4f, 0.2f), // Brown
            FeatureType.Bridge => new Color(0.9f, 0.8f, 0.6f), // Light tan/wood color
            FeatureType.Tunnel => new Color(0.3f, 0.3f, 0.3f), // Dark gray
            FeatureType.Harbour => new Color(0.4f, 0.3f, 0.2f), // Dark brown
            FeatureType.Ferry => new Color(0.7f, 0.9f, 1.0f), // Light blue/cyan for water routes
            FeatureType.Town => new Color(0.8f, 0.2f, 0.2f), // Red
            FeatureType.Village => new Color(0.9f, 0.7f, 0.3f), // Yellow
            _ => Color.white
        };
    }

    public void ClearAllTiles()
    {
        foreach (var tile in terrainTiles.Values)
        {
            if (tile != null) DestroyImmediate(tile);
        }

        foreach (var tile in featureTiles.Values)
        {
            if (tile != null) DestroyImmediate(tile);
        }

        terrainTiles.Clear();
        featureTiles.Clear();
    }

    private void CreateSimpleForestRiverEffect(GameObject forestRiverTile)
    {
        // Create a simple blue overlay to indicate river flowing through forest
        GameObject riverEffect = new GameObject("RiverEffect");
        riverEffect.transform.parent = forestRiverTile.transform;
        riverEffect.transform.localPosition = Vector3.zero;
        riverEffect.transform.localScale = new Vector3(0.6f, 0.6f, 1f); // Smaller than the base tile

        SpriteRenderer effectRenderer = riverEffect.AddComponent<SpriteRenderer>();
        effectRenderer.sprite = defaultSprite != null ? defaultSprite : generatedDefaultSprite;
        effectRenderer.sortingOrder = 1; // Above the forest base
        effectRenderer.color = new Color(0.4f, 0.6f, 1f, 0.7f); // Semi-transparent river blue
    }
}