using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Theriapolis.Core;
using Theriapolis.Core.Entities;
using Theriapolis.Core.Rules.Character;

namespace Theriapolis.Game.Rendering;

/// <summary>
/// Phase 6 M4 — renders <see cref="NpcActor"/>s on the tactical map (and
/// as muted dots on the world map at low zoom). Mirrors
/// <see cref="PlayerSprite"/>'s counter-scale-by-1/Zoom approach so NPCs
/// stay constant-on-screen-size at any zoom level.
///
/// Body colour encodes allegiance:
///   Hostile   → red
///   Neutral   → grey
///   Friendly  → green
///   Allied    → cyan
/// Player allegiance never shows here (only the player sprite renders).
///
/// A coloured outer ring is drawn beneath each body so NPCs remain
/// visible against similar-toned terrain (settlement cobble, rock,
/// snow). Generic placeholder art — Phase 9 polish swaps for real
/// per-species sprites.
/// </summary>
public sealed class NpcSprite : IDisposable
{
    private readonly GraphicsDevice _gd;
    private Texture2D _hostile = null!;
    private Texture2D _neutral = null!;
    private Texture2D _friendly = null!;
    private Texture2D _allied = null!;
    private Texture2D _ring   = null!;
    private bool _disposed;

    private const int MarkerPx = C.PLAYER_MARKER_SCREEN_PX;

    public NpcSprite(GraphicsDevice gd)
    {
        _gd = gd;
        BuildTextures();
    }

    private void BuildTextures()
    {
        _hostile  = BuildBody(new Color(220,  60,  50));
        _neutral  = BuildBody(new Color(180, 180, 180));
        _friendly = BuildBody(new Color( 90, 180,  90));
        _allied   = BuildBody(new Color( 90, 200, 220));
        _ring     = BuildRing();
    }

    private Texture2D BuildBody(Color body)
    {
        int s = MarkerPx;
        var pixels = new Color[s * s];
        float cx = (s - 1) * 0.5f, cy = (s - 1) * 0.5f, r = s * 0.5f - 1f;
        // Slightly smaller body than player (0.78 vs 0.85) so player reads as
        // the protagonist when next to NPCs.
        for (int y = 0; y < s; y++)
        for (int x = 0; x < s; x++)
        {
            float nx = x - cx, ny = y - cy;
            float dist = MathF.Sqrt(nx * nx + ny * ny);
            pixels[y * s + x] = dist <= r * 0.78f ? body : Color.Transparent;
        }
        var tex = new Texture2D(_gd, s, s);
        tex.SetData(pixels);
        return tex;
    }

    private Texture2D BuildRing()
    {
        int s = MarkerPx;
        var pixels = new Color[s * s];
        float cx = (s - 1) * 0.5f, cy = (s - 1) * 0.5f, r = s * 0.5f - 1f;
        for (int y = 0; y < s; y++)
        for (int x = 0; x < s; x++)
        {
            float nx = x - cx, ny = y - cy;
            float dist = MathF.Sqrt(nx * nx + ny * ny);
            pixels[y * s + x] = (dist > r * 0.78f && dist <= r) ? new Color(0, 0, 0, 200) : Color.Transparent;
        }
        var tex = new Texture2D(_gd, s, s);
        tex.SetData(pixels);
        return tex;
    }

    /// <summary>Draw every live NPC. Caller wraps SpriteBatch.Begin/End around it.</summary>
    public void Draw(SpriteBatch sb, Camera2D camera, IEnumerable<NpcActor> npcs)
    {
        sb.Begin(
            transformMatrix: camera.TransformMatrix,
            samplerState:    SamplerState.PointClamp,
            sortMode:        SpriteSortMode.Deferred,
            blendState:      BlendState.AlphaBlend);

        int s = MarkerPx;
        var origin = new Vector2(s * 0.5f, s * 0.5f);
        float screenScale = camera.Zoom > 1e-6f ? 1f / camera.Zoom : 1f;

        foreach (var npc in npcs)
        {
            if (!npc.IsAlive) continue;
            var pos  = new Vector2(npc.Position.X, npc.Position.Y);
            var body = TextureFor(npc.Allegiance);

            sb.Draw(_ring, pos, null, Color.White, 0f, origin, screenScale, SpriteEffects.None, 0f);
            sb.Draw(body,  pos, null, Color.White, 0f, origin, screenScale, SpriteEffects.None, 0f);
        }

        sb.End();
    }

    private Texture2D TextureFor(Allegiance a) => a switch
    {
        Allegiance.Hostile  => _hostile,
        Allegiance.Allied   => _allied,
        Allegiance.Friendly => _friendly,
        Allegiance.Neutral  => _neutral,
        _                    => _neutral,
    };

    public void Dispose()
    {
        if (_disposed) return;
        _disposed = true;
        _hostile?.Dispose();
        _neutral?.Dispose();
        _friendly?.Dispose();
        _allied?.Dispose();
        _ring?.Dispose();
    }
}
