using UnityEngine;

namespace FishROV.AvoidanceBenchmark
{
    public sealed class BenchmarkAgent : MonoBehaviour
    {
        private const int ConeSegments = 16;

        private static Mesh sharedConeMesh;
        private static Material sharedConeMaterial;
        private static MaterialPropertyBlock colorPropertyBlock;

        private Vector3 velocity;
        private float maxSpeed = 1f;

        public BenchmarkAgentRole Role { get; private set; }
        public int GroupId { get; private set; }
        public float Radius { get; private set; }
        public float MaxSpeed => maxSpeed;
        public Vector3 Velocity => velocity;
        public Vector3 Target { get; set; }

        public static BenchmarkAgent CreatePrimitive(BenchmarkAgentSpawnInfo spawnInfo, Transform parent)
        {
            var go = CreateConeObject();
            go.name = $"{spawnInfo.Role}_{spawnInfo.GroupId:0000}";
            go.transform.SetParent(parent, false);
            go.transform.position = spawnInfo.Position;

            var scale = new Vector3(
                Mathf.Max(0.1f, spawnInfo.Radius * 1.4f),
                Mathf.Max(0.1f, spawnInfo.Radius * 1.4f),
                Mathf.Max(0.2f, spawnInfo.Radius * 3.2f));
            go.transform.localScale = scale;

            var collider = go.GetComponent<Collider>();
            if (collider != null)
            {
                collider.enabled = false;
            }

            var agent = go.AddComponent<BenchmarkAgent>();
            agent.Initialize(spawnInfo);
            return agent;
        }

        public void Initialize(BenchmarkAgentSpawnInfo spawnInfo)
        {
            Role = spawnInfo.Role;
            GroupId = spawnInfo.GroupId;
            Radius = spawnInfo.Radius;
            maxSpeed = Mathf.Max(0.01f, spawnInfo.MaxSpeed);
            ApplyColor();
        }

        public void ApplyVelocity(Vector3 preferredVelocity)
        {
            velocity = Vector3.ClampMagnitude(preferredVelocity, maxSpeed);
        }

        public void Integrate(float deltaTime, AvoidanceDimensionMode dimensionMode, float arenaRadius)
        {
            var next = transform.position + velocity * deltaTime;
            if (dimensionMode != AvoidanceDimensionMode.XYZ3D)
            {
                next.y = transform.position.y;
            }

            var flat = new Vector2(next.x, next.z);
            if (flat.magnitude > arenaRadius)
            {
                flat = flat.normalized * arenaRadius;
                next.x = flat.x;
                next.z = flat.y;
            }

            transform.position = next;

            var flatVelocity = new Vector3(velocity.x, 0f, velocity.z);
            if (flatVelocity.sqrMagnitude > 0.0001f)
            {
                transform.rotation = Quaternion.LookRotation(flatVelocity, Vector3.up);
            }
        }

        private void ApplyColor()
        {
            var renderer = GetComponent<Renderer>();
            if (renderer == null)
            {
                return;
            }

            var color = Color.white;
            switch (Role)
            {
                case BenchmarkAgentRole.Fish:
                    color = new Color(0.10f, 0.72f, 0.95f);
                    break;
                case BenchmarkAgentRole.SchoolProxy:
                    color = new Color(0.20f, 0.95f, 0.42f);
                    break;
                case BenchmarkAgentRole.Obstacle:
                    color = new Color(0.85f, 0.80f, 0.38f);
                    break;
            }

            var propertyBlock = GetColorPropertyBlock();
            propertyBlock.SetColor("_Color", color);
            renderer.SetPropertyBlock(propertyBlock);
        }

        private static GameObject CreateConeObject()
        {
            var go = new GameObject("ConeAgent");
            var meshFilter = go.AddComponent<MeshFilter>();
            meshFilter.sharedMesh = GetSharedConeMesh();

            var meshRenderer = go.AddComponent<MeshRenderer>();
            meshRenderer.sharedMaterial = GetSharedConeMaterial();
            return go;
        }

        private static Mesh GetSharedConeMesh()
        {
            if (sharedConeMesh != null)
            {
                return sharedConeMesh;
            }

            var vertices = new Vector3[ConeSegments + 2];
            var triangles = new int[ConeSegments * 6];

            vertices[0] = new Vector3(0f, 0f, 0.5f);
            vertices[1] = new Vector3(0f, 0f, -0.5f);

            for (var i = 0; i < ConeSegments; i++)
            {
                var angle = i * Mathf.PI * 2f / ConeSegments;
                vertices[i + 2] = new Vector3(Mathf.Cos(angle) * 0.5f, Mathf.Sin(angle) * 0.5f, -0.5f);
            }

            var triangleIndex = 0;
            for (var i = 0; i < ConeSegments; i++)
            {
                var current = i + 2;
                var next = i == ConeSegments - 1 ? 2 : current + 1;

                triangles[triangleIndex++] = 0;
                triangles[triangleIndex++] = current;
                triangles[triangleIndex++] = next;

                triangles[triangleIndex++] = 1;
                triangles[triangleIndex++] = next;
                triangles[triangleIndex++] = current;
            }

            sharedConeMesh = new Mesh
            {
                name = "Benchmark Agent Direction Cone",
                vertices = vertices,
                triangles = triangles
            };
            sharedConeMesh.RecalculateNormals();
            sharedConeMesh.RecalculateBounds();
            return sharedConeMesh;
        }

        private static Material GetSharedConeMaterial()
        {
            if (sharedConeMaterial != null)
            {
                return sharedConeMaterial;
            }

            sharedConeMaterial = new Material(Shader.Find("Standard"))
            {
                name = "Benchmark Agent Shared Material"
            };
            return sharedConeMaterial;
        }

        private static MaterialPropertyBlock GetColorPropertyBlock()
        {
            return colorPropertyBlock ??= new MaterialPropertyBlock();
        }
    }
}
