using System.Collections.Generic;
using UnityEngine;

namespace FishROV.AvoidanceBenchmark
{
    public sealed class AvoidancePerfBootstrap : MonoBehaviour
    {
        private static readonly int[] CountPresets = { 100, 300, 500, 800, 1000, 1500, 2000, 3000, 5000 };

        [SerializeField] private AvoidanceFrameworkKind framework = AvoidanceFrameworkKind.NoAvoidance;
        [SerializeField] private AvoidanceScenarioKind scenario = AvoidanceScenarioKind.FreeSwim;
        [SerializeField] private AvoidanceDimensionMode dimensionMode = AvoidanceDimensionMode.XZ25D;
        [SerializeField] private BenchmarkViewMode viewMode = BenchmarkViewMode.Overview;
        [SerializeField] private int fishCount = 300;

        private readonly List<BenchmarkAgent> agents = new List<BenchmarkAgent>(512);
        private readonly AvoidanceScenarioController scenarioController = new AvoidanceScenarioController();
        private readonly AvoidanceMetricsRecorder metrics = new AvoidanceMetricsRecorder();
        private readonly AvoidanceCollisionProbe collisionProbe = new AvoidanceCollisionProbe();
        private readonly AvoidanceBenchmarkLogWriter logWriter = new AvoidanceBenchmarkLogWriter();
        private AvoidanceBenchmarkConfig config;
        private IAvoidanceAdapter adapter;
        private Transform agentRoot;
        private Camera benchmarkCamera;
        private bool paused;
        private int pendingFishCount;

        private void Start()
        {
            QualitySettings.vSyncCount = 0;
            Application.targetFrameRate = -1;
            pendingFishCount = fishCount;
            metrics.Start();
            EnsureCameraAndLight();
            ResetBenchmark();
        }

        private void Update()
        {
            metrics.RecordFrame(Time.unscaledDeltaTime);
            UpdateCamera();

            if (paused || adapter == null)
            {
                return;
            }

            metrics.BeginSimulation();
            foreach (var agent in agents)
            {
                var velocity = scenarioController.GetPreferredVelocity(agent, Time.deltaTime);
                adapter.SetPreferredVelocity(agent, velocity);
            }

            adapter.Tick(Time.deltaTime);
            foreach (var agent in agents)
            {
                agent.Integrate(Time.deltaTime, config.DimensionMode, config.ArenaRadius);
            }

            collisionProbe.Update(agents, config.DimensionMode, Time.unscaledDeltaTime);
            metrics.EndSimulation();
            logWriter.Record(metrics, collisionProbe, agents.Count);
        }

        private void OnGUI()
        {
            GUILayout.BeginArea(new Rect(12, 12, 390, Screen.height - 24), GUI.skin.box);
            GUILayout.Label("FishROV 本地避障压测");
            GUILayout.Label($"当前方案：{adapter?.Name ?? "未初始化"}");
            GUILayout.Label($"状态：{adapter?.Status ?? "未初始化"}");
            GUILayout.Label($"对象数量：鱼 {fishCount}，总数 {agents.Count}");
            GUILayout.Label($"帧率：平均 {metrics.AverageFps:F1} | 1% Low {metrics.OnePercentLowFps:F1}");
            GUILayout.Label($"模拟耗时：{metrics.SimulationMs:F2} ms | GC：{metrics.GcAllocBytes / 1024f:F1} KB");
            GUILayout.Label($"碰撞调试：XZ重叠 {collisionProbe.HorizontalOverlapPairs} 对 | 3D重叠 {collisionProbe.SpatialOverlapPairs} 对 | 最近间距 {collisionProbe.MinimumClearance:F2}");
            GUILayout.Label($"帧率限制：不锁帧，vSync={QualitySettings.vSyncCount}，targetFrameRate={Application.targetFrameRate}");
            GUILayout.Label($"日志样本：{logWriter.SampleCount} | {logWriter.LastExportPath}");

            GUILayout.Space(8);
            DrawEnumButtons("避障方案", framework, value =>
            {
                framework = value;
                ResetBenchmark();
            });
            DrawEnumButtons("测试场景", scenario, value =>
            {
                scenario = value;
                ResetBenchmark();
            });
            DrawEnumButtons("计算维度", dimensionMode, value =>
            {
                dimensionMode = value;
                ResetBenchmark();
            });
            DrawEnumButtons("观察视角", viewMode, value => viewMode = value);

            GUILayout.Space(8);
            GUILayout.Label($"小鱼数量：{pendingFishCount}");
            GUILayout.BeginHorizontal();
            foreach (var preset in CountPresets)
            {
                if (GUILayout.Button(preset.ToString(), GUILayout.Height(24)))
                {
                    pendingFishCount = preset;
                    fishCount = preset;
                    ResetBenchmark();
                }
            }
            GUILayout.EndHorizontal();

            pendingFishCount = Mathf.RoundToInt(GUILayout.HorizontalSlider(pendingFishCount, 10, 5000));

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("应用数量"))
            {
                fishCount = pendingFishCount;
                ResetBenchmark();
            }

            if (GUILayout.Button(paused ? "继续" : "暂停"))
            {
                paused = !paused;
            }

            if (GUILayout.Button("重置"))
            {
                ResetBenchmark();
            }

            if (GUILayout.Button("导出日志"))
            {
                logWriter.ExportIfNeeded();
            }
            GUILayout.EndHorizontal();
            GUILayout.EndArea();
        }

        private void OnDestroy()
        {
            logWriter.ExportIfNeeded();
            adapter?.Dispose();
            metrics.Dispose();
        }

        private void ResetBenchmark()
        {
            adapter?.Dispose();
            adapter = null;
            logWriter.ExportIfNeeded();

            if (agentRoot != null)
            {
                Destroy(agentRoot.gameObject);
            }

            Random.InitState(13999);
            config = new AvoidanceBenchmarkConfig
            {
                Framework = framework,
                Scenario = scenario,
                DimensionMode = dimensionMode,
                FishCount = fishCount,
                UseSchoolProxy = false
            };

            agentRoot = new GameObject("Benchmark Agents").transform;
            agents.Clear();
            adapter = AvoidanceAdapterRegistry.Create(config.Framework);
            adapter.Initialize(config);

            SpawnFish();
            scenarioController.Reset(config, agents);
            logWriter.StartRun(config, adapter.Name);
        }

        private void SpawnFish()
        {
            for (var i = 0; i < config.FishCount; i++)
            {
                var spawn = new BenchmarkAgentSpawnInfo
                {
                    Position = SpawnPosition(i, config.FishCount, config.ArenaRadius * 0.5f),
                    Radius = config.FishRadius,
                    Height = config.FishRadius * 2f,
                    MaxSpeed = config.FishSpeed,
                    Layer = 1,
                    CollidesWith = 1 | 2 | 4,
                    Priority = 0.2f,
                    Role = BenchmarkAgentRole.Fish,
                    GroupId = i
                };
                agents.Add(adapter.CreateAgent(spawn, agentRoot));
            }
        }

        private Vector3 SpawnPosition(int index, int total, float radius)
        {
            if (scenario == AvoidanceScenarioKind.CrossFlow)
            {
                var side = index % 2 == 0 ? -1f : 1f;
                return new Vector3(side * radius, LayerY(index), Random.Range(-radius * 0.7f, radius * 0.7f));
            }

            if (scenario == AvoidanceScenarioKind.DenseCircleStress)
            {
                var angle = index * Mathf.PI * 2f / Mathf.Max(1, total);
                return new Vector3(Mathf.Cos(angle) * radius, LayerY(index), Mathf.Sin(angle) * radius);
            }

            var point = Random.insideUnitCircle * radius;
            return new Vector3(point.x, LayerY(index), point.y);
        }

        private float LayerY(int index)
        {
            switch (dimensionMode)
            {
                case AvoidanceDimensionMode.XZ2D:
                    return 0f;
                case AvoidanceDimensionMode.XZ25D:
                    return (index % 5 - 2) * 0.22f;
                case AvoidanceDimensionMode.XYZ3D:
                    return Random.Range(-4f, 4f);
                default:
                    return 0f;
            }
        }

        private void EnsureCameraAndLight()
        {
            benchmarkCamera = Camera.main;
            if (benchmarkCamera == null)
            {
                var cameraGo = new GameObject("Main Camera");
                cameraGo.tag = "MainCamera";
                benchmarkCamera = cameraGo.AddComponent<Camera>();
                benchmarkCamera.clearFlags = CameraClearFlags.Skybox;
                benchmarkCamera.fieldOfView = 55f;
            }

            if (FindObjectOfType<Light>() == null)
            {
                var lightGo = new GameObject("Directional Light");
                var light = lightGo.AddComponent<Light>();
                light.type = LightType.Directional;
                light.intensity = 1.2f;
                lightGo.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
            }
        }

        private void UpdateCamera()
        {
            if (benchmarkCamera == null || config == null)
            {
                return;
            }

            var radius = config.ArenaRadius;
            Vector3 position;
            Quaternion rotation;

            switch (viewMode)
            {
                case BenchmarkViewMode.TopDown:
                    position = new Vector3(0f, radius * 2.1f, 0.1f);
                    rotation = Quaternion.Euler(89f, 0f, 0f);
                    break;
                case BenchmarkViewMode.Side:
                    position = new Vector3(0f, radius * 0.45f, -radius * 1.9f);
                    rotation = Quaternion.Euler(12f, 0f, 0f);
                    break;
                case BenchmarkViewMode.FollowSchool:
                    var center = CalculateAgentCenter();
                    position = center + new Vector3(0f, radius * 0.65f, -radius);
                    rotation = Quaternion.LookRotation(center - position, Vector3.up);
                    break;
                case BenchmarkViewMode.Overview:
                default:
                    position = new Vector3(0f, radius * 1.1f, -radius * 1.45f);
                    rotation = Quaternion.Euler(42f, 0f, 0f);
                    break;
            }

            benchmarkCamera.transform.SetPositionAndRotation(position, rotation);
        }

        private Vector3 CalculateAgentCenter()
        {
            if (agents.Count == 0)
            {
                return Vector3.zero;
            }

            var sum = Vector3.zero;
            foreach (var agent in agents)
            {
                sum += agent.transform.position;
            }

            return sum / agents.Count;
        }

        private static void DrawEnumButtons<T>(string label, T current, System.Action<T> onChanged) where T : System.Enum
        {
            GUILayout.Label(label);
            GUILayout.BeginHorizontal();
            foreach (T value in System.Enum.GetValues(typeof(T)))
            {
                GUI.enabled = !EqualityComparer<T>.Default.Equals(value, current);
                if (GUILayout.Button(GetDisplayName(value), GUILayout.Height(24)))
                {
                    onChanged(value);
                }
            }
            GUI.enabled = true;
            GUILayout.EndHorizontal();
        }

        private static string GetDisplayName<T>(T value) where T : System.Enum
        {
            var boxed = (object)value;
            switch (boxed)
            {
                case AvoidanceFrameworkKind.NoAvoidance:
                    return "无避障";
                case AvoidanceFrameworkKind.Rvo2:
                    return "RVO2";
                case AvoidanceFrameworkKind.SamplingLocal:
                    return "自研版";
                case AvoidanceScenarioKind.FreeSwim:
                    return "自由巡游";
                case AvoidanceScenarioKind.CrossFlow:
                    return "对向穿流";
                case AvoidanceScenarioKind.NarrowPassage:
                    return "狭窄通道";
                case AvoidanceScenarioKind.DenseCircleStress:
                    return "圆阵高压";
                case AvoidanceDimensionMode.XZ2D:
                    return "2D/XZ";
                case AvoidanceDimensionMode.XZ25D:
                    return "2.5D";
                case AvoidanceDimensionMode.XYZ3D:
                    return "3D/XYZ";
                case BenchmarkViewMode.Overview:
                    return "总览";
                case BenchmarkViewMode.TopDown:
                    return "俯视";
                case BenchmarkViewMode.Side:
                    return "侧视";
                case BenchmarkViewMode.FollowSchool:
                    return "跟鱼群";
                default:
                    return value.ToString();
            }
        }
    }
}
