using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

/// <summary>
/// MinimapEdgeIndicator - Shows arrows on the edge of the minimap UI
/// pointing toward off-screen entities (allies, enemies, cash bags, objectives).
/// 
/// Like games such as GTA, Fortnite, and PUBG - when entities are outside the 
/// minimap view, arrows appear at the border pointing in their direction.
/// 
/// Attach to the minimap RawImage UI element, or let MinimapManager create it.
/// </summary>
public class MinimapEdgeIndicator : MonoBehaviour
{
    [Header("Settings")]
    [SerializeField] private RectTransform minimapRect;
    [SerializeField] private float edgePadding = 15f;
    [SerializeField] private float indicatorSize = 60f;
    [SerializeField] private float updateInterval = 0.1f;
    
    [Header("Visibility")]
    [SerializeField] private bool showAllies = true;
    [SerializeField] private bool showEnemies = true;
    [SerializeField] private bool showCashBags = true;
    [SerializeField] private bool showVaults = true;
    
    [Header("Colors")]
    [SerializeField] private Color allyColor = new Color(0.2f, 0.8f, 0.2f, 1f);
    [SerializeField] private Color enemyColor = new Color(1f, 0.2f, 0.2f, 1f);
    [SerializeField] private Color cashBagColor = new Color(1f, 0.85f, 0f, 1f);
    [SerializeField] private Color playerVaultColor = new Color(0.2f, 0.9f, 0.2f, 1f);
    [SerializeField] private Color enemyVaultColor = new Color(0.9f, 0.2f, 0.2f, 1f);

    // Pool of indicator objects
    private List<RectTransform> indicatorPool = new List<RectTransform>();
    private List<Image> indicatorImages = new List<Image>();
    private int activeIndicatorCount = 0;
    
    private Camera minimapCamera;
    private Transform playerTransform;
    private string playerTeamTag = "Player";
    private float lastUpdateTime;
    private Sprite arrowSprite;
    private float playerHeadingDeg; // Cached player Y rotation for GTA V style
    
    // Cached references for performance
    private CashSystemManager cachedCSM;
    private bool csmSearched = false;
    
    private void Start()
    {
        // Find minimap camera
        MinimapManager mm = FindObjectOfType<MinimapManager>();
        if (mm != null)
        {
            minimapCamera = mm.GetComponent<Camera>();
        }
        
        // Auto-find minimap rect if not assigned
        if (minimapRect == null)
        {
            minimapRect = GetComponent<RectTransform>();
            
            // Try parent if this component doesn't have RectTransform
            if (minimapRect == null)
            {
                minimapRect = GetComponentInParent<RectTransform>();
            }
        }
        
        // Create arrow sprite once
        arrowSprite = CreateArrowSprite();
        
        // Create initial pool
        for (int i = 0; i < 15; i++)
        {
            CreateIndicator();
        }
        
        Debug.Log("[MinimapEdgeIndicator] Initialized - will show arrows for off-screen entities");
    }
    
    private void CreateIndicator()
    {
        GameObject indicatorObj = new GameObject("EdgeIndicator");
        indicatorObj.transform.SetParent(transform, false);
        
        RectTransform rect = indicatorObj.AddComponent<RectTransform>();
        rect.sizeDelta = new Vector2(indicatorSize, indicatorSize);
        rect.localScale = Vector3.one;
        
        Image img = indicatorObj.AddComponent<Image>();
        img.sprite = arrowSprite;
        img.color = enemyColor;
        img.raycastTarget = false;
        
        // Add outline for better visibility
        Outline outline = indicatorObj.AddComponent<Outline>();
        outline.effectColor = new Color(0, 0, 0, 0.8f);
        outline.effectDistance = new Vector2(1, 1);
        
        indicatorObj.SetActive(false);
        
        indicatorPool.Add(rect);
        indicatorImages.Add(img);
    }
    
    private Sprite CreateArrowSprite()
    {
        int size = 48; // Higher resolution for sharper arrow
        Texture2D tex = new Texture2D(size, size);
        tex.filterMode = FilterMode.Bilinear;
        Color[] colors = new Color[size * size];
        
        for (int i = 0; i < colors.Length; i++) colors[i] = Color.clear;
        
        Vector2 center = new Vector2(size / 2f, size / 2f);
        
        // Draw a clean filled arrow/chevron pointing right
        // (rotation applied at runtime to point toward off-screen targets)
        for (int y = 0; y < size; y++)
        {
            for (int x = 0; x < size; x++)
            {
                float nx = (x - center.x) / (size / 2f); // -1 to 1
                float ny = (y - center.y) / (size / 2f); // -1 to 1
                
                // Filled arrow pointing right: tip at nx=0.8, base fans from nx=-0.4
                // The arrow body fills from base to tip
                if (nx >= -0.4f && nx <= 0.8f)
                {
                    float t = (nx + 0.4f) / 1.2f; // 0 at base, 1 at tip
                    float allowedY = 0.6f * (1f - t); // Max width at base, 0 at tip
                    
                    if (Mathf.Abs(ny) <= allowedY)
                    {
                        // Anti-alias the edges
                        float edgeDist = allowedY - Mathf.Abs(ny);
                        float alpha = Mathf.Clamp01(edgeDist * 4f);
                        colors[y * size + x] = new Color(1, 1, 1, alpha);
                    }
                }
            }
        }
        
        tex.SetPixels(colors);
        tex.Apply();
        
        return Sprite.Create(tex, new Rect(0, 0, size, size), new Vector2(0.5f, 0.5f));
    }
    
    private void Update()
    {
        if (Time.time - lastUpdateTime < updateInterval) return;
        lastUpdateTime = Time.time;
        
        UpdateIndicators();
    }
    
    private void UpdateIndicators()
    {
        // Find player
        FindPlayer();
        if (playerTransform == null || minimapCamera == null || minimapRect == null) return;
        
        // Hide all first
        activeIndicatorCount = 0;
        for (int i = 0; i < indicatorPool.Count; i++)
        {
            indicatorPool[i].gameObject.SetActive(false);
        }
        
        Vector3 playerPos = playerTransform.position;
        float viewSize = minimapCamera.orthographicSize;
        
        // Cache player heading for GTA V rotating minimap
        playerHeadingDeg = playerTransform.eulerAngles.y;
        
        // Get minimap UI half-size
        Vector2 halfSize = minimapRect.rect.size / 2f;
        
        // Track all characters using static registry (zero-allocation, replaces FindObjectsOfType)
        if (showAllies || showEnemies)
        {
            foreach (var member in TeamMember.AllMembers)
            {
                if (member == null || member.transform == playerTransform) continue;
                
                bool isAlly = member.gameObject.CompareTag(playerTeamTag);
                
                if (isAlly && showAllies)
                {
                    ShowIndicatorIfOffscreen(member.transform.position, playerPos, viewSize, halfSize, allyColor);
                }
                else if (!isAlly && showEnemies)
                {
                    ShowIndicatorIfOffscreen(member.transform.position, playerPos, viewSize, halfSize, enemyColor);
                }
            }
        }
        
        // Cash bags
        if (showCashBags)
        {
            var cashBags = EntityRegistry<CashBag>.GetAll();
            for (int i = 0; i < cashBags.Count; i++)
            {
                if (cashBags[i] == null) continue;
                ShowIndicatorIfOffscreen(cashBags[i].transform.position, playerPos, viewSize, halfSize, cashBagColor);
            }
        }
        
        // Vaults
        if (showVaults)
        {
            TeamVault[] vaults = FindObjectsOfType<TeamVault>();
            foreach (var vault in vaults)
            {
                if (vault == null) continue;
                Color vColor = vault.TeamTag == playerTeamTag ? playerVaultColor : enemyVaultColor;
                ShowIndicatorIfOffscreen(vault.transform.position, playerPos, viewSize, halfSize, vColor);
            }
        }
    }
    
    private void ShowIndicatorIfOffscreen(Vector3 worldPos, Vector3 playerPos, float viewSize, Vector2 halfSize, Color color)
    {
        // Calculate relative position in world space
        Vector3 relativePos = worldPos - playerPos;
        Vector2 relativePos2D = new Vector2(relativePos.x, relativePos.z);
        
        // GTA V STYLE: Rotate by negative player heading so edge positions
        // are in "minimap space" (which rotates with the player).
        // Without this, arrows point to wrong edges when the map is rotated.
        float headingRad = -playerHeadingDeg * Mathf.Deg2Rad;
        float cos = Mathf.Cos(headingRad);
        float sin = Mathf.Sin(headingRad);
        Vector2 rotated = new Vector2(
            relativePos2D.x * cos - relativePos2D.y * sin,
            relativePos2D.x * sin + relativePos2D.y * cos
        );
        
        // Convert world distance to minimap UI space
        float worldToUIScale = halfSize.y / viewSize;
        Vector2 minimapPos = rotated * worldToUIScale;
        
        // Check if within minimap bounds
        bool isWithinBounds = Mathf.Abs(minimapPos.x) <= halfSize.x * 0.85f && 
                              Mathf.Abs(minimapPos.y) <= halfSize.y * 0.85f;
        
        if (isWithinBounds) return; // Already visible on minimap, don't show edge indicator
        
        // Calculate angle for arrow rotation (in rotated/minimap space)
        float angle = Mathf.Atan2(rotated.y, rotated.x) * Mathf.Rad2Deg;
        
        // Clamp to edge of minimap
        Vector2 normalizedDir = rotated.normalized;
        
        // Find intersection with minimap edge
        float maxX = halfSize.x - edgePadding;
        float maxY = halfSize.y - edgePadding;
        
        float tX = normalizedDir.x != 0 ? Mathf.Abs(maxX / normalizedDir.x) : float.MaxValue;
        float tY = normalizedDir.y != 0 ? Mathf.Abs(maxY / normalizedDir.y) : float.MaxValue;
        float t = Mathf.Min(tX, tY);
        
        Vector2 clampedPos = normalizedDir * t;
        
        // Get or create indicator
        if (activeIndicatorCount >= indicatorPool.Count)
        {
            CreateIndicator();
        }
        
        RectTransform indicator = indicatorPool[activeIndicatorCount];
        Image img = indicatorImages[activeIndicatorCount];
        
        indicator.gameObject.SetActive(true);
        indicator.sizeDelta = new Vector2(indicatorSize, indicatorSize); // Force size every frame
        indicator.anchoredPosition = clampedPos;
        indicator.localRotation = Quaternion.Euler(0, 0, angle);
        img.color = color;
        
        activeIndicatorCount++;
    }
    
    private void FindPlayer()
    {
        // Cache CashSystemManager lookup
        if (!csmSearched)
        {
            cachedCSM = FindObjectOfType<CashSystemManager>();
            csmSearched = true;
        }
        
        // In Cash System mode, find controlled character
        if (cachedCSM != null && cachedCSM.CurrentControlledCharacter != null)
        {
            playerTransform = cachedCSM.CurrentControlledCharacter.transform;
            playerTeamTag = cachedCSM.CurrentControlledCharacter.tag;
            return;
        }
        
        // Fallback to Player tag
        if (playerTransform == null)
        {
            GameObject player = GameObject.FindGameObjectWithTag("Player");
            if (player != null)
            {
                playerTransform = player.transform;
                playerTeamTag = "Player";
            }
        }
    }
    
    /// <summary>
    /// Creates and attaches an edge indicator to a minimap UI element
    /// </summary>
    public static MinimapEdgeIndicator AttachTo(RectTransform minimapUI)
    {
        if (minimapUI == null) return null;
        
        MinimapEdgeIndicator existing = minimapUI.GetComponent<MinimapEdgeIndicator>();
        if (existing != null) return existing;
        
        MinimapEdgeIndicator indicator = minimapUI.gameObject.AddComponent<MinimapEdgeIndicator>();
        indicator.minimapRect = minimapUI;
        
        Debug.Log("[MinimapEdgeIndicator] Attached to " + minimapUI.name);
        return indicator;
    }
}
