using UnityEngine;

/// <summary>
/// MinimapMarker - Creates a 3D marker visible to the minimap camera.
/// This follows the same pattern as TeamIndicator but renders on a minimap-specific layer.
/// Attach to character prefabs or add dynamically via MinimapMarkerManager.
/// 
/// IMPORTANT: You MUST create a layer called "Minimap" in Unity Project Settings
/// and configure your Main Camera to NOT render this layer (uncheck it in Culling Mask).
/// </summary>
public class MinimapMarker : MonoBehaviour
{
    [Header("Marker Settings")]
    [SerializeField] private float heightOffset = 5f; // Height above character for minimap visibility
    [SerializeField] private float markerSize = 6f;   // Size of marker in world units
    
    [Header("Team Colors")]
    [SerializeField] private Color allyColor = new Color(0.2f, 0.8f, 0.2f, 1f);      // Green
    [SerializeField] private Color enemyColor = new Color(0.9f, 0.2f, 0.2f, 1f);     // Red
    [SerializeField] private Color controlledColor = Color.white;                      // White (GTA V style)
    
    [Header("Layer Settings")]
    [SerializeField] private string minimapLayerName = "Minimap";
    
    private GameObject markerObject;
    private MeshRenderer markerRenderer;
    private TeamMember teamMember;
    private string myTeamTag;
    private bool isCurrentlyControlled = false;
    private bool hasConfiguredLayer = false;
    
    // Cached textures to avoid regeneration
    private static Texture2D cachedCircleTexture;
    private static Texture2D cachedArrowTexture;
    
    // Static reference to current player team tag
    private static string currentPlayerTeamTag = "Player";
    
    private bool _markerCreated;

    private void Start()
    {
        // Works in ALL modes (Cash System, Practice, etc.)
        teamMember = GetComponent<TeamMember>();
        myTeamTag = gameObject.tag;
        
        CreateMarker();
        UpdateMarkerColor();
    }
    
    private void LateUpdate()
    {
        if (markerObject == null) return;
        
        // Ensure layer is correctly set (handles late layer creation)
        if (!hasConfiguredLayer)
        {
            ConfigureLayer();
        }
        
        // Check if this character is currently controlled
        bool wasControlled = isCurrentlyControlled;
        isCurrentlyControlled = teamMember != null && teamMember.IsPlayerControlled;
        
        if (isCurrentlyControlled)
        {
            currentPlayerTeamTag = myTeamTag;
            
            // PARENT to player so position + yaw follow exactly (no lag/jitter)
            if (markerObject.transform.parent != transform)
                markerObject.transform.SetParent(transform, false);
            
            // Local position: straight up; local rotation: flat quad facing up
            // The minimap camera rotates with the player, so the marker just
            // sits above the player and the camera handles heading visuals.
            markerObject.transform.localPosition = Vector3.up * heightOffset;
            markerObject.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
            
            markerObject.transform.localScale = Vector3.one * (markerSize * 2f);
        }
        else
        {
            // AI / enemy markers: also parent to owner so they can't drift
            if (markerObject.transform.parent != transform)
                markerObject.transform.SetParent(transform, false);
            
            markerObject.transform.localPosition = Vector3.up * heightOffset;
            markerObject.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
            markerObject.transform.localScale = Vector3.one * markerSize;
        }
        
        // Update color/shape if control status changed
        if (wasControlled != isCurrentlyControlled)
        {
            UpdateMarkerVisuals();
        }
        
        markerObject.SetActive(true);
    }
    
    private void CreateMarker()
    {
        // Guard: only ever create ONE marker per component instance
        if (_markerCreated && markerObject != null) return;
        
        markerObject = GameObject.CreatePrimitive(PrimitiveType.Quad);
        markerObject.name = $"{gameObject.name}_MinimapMarker";
        
        // Parent to owner immediately so it follows position + rotation
        markerObject.transform.SetParent(transform, false);
        markerObject.transform.localPosition = Vector3.up * heightOffset;
        markerObject.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
        markerObject.transform.localScale = Vector3.one * markerSize;
        
        // Remove collider - we don't need it
        Collider col = markerObject.GetComponent<Collider>();
        if (col != null) Destroy(col);
        
        // Configure layer
        ConfigureLayer();
        
        // Get renderer and create material
        markerRenderer = markerObject.GetComponent<MeshRenderer>();
        
        Material markerMaterial = new Material(Shader.Find("Sprites/Default"));
        if (markerMaterial != null)
        {
            markerRenderer.material = markerMaterial;
        }
        
        _markerCreated = true;
        Debug.Log($"[MinimapMarker] Created marker for {gameObject.name} on layer {markerObject.layer}");
    }
    
    private void ConfigureLayer()
    {
        int minimapLayer = LayerMask.NameToLayer(minimapLayerName);
        
        if (minimapLayer == -1)
        {
            // Layer not found - this is a critical setup issue
            // Log error once and use a high layer number that's less likely to be rendered
            if (!hasConfiguredLayer)
            {
                Debug.LogError($"[MinimapMarker] CRITICAL: Layer '{minimapLayerName}' not found! " +
                    "Please create this layer in Project Settings > Tags and Layers. " +
                    "IMPORTANT: Set your Main Camera's Culling Mask to EXCLUDE the 'Minimap' layer!");
            }
            // Use layer 31 as fallback (typically unused, less likely to show on main camera)
            markerObject.layer = 31;
        }
        else
        {
            markerObject.layer = minimapLayer;
            hasConfiguredLayer = true;
        }
    }
    
    private void UpdateMarkerVisuals()
    {
        if (markerRenderer == null) return;
        
        Color targetColor;
        Texture2D texture;
        
        // Currently controlled character
        if (isCurrentlyControlled)
        {
            targetColor = controlledColor;
            texture = GetArrowTexture(); // Directional arrow
        }
        // Determine if ally or enemy relative to player
        else if (myTeamTag == currentPlayerTeamTag)
        {
            targetColor = allyColor;
            texture = GetCircleTexture(); // Round circle
        }
        else
        {
            targetColor = enemyColor;
            texture = GetCircleTexture(); // Round circle
        }
        
        markerRenderer.material.color = targetColor;
        markerRenderer.material.mainTexture = texture;
    }
    
    // Get cached circle texture
    private Texture2D GetCircleTexture()
    {
        if (cachedCircleTexture == null)
        {
            cachedCircleTexture = GenerateCircleTexture();
        }
        return cachedCircleTexture;
    }
    
    // Get cached arrow texture
    private Texture2D GetArrowTexture()
    {
        if (cachedArrowTexture == null)
        {
            cachedArrowTexture = GenerateArrowTexture();
        }
        return cachedArrowTexture;
    }
    
    // Generate a clean solid dot — GTA V style (filled circle with soft edge)
    private Texture2D GenerateCircleTexture()
    {
        int size = 64;
        Texture2D tex = new Texture2D(size, size);
        Color[] colors = new Color[size * size];
        float center = size / 2f;
        float radius = size / 2f - 4f; // Slightly smaller for clean edges
        
        for (int y = 0; y < size; y++)
        {
            for (int x = 0; x < size; x++)
            {
                float dist = Vector2.Distance(new Vector2(x, y), new Vector2(center, center));
                
                if (dist <= radius - 1.5f)
                {
                    // Solid fill
                    colors[y * size + x] = Color.white;
                }
                else if (dist <= radius + 1.5f)
                {
                    // Anti-aliased edge
                    float alpha = 1f - Mathf.Clamp01((dist - radius + 1.5f) / 3f);
                    colors[y * size + x] = new Color(1, 1, 1, alpha);
                }
                else
                {
                    colors[y * size + x] = Color.clear;
                }
            }
        }
        
        tex.SetPixels(colors);
        tex.Apply();
        return tex;
    }
    
    // Generate a GTA V-style chevron (V-shape) pointing up
    // Sharp pointed front, open back — gives instant directional clarity
    private Texture2D GenerateArrowTexture()
    {
        int size = 64;
        Texture2D tex = new Texture2D(size, size);
        Color[] colors = new Color[size * size];
        
        // Clear transparency
        for (int i = 0; i < colors.Length; i++) colors[i] = Color.clear;
        
        float cx = size / 2f;
        float cy = size / 2f;
        float thickness = 5f; // Chevron line thickness
        
        // Chevron is two angled lines meeting at a point (top center)
        // Left arm: from bottom-left to top-center
        // Right arm: from bottom-right to top-center
        Vector2 tip = new Vector2(cx, size * 0.85f);         // Top tip
        Vector2 bottomLeft = new Vector2(size * 0.15f, size * 0.25f);  // Bottom left
        Vector2 bottomRight = new Vector2(size * 0.85f, size * 0.25f); // Bottom right
        
        for (int y = 0; y < size; y++)
        {
            for (int x = 0; x < size; x++)
            {
                Vector2 p = new Vector2(x, y);
                
                // Distance to left arm line segment
                float distLeft = DistanceToSegment(p, bottomLeft, tip);
                // Distance to right arm line segment
                float distRight = DistanceToSegment(p, bottomRight, tip);
                
                float minDist = Mathf.Min(distLeft, distRight);
                
                if (minDist <= thickness)
                {
                    // Solid white with anti-aliased edges
                    float alpha = 1f - Mathf.Clamp01((minDist - thickness + 1.5f) / 1.5f);
                    alpha = Mathf.Max(alpha, 0f);
                    if (minDist < thickness - 1f) alpha = 1f;
                    colors[y * size + x] = new Color(1, 1, 1, alpha);
                }
            }
        }
        
        tex.SetPixels(colors);
        tex.Apply();
        return tex;
    }
    
    /// <summary>
    /// Distance from point P to line segment AB
    /// </summary>
    private static float DistanceToSegment(Vector2 p, Vector2 a, Vector2 b)
    {
        Vector2 ab = b - a;
        float t = Mathf.Clamp01(Vector2.Dot(p - a, ab) / Vector2.Dot(ab, ab));
        Vector2 closest = a + t * ab;
        return Vector2.Distance(p, closest);
    }
    
    private void UpdateMarkerColor()
    {
        UpdateMarkerVisuals();
    }
    
    private void OnDestroy()
    {
        if (markerObject != null)
        {
            Destroy(markerObject);
        }
    }
    
    /// <summary>
    /// Force update the marker color (call after team changes)
    /// </summary>
    public void RefreshMarker()
    {
        myTeamTag = gameObject.tag;
        UpdateMarkerColor();
    }
    
    /// <summary>
    /// Set marker visibility
    /// </summary>
    public void SetVisible(bool visible)
    {
        if (markerObject != null)
        {
            markerObject.SetActive(visible);
        }
    }
    
    /// <summary>
    /// Static method to update all markers when player changes
    /// </summary>
    public static void RefreshAllMarkers()
    {
        foreach (var marker in FindObjectsOfType<MinimapMarker>())
        {
            marker.RefreshMarker();
        }
    }
}
